<?php
if (!defined('ABSPATH')) exit;

class FORMFORGE_Admin {
    private static $instance = null;
    public static function instance() { if (!self::$instance) self::$instance = new self(); return self::$instance; }

    private function __construct() {
        add_filter('plugin_action_links_' . FORMFORGE_BASENAME, function ($links) {
            array_unshift($links, '<a href="' . admin_url('admin.php?page=formforge') . '">' . __('Forms', 'formforge') . '</a>');
            return $links;
        });
    }

    private function is_valid_mailchimp_api_key($api_key) {
        return preg_match('/^[A-Za-z0-9]{8,}-us\d+$/', (string) $api_key) === 1;
    }

    private function field_type_feature_gate($type) {
        if (class_exists('FORMFORGE_Field_Types')) {
            return FORMFORGE_Field_Types::pro_feature_for_type($type);
        }
        return '';
    }

    private function can_pro_feature($feature) {
        return class_exists('FORMFORGE_License') && FORMFORGE_License::instance()->can($feature);
    }

    private function settings_card_class($feature) {
        return 'ff-settings-card' . ($this->can_pro_feature($feature) ? '' : ' ff-settings-card--pro-locked');
    }

    private function disabled_without_feature($feature) {
        disabled(!$this->can_pro_feature($feature));
    }

    // HubSpot token-shape validation moved into the dedicated AJAX
    // handler FORMFORGE_Core::ajax_hubspot_connect when the bare-submit
    // HubSpot Connect button was replaced with a real connect flow.

    /**
 * R4: the ONE label-resolution path shared by the Submissions
 * table, the View modal, and the PDF export so they can never drift.
 *
 * A submission's `data` JSON entry is `<field_id> => {label, value,
 * type}` (normalised this). But the stored `label` can be empty
 * or missing — older rows, the legacy-abandoned flat shape (which
 * filter_submission_display_data back-fills with the bare id), or
 * field types that simply didn't persist one (repeater / calculation /
 * payment were the reported offenders). Sergei's retest saw the View
 * modal print raw keys like `f4`, `f5` because its JS did
 * `field.label || key` with nothing better to fall back to.
 *
 * Resolution order:
 * 1. the label stored on the submission row, if non-empty AND not
 * just a copy of the field id;
 * 2. the label from the LIVE form definition, matched by field id;
 * 3. the raw key — last resort only.
 *
 * @param string $key Field id / submission data key.
 * @param array $field The submission's stored field entry.
 * @param array $field_config_map id => live field config (from $form->fields).
 * @return string Human label.
 */
    private function resolve_field_label($key, $field, $field_config_map = []) {
        $key = (string) $key;
        $stored = is_array($field) ? trim((string) ($field['label'] ?? '')) : '';
        // A stored label that just echoes the id is not a real label — the
        // legacy-abandoned shim writes `'label' => $field_id`, and some
        // writers default to the key. Treat it as "missing" so the live
        // form definition gets a chance.
        if ($stored !== '' && $stored !== $key) {
            return $stored;
        }
        if (isset($field_config_map[$key]) && is_array($field_config_map[$key])) {
            $live = trim((string) ($field_config_map[$key]['label'] ?? ''));
            if ($live !== '') {
                return $live;
            }
        }
        return $stored !== '' ? $stored : $key;
    }

    /**
 * R4: build the id => live field config map used by
 * resolve_field_label. Centralised so the table and the View modal
 * data prep build it the same way.
 */
    private function build_field_config_map($form_obj) {
        $map = [];
        if ($form_obj && !empty($form_obj->fields) && is_array($form_obj->fields)) {
            foreach ($form_obj->fields as $fc) {
                if (!empty($fc['id'])) {
                    $map[(string) $fc['id']] = $fc;
                }
            }
        }
        return $map;
    }

    private function prepare_submission_view_data($data, $submission_id, $field_config_map = []) {
        if (!is_array($data)) return [];
        $data = $this->filter_submission_display_data($data);
        foreach ($data as $key => &$field) {
            // R4: resolve the real label here so the View modal shows
            // "Price" instead of the raw "f4" key. The modal JS still does
            // `field.label || key`, but now `field.label` is always the
            // properly resolved label — the table and PDF go through the
            // same resolve_field_label so the three can't disagree.
            if (is_array($field)) {
                $field['label'] = $this->resolve_field_label($key, $field, $field_config_map);
                $field_type = $field['type'] ?? '';
                if (in_array($field_type, ['calendar_picker', 'date', 'time'], true)) {
                    $field['value'] = FORMFORGE_Value_Formatter::field_text($field, '');
                } elseif ($field_type === 'map_address') {
                    $field['value'] = FORMFORGE_Value_Formatter::text($field['value'] ?? '', '');
                }
            }
            if (($field['type'] ?? '') !== 'file_upload' || empty($field['file']) || !is_array($field['file'])) {
                continue;
            }
            $token = (string) ($field['file']['token'] ?? '');
            if ($token === '') continue;
            $field['download_url'] = admin_url('admin-ajax.php?action=formforge_download_file'
                . '&submission_id=' . (int) $submission_id
                . '&field=' . rawurlencode((string) $key)
                . '&token=' . rawurlencode($token)
                . '&_wpnonce=' . wp_create_nonce('formforge_file_' . (int) $submission_id . '_' . (string) $key));
        }
        unset($field);
        return $data;
    }

    /**
 * Build a Payment-details payload for the View modal / PDF / CSV.
 *
 * Pulls together the fields the QA team wants visible — payment_status,
 * payment_intent_id, amount, currency, updated_at — from whichever
 * source is available. The submit-time handler stores them in the
 * payment field row AND a $data['_payment'] bag; webhooks back-fill
 * $data['_payment'] with the authoritative Stripe values after capture.
 *
 * Returns an empty array if the submission has no payment information,
 * so callers can omit the section entirely (don't render empty rows).
 */
    private function prepare_payment_details($raw_data, $submission_row) {
        if (!is_array($raw_data)) return [];

        $payment_bag = isset($raw_data['_payment']) && is_array($raw_data['_payment'])
            ? $raw_data['_payment']
            : [];

        $field_row = null;
        foreach ($raw_data as $key => $field) {
            if (!is_array($field)) continue;
            if (($field['type'] ?? '') === 'payment') {
                $field_row = $field;
                break;
            }
        }

        // Column on the submission row — authoritative for status.
        $row_status = is_object($submission_row) ? (string) ($submission_row->payment_status ?? '') : '';

        $status = (string) ($payment_bag['status'] ?? '');
        if ($status === '' && $row_status !== '') $status = $row_status;

        $payment_id = (string) ($payment_bag['payment_id'] ?? '');
        if ($payment_id === '' && $field_row && is_string($field_row['value'] ?? null)) {
            $payment_id = (string) $field_row['value'];
        }

        $amount = isset($payment_bag['amount']) ? (int) $payment_bag['amount'] : 0;
        if ($amount <= 0 && $field_row && isset($field_row['amount'])) {
            $amount = (int) $field_row['amount'];
        }

        $currency = strtolower((string) ($payment_bag['currency'] ?? ''));
        if ($currency === '' && $field_row && !empty($field_row['currency'])) {
            $currency = strtolower((string) $field_row['currency']);
        }

        $updated_at = (string) ($payment_bag['updated_at'] ?? '');

        // No payment info at all — let the caller skip the section.
        if ($status === '' && $payment_id === '' && $amount <= 0) return [];

        // Stripe Dashboard URL — test-mode submissions get the "test/"
        // subpath so admins land on the right environment. The mode lives
        // on the global Stripe settings; safe to look it up here.
        $stripe_settings = get_option('formforge_stripe_settings', []);
        $stripe_mode = ($stripe_settings['mode'] ?? 'test') === 'live' ? 'live' : 'test';
        $dashboard_url = '';
        if ($payment_id !== '' && strpos($payment_id, 'pi_') === 0) {
            $dashboard_url = ($stripe_mode === 'live')
                ? 'https://dashboard.stripe.com/payments/' . rawurlencode($payment_id)
                : 'https://dashboard.stripe.com/test/payments/' . rawurlencode($payment_id);
        }

        return [
            'status'         => $status,
            'payment_id'     => $payment_id,
            'amount'         => $amount,
            'currency'       => $currency,
            'updated_at'     => $updated_at,
            'dashboard_url'  => $dashboard_url,
            'amount_display' => $amount > 0 && $currency !== ''
                ? strtoupper($currency) . ' ' . number_format($amount / 100, 2)
                : '',
        ];
    }

    private function payment_status_chip_style($status) {
        switch (strtolower((string) $status)) {
            case 'succeeded':
                return ['bg' => '#ECFDF5', 'fg' => '#047857'];
            case 'pending':
                return ['bg' => '#FEF3C7', 'fg' => '#92400E'];
            case 'failed':
                return ['bg' => '#FEE2E2', 'fg' => '#B91C1C'];
            case 'refunded':
                return ['bg' => '#EDE9FE', 'fg' => '#6D28D9'];
            default:
                return ['bg' => '#F1F5F9', 'fg' => '#475569'];
        }
    }

    private function is_internal_submission_key($key) {
        $key = (string) $key;
        return $key !== '' && $key[0] === '_';
    }

    private function filter_submission_display_data($data) {
        if (!is_array($data)) return [];
        $filtered = [];
        foreach ($data as $key => $field) {
            if ($this->is_internal_submission_key($key)) continue;
            // r4: legacy-abandoned compatibility shim. Round 1-3
            // abandoned rows (and any row written before the r4 writer fix)
            // stored captured partial values in a flat
            // `formforge_field_<id> => scalar` shape instead of the canonical
            // `<id> => {label, value, type}` object a normal submission uses.
            // The renderer (table, columns, View modal) only understands the
            // object shape, so those rows rendered empty. Normalise the flat
            // shape here, once, so every downstream consumer stays
            // single-pathed — no per-row `status === 'abandoned'` branching.
            // New rows already arrive in the object shape and skip this.
            if (!is_array($field)) {
                if (!is_scalar($field)) continue;
                $field_id = strpos((string) $key, 'formforge_field_') === 0
                    ? substr((string) $key, strlen('formforge_field_'))
                    : (string) $key;
                if ($field_id === '') continue;
                // Key on the bare field id so the existing
                // $field_config_map[$id] label lookup in the Submissions
                // table resolves the human label automatically.
                $filtered[$field_id] = [
                    'label' => $field_id,
                    'value' => (string) $field,
                    'type'  => 'text',
                ];
                continue;
            }
            $filtered[$key] = $field;
        }
        return $filtered;
    }

    private function get_submission_columns($subs, $form_obj) {
        $columns = [];
        if ($form_obj && !empty($form_obj->fields)) {
            foreach ($form_obj->fields as $field) {
                $type = $field['type'] ?? '';
                $id = (string) ($field['id'] ?? '');
                if ($id === '' || in_array($type, ['heading', 'html_block', 'divider', 'page_break'], true)) continue;
                $columns[] = $id;
            }
        }
        if (!empty($columns)) return $columns;

        foreach ($subs as $sub) {
            $data = $this->filter_submission_display_data(json_decode($sub->data, true) ?: []);
            foreach (array_keys($data) as $key) {
                if (!in_array($key, $columns, true)) {
                    $columns[] = $key;
                }
            }
        }
        return $columns;
    }

    private function normalize_connect_error($service, $result) {
        if (!is_wp_error($result)) {
            return '';
        }

        $message = trim((string) $result->get_error_message());
        $normalized = strtolower($message);
        if ($normalized === 'invalid request' || $normalized === 'api error') {
            if ($service === 'mailchimp') {
                return __('Could not connect to Mailchimp. Check the API key and try again.', 'formforge');
            }
            if ($service === 'hubspot') {
                return __('Could not connect to HubSpot. Check the private app token and try again.', 'formforge');
            }
        }

        return $message !== ''
            ? $message
            : __('Connection failed. Please try again.', 'formforge');
    }

    private function sanitize_service_webhook_url($raw_url, $service) {
        $url = trim((string) $raw_url);
        if ($url === '') {
            return '';
        }

        $sanitized = esc_url_raw($url);
        if ($sanitized === '') {
            return new WP_Error('invalid_url', __('Enter a valid webhook URL.', 'formforge'));
        }

        $host = strtolower((string) wp_parse_url($sanitized, PHP_URL_HOST));
        $path = (string) wp_parse_url($sanitized, PHP_URL_PATH);
        $scheme = strtolower((string) wp_parse_url($sanitized, PHP_URL_SCHEME));

        if ($service === 'slack') {
            if ($scheme !== 'https' || $host !== 'hooks.slack.com' || strpos($path, '/services/') !== 0) {
                return new WP_Error('invalid_slack_webhook', __('Slack webhook URL must start with https://hooks.slack.com/services/.', 'formforge'));
            }
        }

        if ($service === 'discord') {
            if ($scheme !== 'https' || $host !== 'discord.com' || strpos($path, '/api/webhooks/') !== 0) {
                return new WP_Error('invalid_discord_webhook', __('Discord webhook URL must start with https://discord.com/api/webhooks/.', 'formforge'));
            }
        }

        return $sanitized;
    }

    private function sanitize_whatsapp_recipient_phone($raw_phone) {
        $raw_phone = trim((string) $raw_phone);
        if ($raw_phone === '') {
            return '';
        }

        $has_leading_plus = substr($raw_phone, 0, 1) === '+';
        $digits = preg_replace('/\D+/', '', $raw_phone);
        if (!is_string($digits) || $digits === '') {
            return '';
        }

        return ($has_leading_plus ? '+' : '') . $digits;
    }

    public function render_forms_list() {
        // Admin list shows the FULL saved set (incl. forms locked by Free
        // soft-limit) so the admin can see / edit / delete everything they
        // own. Runtime REST / shortcode discovery uses get_all which
        // slices to the Free limit.
        $forms      = FORMFORGE_Form_Builder::instance()->get_all_unfiltered();
        $form_limit = (int) FORMFORGE_License::instance()->get_limit('forms');
        $is_free    = FORMFORGE_License::instance()->is_free();
        $over_limit = $is_free && $form_limit > 0 && count($forms) > $form_limit;
        ?>
        <div class="wrap" style="max-width:1100px;">
            <?php FORMFORGE_Admin_Tabs::render('formforge'); ?>
            <?php FORMFORGE_License::instance()->render_upgrade_banner(); ?>
            <?php if ($over_limit):
                // NOTE: own class — `.notice` would be relocated by WP-core JS
                // into the page's custom flex-based header and become invisible.
                // See LangForge for the same fix.
                $upgrade_url = esc_url(FORMFORGE_License::instance()->upgrade_url());
                $msg_count = sprintf(
                    /* translators: 1: current number of forms, 2: number of forms allowed on the Free plan */
                    esc_html__('You have %1$d forms, but the Free plan supports %2$d.', 'formforge'),
                    count($forms),
                    $form_limit
                );
                $msg_body = sprintf(
                    /* translators: 1: form position number, 2: opening link tag, 3: closing link tag */
                    esc_html__('Forms past position %1$d are hidden from the [formforge] shortcode and REST API — data is preserved and reactivates on %2$supgrade to Pro%3$s.', 'formforge'),
                    $form_limit,
                    '<a href="' . $upgrade_url . '" target="_blank" rel="noopener" style="font-weight:600;color:#3B82F6;">',
                    '</a>'
                );
                echo '<div class="ff-overlimit-banner" style="display:block;border:1px solid #bfdbfe;border-left:4px solid #3B82F6;background:#eff6ff;padding:12px 16px;margin:0 0 20px;border-radius:4px;">';
                echo '<p style="margin:0 0 6px;font-weight:600;font-size:14px;color:#1e293b;">⚠ ' . $msg_count . '</p>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $msg_count built from esc_html__ + integers
                echo '<p style="margin:0;color:#475569;font-size:13px;">' . $msg_body . '</p>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $msg_body built from esc_html__ + trusted anchor (esc_url'd href)
                echo '</div>';
            endif; ?>
            <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:20px;padding-bottom:14px;border-bottom:2px solid #3B82F6;">
                <div style="margin:0;font-size:22px;font-weight:700;display:flex;align-items:center;gap:8px;">
                    <span class="dashicons dashicons-forms" style="color:#3B82F6;font-size:24px;width:24px;height:24px;"></span>
                    <?php esc_html_e('Forms', 'formforge'); ?>
                    <span style="font-size:14px;font-weight:400;color:#94a3b8;margin-left:4px;">(<?php echo esc_html(count($forms)); ?>)</span>
                    <?php Forge_Docs_Link::render('formforge', 'creating-a-form-three-ways', __('Creating a form', 'formforge'), __('Three ways to start: scratch, template, or AI generation.', 'formforge')); ?>
                </div>
                <a href="<?php echo esc_url(admin_url('admin.php?page=formforge-new')); ?>" style="display:inline-flex;align-items:center;gap:6px;background:#3B82F6;color:#fff;text-decoration:none;padding:9px 18px;border-radius:8px;font-size:14px;font-weight:600;transition:all 0.15s;">
                    <span class="dashicons dashicons-plus-alt2" style="font-size:16px;width:16px;height:16px;"></span>
                    <?php esc_html_e('New Form', 'formforge'); ?>
                </a>
            </div>

            <?php if (!empty($forms)): ?>
            <div style="margin-bottom:10px;display:flex;gap:8px;align-items:center;">
                <button type="button" id="ff-bulk-delete" style="display:none;background:#fee2e2;color:#dc2626;border:1px solid #fca5a5;border-radius:6px;padding:6px 14px;font-size:14px;font-weight:600;cursor:pointer;">
                    <span class="dashicons dashicons-trash" style="font-size:14px;width:14px;height:14px;vertical-align:text-bottom;"></span>
                    <?php esc_html_e('Delete Selected', 'formforge'); ?>
                </button>
            </div>
            <?php endif; ?>

            <div class="ff-forms-card">
                <table class="ff-forms-table">
                    <thead>
                        <tr>
                            <th style="width:36px;"><input type="checkbox" id="ff-select-all"></th>
                            <th><?php esc_html_e('Form', 'formforge'); ?></th>
                            <th style="width:180px;"><?php esc_html_e('Shortcode', 'formforge'); ?></th>
                            <th style="width:70px;text-align:center;"><?php esc_html_e('Fields', 'formforge'); ?></th>
                            <th style="width:90px;text-align:center;"><?php esc_html_e('Responses', 'formforge'); ?></th>
                            <th style="width:80px;text-align:center;"><?php esc_html_e('Rate', 'formforge'); ?></th>
                            <th style="width:120px;"><?php esc_html_e('Actions', 'formforge'); ?></th>
                        </tr>
                    </thead>
                    <tbody>
                    <?php if (empty($forms)): ?>
                        <tr><td colspan="7" style="text-align:center;padding:40px;color:#94a3b8;">
                            <span class="dashicons dashicons-forms" style="font-size:32px;width:32px;height:32px;display:block;margin:0 auto 8px;color:#e2e8f0;"></span>
                            <?php esc_html_e('No forms yet.', 'formforge'); ?>
                            <br><a href="<?php echo esc_url(admin_url('admin.php?page=formforge-new')); ?>" style="color:#3B82F6;font-weight:600;"><?php esc_html_e('Create your first form', 'formforge'); ?> &rarr;</a>
                        </td></tr>
                    <?php endif; ?>
                    <?php foreach ($forms as $form):
                        // Analytics is Pro — on Free-only sites the class is
                        // absent. Fall back to a zeroed stats row so the
                        // forms-list table still renders (Pro=accurate stats).
                        $stats = class_exists('FORMFORGE_Analytics')
                            ? FORMFORGE_Analytics::instance()->get_stats($form->id)
                            : ['views' => 0, 'submissions' => 0, 'conversion_rate' => 0];
                        $field_count = is_array($form->fields) ? count($form->fields) : 0;
                    ?>
                        <tr>
                            <td><input type="checkbox" class="ff-form-check" value="<?php echo esc_attr($form->id); ?>"></td>
                            <td>
                                <a href="<?php echo esc_url(admin_url('admin.php?page=formforge-new&form_id=' . $form->id)); ?>" class="ff-form-title"><?php echo esc_html($form->title); ?></a>
                                <div class="ff-form-meta">ID: <?php echo esc_html($form->id); ?> &middot; <?php echo esc_html(date_i18n('M j, Y', strtotime($form->created_at ?? 'now'))); ?></div>
                            </td>
                            <td>
                                <span class="ff-shortcode-inline" title="<?php esc_attr_e('Copy this shortcode and paste it into a page, post, widget, or page builder module.', 'formforge'); ?>" aria-label="<?php esc_attr_e('Copy this shortcode and paste it into a page, post, widget, or page builder module.', 'formforge'); ?>" onclick="navigator.clipboard.writeText('[formforge id=&quot;<?php echo esc_attr($form->id); ?>&quot;]');this.textContent='Copied!';var s=this;setTimeout(function(){s.textContent='[formforge id=&quot;<?php echo esc_attr($form->id); ?>&quot;]'},1500);">
                                    [formforge id="<?php echo esc_html($form->id); ?>"]
                                </span>
                            </td>
                            <td style="text-align:center;"><span class="ff-stat"><?php echo esc_html($field_count); ?></span></td>
                            <td style="text-align:center;"><span class="ff-stat"><?php echo esc_html($stats['submissions']); ?></span></td>
                            <td style="text-align:center;"><span class="ff-stat"><?php echo esc_html($stats['conversion_rate']); ?>%</span></td>
                            <td>
                                <div class="ff-row-actions">
                                    <a href="<?php echo esc_url(admin_url('admin.php?page=formforge-new&form_id=' . $form->id)); ?>" class="ff-row-btn" title="Edit"><span class="dashicons dashicons-edit"></span></a>
                                    <a href="<?php echo esc_url(admin_url('admin.php?page=formforge-submissions&form_id=' . $form->id)); ?>" class="ff-row-btn" title="Submissions"><span class="dashicons dashicons-email"></span></a>
                                    <a href="#" class="ff-row-btn ff-row-btn--danger formforge-delete-form" data-id="<?php echo esc_attr($form->id); ?>" title="Delete"><span class="dashicons dashicons-trash"></span></a>
                                </div>
                            </td>
                        </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        </div>

        <style>
        .ff-forms-card{background:#fff;border:1px solid #e2e8f0;border-radius:12px;overflow:hidden;box-shadow:0 1px 3px rgba(0,0,0,0.06);}
        .ff-forms-table{width:100%;border-collapse:collapse;}
        .ff-forms-table thead th{padding:12px 14px;font-size:14px;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;color:#64748b;border-bottom:2px solid #e2e8f0;background:#f8fafc;text-align:left;}
        .ff-forms-table tbody tr{border-bottom:1px solid #f1f5f9;transition:background 0.1s;}
        .ff-forms-table tbody tr:last-child{border-bottom:none;}
        .ff-forms-table tbody tr:hover{background:#fafafe;}
        .ff-forms-table td{padding:12px 14px;vertical-align:middle;}
        .ff-forms-table input[type=checkbox]{accent-color:#3B82F6;}
        .ff-form-title{color:#1e293b;font-weight:600;font-size:14px;text-decoration:none;}
        .ff-form-title:hover{color:#3B82F6;}
        .ff-form-meta{font-size:14px;color:#94a3b8;margin-top:2px;}
        .ff-shortcode-inline{background:#f1f5f9;padding:4px 10px;border-radius:4px;font-family:monospace;font-size:14px;color:#3B82F6;cursor:pointer;transition:all 0.15s;display:inline-block;}
        .ff-shortcode-inline:hover{background:#EFF6FF;color:#2D74E8;}
        .ff-stat{font-size:14px;font-weight:600;color:#1e293b;}
        .ff-row-actions{display:flex;gap:4px;}
        .ff-row-btn{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:6px;color:#94a3b8;transition:all 0.15s;text-decoration:none;}
        .ff-row-btn:hover{background:#EFF6FF;color:#3B82F6;}
        .ff-row-btn .dashicons{font-size:16px;width:16px;height:16px;}
        .ff-row-btn--danger:hover{background:#fee2e2;color:#dc2626;}
        </style>

        <script>
        jQuery(function($){
            // Select all checkbox
            $('#ff-select-all').on('change', function(){
                var checked = $(this).prop('checked');
                $('.ff-form-check').prop('checked', checked);
                $('#ff-bulk-delete').toggle($('.ff-form-check:checked').length > 0);
            });
            $(document).on('change', '.ff-form-check', function(){
                var anyChecked = $('.ff-form-check:checked').length > 0;
                $('#ff-bulk-delete').toggle(anyChecked);
                $('#ff-select-all').prop('checked', $('.ff-form-check:checked').length === $('.ff-form-check').length);
            });
            // Bulk delete
            $('#ff-bulk-delete').on('click', function(){
                var ids = [];
                $('.ff-form-check:checked').each(function(){ ids.push($(this).val()); });
                if (!ids.length) return;
                if (!confirm('Delete ' + ids.length + ' form(s) and all their submissions?')) return;
                var done = 0;
                ids.forEach(function(id){
                    $.post(formforgeAdmin.ajaxUrl, {action:'formforge_delete_form', nonce:formforgeAdmin.nonce, form_id:id}, function(){
                        if(++done >= ids.length) location.reload();
                    });
                });
            });
 // Sync browser <title> to include form count
            <?php /* translators: %d: number of forms */ ?>
            document.title = document.title.replace(/^[^\u2039]+/, '<?php echo esc_js(sprintf(__('Forms (%d)', 'formforge'), count($forms))); ?> \u2039 ');
        });
        </script>
        <?php
    }

    /**
 * Render the AI Submission Analysis entry point (button + panel + JS).
 *
 * "AI Submission Analysis" analyzes collected submissions / conversion
 * data, so its primary home is the Submissions page (and Analytics). The
 * form editor keeps it as a secondary in-context shortcut. This single
 * helper renders all three so the button markup, the credit badge and the
 * job-status polling behaviour stay identical everywhere it appears.
 *
 * @param int $form_id Form whose submissions to analyze.
 * @param string $context 'editor' | 'submissions' | 'analytics' — controls
 * layout only. Recommendation "apply" actions are
 * editor-only (they mutate the builder's field JSON).
 */
    public function render_ai_analysis_widget($form_id, $context = 'submissions') {
        $form_id = intval($form_id);
        if (!$form_id) return;
        if (!class_exists('FORMFORGE_Submissions_AI')) return;
        // Gate identically to the AJAX handler — no point showing a button
        // that the endpoint will reject.
        if (!FORMFORGE_License::instance()->can('submissions_ai')) return;

        $is_editor   = ($context === 'editor');
        // Editor: a compact standalone button right under the page heading.
        // Submissions / Analytics: the button gets its own toolbar row (with a
        // one-line hint) above the table. Same gradient + .ff-credits-badge
        // either way so it reads as one consistent control.
	        $btn_style   = $is_editor
	            ? 'display:inline-flex;align-items:center;gap:6px;background:linear-gradient(135deg,#2563EB,#3B82F6);color:#fff;border:none;padding:7px 16px;border-radius:6px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.15s;box-shadow:0 1px 4px rgba(59,130,246,0.18);'
	            : 'display:inline-flex;align-items:center;gap:6px;background:linear-gradient(135deg,#2563EB,#3B82F6);color:#fff;border:none;padding:8px 18px;border-radius:8px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.15s;box-shadow:0 1px 4px rgba(59,130,246,0.18);';
	        $analysis_badge = class_exists('Forge_Credits_Widget') ? Forge_Credits_Widget::render_badge_html('analyze_submissions', 'ff') : '';
        ?>
        <?php if ($is_editor): ?>
        <div class="ff-ai-analysis-editor-bar" style="margin:8px 0 4px;">
        <?php endif; ?>
        <?php if (!$is_editor): ?>
        <div id="ff-ai-analysis" class="ff-ai-analysis-toolbar" style="display:flex;align-items:center;gap:12px;margin:16px 0;flex-wrap:wrap;">
        <?php endif; ?>
            <?php /* data-forge-decorated="1" is intentional — the shared
 credits widget's scan skips already-decorated nodes, so the
 SSR badge below is the ONLY credit badge. Without this the JS
 decorate appended a second .forge-credit-badge pill (the
 "duplicate 5 badge" Sergei reported). The heavy-action confirm
 modal only keys off data-forge-ai-action, so it still fires. */ ?>
	            <button type="button" class="ff-ai-cache-btn" data-form-id="<?php echo esc_attr($form_id); ?>" data-context="<?php echo esc_attr($context); ?>" style="<?php echo esc_attr(str_replace('background:linear-gradient(135deg,#2563EB,#3B82F6);color:#fff;border:none;', 'background:#fff;color:#2563EB;border:1px solid #bfdbfe;', $btn_style)); ?>">
	                <span class="dashicons dashicons-visibility" style="font-size:16px;width:16px;height:16px;"></span>
	                <?php esc_html_e('View cached analysis', 'formforge'); ?>
	            </button>
	            <button type="button" class="ff-ai-analyze-btn" data-forge-ai-action="analyze_submissions" data-forge-decorated="1" data-form-id="<?php echo esc_attr($form_id); ?>" data-context="<?php echo esc_attr($context); ?>" style="<?php echo esc_attr($btn_style); ?>">
	                <span class="dashicons dashicons-chart-area" style="font-size:16px;width:16px;height:16px;"></span>
	                <?php esc_html_e('Run new analysis', 'formforge'); ?> <?php echo $analysis_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- trusted pre-built markup from Forge_Credits_Widget::render_badge_html ?>
	            </button>
            <?php if (!$is_editor): ?>
            <span style="font-size:13px;color:#94a3b8;"><?php esc_html_e('Analyze this form’s submissions and conversion data for recommendations.', 'formforge'); ?></span>
            <?php endif; ?>
        </div><?php /* closes .ff-ai-analysis-toolbar / .ff-ai-analysis-editor-bar */ ?>

        <!-- AI Analysis Panel (hidden by default) -->
        <div class="ff-ai-analysis-panel<?php echo $is_editor ? ' ff-ai-analysis-panel--editor' : ''; ?>" data-form-id="<?php echo esc_attr($form_id); ?>" style="display:none;margin:16px 0;background:#fff;border:1px solid #e2e8f0;border-radius:12px;overflow:hidden;box-shadow:0 1px 3px rgba(0,0,0,0.06);">
            <div style="display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid #f1f5f9;background:#f8fafc;">
                <div style="display:flex;align-items:center;gap:8px;">
                    <span class="dashicons dashicons-chart-area" style="color:#2563EB;font-size:20px;width:20px;height:20px;"></span>
                    <strong style="font-size:15px;color:#1e293b;"><?php esc_html_e('AI Submission Analysis', 'formforge'); ?></strong>
                </div>
                <button type="button" class="ff-ai-analysis-close" style="background:none;border:none;cursor:pointer;color:#94a3b8;padding:4px;border-radius:4px;display:flex;align-items:center;" title="<?php esc_attr_e('Close', 'formforge'); ?>">
                    <span class="dashicons dashicons-no-alt" style="font-size:20px;width:20px;height:20px;"></span>
                </button>
            </div>
            <div class="ff-ai-analysis-content" style="padding:24px;">
                <!-- Processing state -->
                <div class="ff-ai-analysis-loading" style="text-align:center;padding:40px;">
                    <div class="ff-ai-spinner"></div>
                    <p class="ff-ai-analysis-loading-msg" style="margin:16px 0 0;color:#64748b;font-size:14px;"><?php esc_html_e('Analysis in progress…', 'formforge'); ?></p>
                </div>
                <!-- Results populated by JS -->
                <div class="ff-ai-analysis-results" style="display:none;"></div>
                <!-- Error / timeout state -->
                <div class="ff-ai-analysis-error" style="display:none;text-align:center;padding:30px;">
                    <span class="dashicons dashicons-warning" style="font-size:32px;width:32px;height:32px;color:#f59e0b;display:block;margin:0 auto 8px;"></span>
                    <p class="ff-ai-analysis-error-msg" style="color:#64748b;font-size:14px;"></p>
                    <button type="button" class="ff-ai-analysis-retry button" style="margin-top:12px;"><?php esc_html_e('Try again', 'formforge'); ?></button>
                </div>
            </div>
        </div>

        <style>
        .ff-ai-spinner{width:36px;height:36px;border:3px solid #e2e8f0;border-top-color:#2563EB;border-radius:50%;animation:ff-ai-spin 0.8s linear infinite;margin:0 auto;}
        @keyframes ff-ai-spin{to{transform:rotate(360deg);}}
        .ff-ai-score-wrap{display:flex;align-items:flex-start;gap:24px;margin-bottom:24px;}
        .ff-ai-score-column{display:flex;flex-direction:column;align-items:center;flex:0 0 auto;gap:4px;}
        .ff-ai-score-circle{position:relative;width:100px;height:100px;flex-shrink:0;}
        .ff-ai-score-circle svg{width:100px;height:100px;transform:rotate(-90deg);}
        .ff-ai-score-circle .ff-score-bg{fill:none;stroke:#e2e8f0;stroke-width:8;}
        .ff-ai-score-circle .ff-score-fg{fill:none;stroke-width:8;stroke-linecap:round;transition:stroke-dashoffset 1s ease;}
        .ff-ai-score-value{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:28px;font-weight:800;color:#1e293b;}
        .ff-ai-score-label{font-size:14px;color:#64748b;text-align:center;line-height:1.2;}
        .ff-ai-score-summary{flex:1;}
        .ff-ai-score-summary h3{margin:0 0 6px;font-size:16px;font-weight:700;color:#1e293b;}
        .ff-ai-score-summary p{margin:0;font-size:14px;color:#64748b;line-height:1.5;}
        .ff-ai-insights{display:flex;flex-direction:column;gap:10px;}
        .ff-ai-insight-card{display:flex;align-items:flex-start;gap:12px;padding:14px 16px;border:1px solid #e2e8f0;border-radius:10px;background:#fff;transition:all 0.15s;}
        .ff-ai-insight-card:hover{border-color:#93c5fd;background:#eff6ff;}
        .ff-ai-insight-badge{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:8px;font-size:14px;font-weight:800;flex-shrink:0;color:#fff;}
        .ff-ai-badge-1{background:#dc2626;}
        .ff-ai-badge-2{background:#f59e0b;}
        .ff-ai-badge-3{background:#3b82f6;}
        .ff-ai-insight-body{flex:1;min-width:0;}
        .ff-ai-insight-title{font-size:14px;font-weight:700;color:#1e293b;margin-bottom:4px;}
        .ff-ai-insight-desc{font-size:14px;color:#64748b;line-height:1.5;margin-bottom:8px;}
        .ff-ai-meta-pill,.ff-ai-source-pill{display:inline-flex;align-items:center;border:1px solid #dbeafe;background:#eff6ff;color:#1d4ed8;border-radius:999px;padding:3px 9px;font-size:12px;font-weight:600;}
        .ff-ai-source-pill{border-color:#e2e8f0;background:#f8fafc;color:#475569;}
        .ff-ai-insight-action{display:inline-flex;align-items:center;gap:4px;background:#EFF6FF;color:#2563EB;border:1px solid #BFDBFE;border-radius:6px;padding:5px 12px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.15s;}
        .ff-ai-insight-action:hover{background:#DBEAFE;border-color:#93c5fd;}
	        .ff-ai-action-confirmation{margin-top:10px;border:1px solid #bfdbfe;background:#eff6ff;color:#1d4ed8;border-radius:8px;padding:8px 10px;font-size:13px;font-weight:700;}
	        .ff-ai-analysis-panel--editor{margin:8px 0 12px!important;border-radius:10px!important;}
	        .ff-ai-analysis-panel--editor > div:first-child{padding:10px 14px!important;}
	        .ff-ai-analysis-panel--editor .ff-ai-analysis-content{padding:12px!important;}
	        .ff-ai-analysis-panel--editor .ff-ai-score-wrap{gap:16px;margin-bottom:12px;}
	        .ff-ai-analysis-panel--editor .ff-ai-score-circle,.ff-ai-analysis-panel--editor .ff-ai-score-circle svg{width:72px;height:72px;}
	        .ff-ai-analysis-panel--editor .ff-ai-score-circle .ff-score-bg,.ff-ai-analysis-panel--editor .ff-ai-score-circle .ff-score-fg{stroke-width:7;}
	        .ff-ai-analysis-panel--editor .ff-ai-score-value{font-size:22px;}
	        .ff-ai-analysis-panel--editor .ff-ai-score-summary p{display:none;}
	        .ff-ai-analysis-panel--editor .ff-ai-meta{margin:0 0 12px!important;}
	        .ff-ai-analysis-panel--editor .ff-ai-insights{max-height:240px;overflow:auto;padding-right:4px;}
        </style>

        <script>
        (function($){
 // scope every selector to THIS widget instance so multiple
            // copies on one page (unlikely, but Analytics + a future surface)
            // never cross-wire. The button, panel and all states share the
            // same data-form-id.
            var formId = <?php echo (int) $form_id; ?>;
	            var $btn   = $('.ff-ai-analyze-btn[data-form-id="' + formId + '"]');
	            var $cacheBtn = $('.ff-ai-cache-btn[data-form-id="' + formId + '"]');
	            var $panel = $('.ff-ai-analysis-panel[data-form-id="' + formId + '"]');
	            if ((!$btn.length && !$cacheBtn.length) || !$panel.length) return;
	            // Guard against double-binding if the helper is rendered twice.
	            if ($panel.data('ffAiBound')) return;
	            $panel.data('ffAiBound', true);

            var $loading  = $panel.find('.ff-ai-analysis-loading');
            var $loadMsg  = $panel.find('.ff-ai-analysis-loading-msg');
            var $results  = $panel.find('.ff-ai-analysis-results');
            var $error    = $panel.find('.ff-ai-analysis-error');
            var $errorMsg = $panel.find('.ff-ai-analysis-error-msg');

            var panelOpen = false;
            var pollTimer = null;
            var pollDeadline = 0;
            var POLL_INTERVAL = 3000;   // ~3s between status polls
            var POLL_TIMEOUT  = 120000; // give up after ~2 min

            var i18n = {
                queued:    <?php echo wp_json_encode(__('Analysis queued…', 'formforge')); ?>,
                processing:<?php echo wp_json_encode(__('Analysis in progress…', 'formforge')); ?>,
                genericErr:<?php echo wp_json_encode(__('Analysis error. Please try again.', 'formforge')); ?>,
                netErr:    <?php echo wp_json_encode(__('Network error. Please try again later.', 'formforge')); ?>,
                timeout:   <?php echo wp_json_encode(__('Analysis is taking longer than expected. Please try again in a moment.', 'formforge')); ?>,
                cached:    <?php echo wp_json_encode(__('Using cached analysis', 'formforge')); ?>,
                lastAnalyzed: <?php echo wp_json_encode(__('Last analyzed', 'formforge')); ?>,
                sample:    <?php echo wp_json_encode(__('Sample', 'formforge')); ?>,
                confidence:<?php echo wp_json_encode(__('Confidence', 'formforge')); ?>,
                completed: <?php echo wp_json_encode(__('completed', 'formforge')); ?>,
                abandoned: <?php echo wp_json_encode(__('abandoned/partial', 'formforge')); ?>,
                failedPayments: <?php echo wp_json_encode(__('failed payments', 'formforge')); ?>,
	                sourceAnalytics: <?php echo wp_json_encode(__('Analytics-driven', 'formforge')); ?>,
	                sourceAi: <?php echo wp_json_encode(__('AI UX', 'formforge')); ?>,
	                sourceHeuristic: <?php echo wp_json_encode(__('Heuristic', 'formforge')); ?>,
	                noCached: <?php echo wp_json_encode(__('No cached analysis yet. Run a new analysis first.', 'formforge')); ?>
	            };

            function stopPolling() {
                if (pollTimer) { clearTimeout(pollTimer); pollTimer = null; }
            }

            function showError(msg) {
                stopPolling();
                $loading.hide();
                $results.hide();
                $errorMsg.text(msg || i18n.genericErr);
                $error.show();
            }

            function showProcessing(state) {
                $error.hide();
                $results.hide();
                $loadMsg.text(state === 'queued' ? i18n.queued : i18n.processing);
                $loading.show();
            }

 // after a credited AI action completes, tell the SHARED
            // credits widget to refresh. The widget's AJAX interceptor only
            // sees the *queue* request (which deducts nothing yet — the job
            // runs in the background), so we dispatch the event ourselves
            // once the job is actually done. We dispatch on BOTH window and
            // document because the shared widget listens on document; window
            // is dispatched too for forward-compat with future listeners.
            function refreshCredits(credits) {
                var detail = credits ? { credits: credits, action: 'analyze_submissions' }
                                     : { action: 'analyze_submissions' };
                try { window.dispatchEvent(new CustomEvent('forge:credits-deducted', { detail: detail })); } catch (e) {}
                try { document.dispatchEvent(new CustomEvent('forge:credits-deducted', { detail: detail })); } catch (e) {}
            }

	            function handleDone(result, options) {
	                options = options || {};
	                stopPolling();
	                $loading.hide();
	                $error.hide();
	                renderAnalysis(result || {});
	                if (options.refreshCredits) {
	                    refreshCredits(result && result.credits);
	                }
	            }

            // Poll the status endpoint until the job is done / failed / times out.
            function pollStatus(jobId) {
                stopPolling();
                $.post(formforgeAdmin.ajaxUrl, {
                    action: 'formforge_ai_analysis_status',
                    nonce: formforgeAdmin.nonce,
                    form_id: formId,
                    job_id: jobId || 0
                }, function(resp){
                    if (!resp || !resp.success || !resp.data) {
                        showError(i18n.genericErr);
                        return;
                    }
                    var state = resp.data.state;
                    if (state === 'done') {
	                        var doneResult = resp.data.result || {};
	                        if (jobId && doneResult) doneResult.cached = false;
	                        handleDone(doneResult, { refreshCredits: !!jobId });
                    } else if (state === 'failed') {
                        showError(resp.data.message || i18n.genericErr);
                    } else {
                        // queued | processing — keep polling until the deadline.
                        showProcessing(state);
                        if (Date.now() >= pollDeadline) {
                            showError(i18n.timeout);
                            return;
                        }
                        pollTimer = setTimeout(function(){ pollStatus(jobId); }, POLL_INTERVAL);
                    }
                }).fail(function(){
                    // One transient network error shouldn't kill the whole poll
                    // loop — retry once on the normal interval if time remains.
                    if (Date.now() < pollDeadline) {
                        pollTimer = setTimeout(function(){ pollStatus(jobId); }, POLL_INTERVAL);
                    } else {
                        showError(i18n.netErr);
                    }
                });
            }

	            function openPanel() {
	                if (!panelOpen) {
	                    panelOpen = true;
	                    $panel.slideDown(200);
	                }
	            }

	            function loadCachedAnalysis() {
	                openPanel();
	                $error.hide();
	                $results.hide();
	                showProcessing('processing');
	                $.post(formforgeAdmin.ajaxUrl, {
	                    action: 'formforge_ai_analysis_status',
	                    nonce: formforgeAdmin.nonce,
	                    form_id: formId,
	                    job_id: 0
	                }, function(resp){
	                    if (resp && resp.success && resp.data && resp.data.state === 'done' && resp.data.result) {
	                        var cachedResult = resp.data.result || {};
	                        cachedResult.cached = true;
	                        handleDone(cachedResult, { refreshCredits: false });
	                        return;
	                    }
	                    showError((resp && resp.data && resp.data.message) || i18n.noCached);
	                }).fail(function(){
	                    showError(i18n.netErr);
	                });
	            }

	            function startAnalysis(forceRefresh) {
	                openPanel();
	                $error.hide();
	                $results.hide();
	                showProcessing('queued');
	                pollDeadline = Date.now() + POLL_TIMEOUT;

	                $.post(formforgeAdmin.ajaxUrl, {
	                    action: 'formforge_ai_analyze',
	                    nonce: formforgeAdmin.nonce,
	                    form_id: formId,
	                    force_refresh: forceRefresh ? 1 : 0
	                }, function(resp){
                    if (!resp || !resp.success) {
                        showError((resp && resp.data && resp.data.message) || i18n.genericErr);
                        return;
                    }
                    var data = resp.data || {};
                    // ajax_analyze() may return a cached result straight away
                    // (state:done), or queue a job (state:queued + job_id).
                    if (data.state === 'done' || (typeof data.score !== 'undefined' && !data.queued)) {
	                        handleDone(data, { refreshCredits: !!forceRefresh });
                    } else if (data.queued || data.job_id) {
                        pollStatus(data.job_id);
                    } else {
                        // No queue + no score — inline fallback returned nothing useful.
                        showError(i18n.genericErr);
                    }
                }).fail(function(){
                    showError(i18n.netErr);
                });
            }

	            $cacheBtn.on('click', function(){
	                loadCachedAnalysis();
	            });

	            $btn.on('click', function(){
	                startAnalysis(true);
	            });

            $panel.find('.ff-ai-analysis-close').on('click', function(){
                $panel.slideUp(200);
                panelOpen = false;
                stopPolling();
            });

	            $panel.find('.ff-ai-analysis-retry').on('click', function(){
	                startAnalysis(true);
	            });

	            <?php if ($is_editor): ?>
	            try {
	                if (new URLSearchParams(window.location.search || '').get('ff_ai_field')) {
	                    loadCachedAnalysis();
	                }
	            } catch (e) {}
	            <?php endif; ?>

	            function esc(s) { return $('<span>').text(s == null ? '' : String(s)).html(); }

	            function recommendationActionLabel(ins) {
	                var type = ins && ins.type ? String(ins.type) : '';
	                var labels = {
	                    add_placeholder: <?php echo wp_json_encode(__('Add placeholder', 'formforge')); ?>,
	                    make_optional: <?php echo wp_json_encode(__('Make optional', 'formforge')); ?>,
	                    remove_field: <?php echo wp_json_encode(__('Remove field', 'formforge')); ?>,
	                    simplify_label: <?php echo wp_json_encode(__('Simplify label', 'formforge')); ?>,
	                    reorder: <?php echo wp_json_encode(__('Move field', 'formforge')); ?>,
	                    reduce_options: <?php echo wp_json_encode(__('Reduce options', 'formforge')); ?>,
	                    split_form: <?php echo wp_json_encode(__('Split into steps', 'formforge')); ?>
	                };
	                if (labels[type]) return labels[type];
	                return <?php echo wp_json_encode(__('Review field', 'formforge')); ?>;
	            }

	            function renderAnalysis(data) {
                var score = parseInt(data.score, 10);
                if (isNaN(score)) score = 0;
                var insights = data.insights || [];
                var meta = data.metadata || {};

                var scoreColor = score >= 70 ? '#22c55e' : (score >= 40 ? '#f59e0b' : '#dc2626');
                var circumference = 2 * Math.PI * 42;
                var offset = circumference - (score / 100) * circumference;

                var summaryTitle, summaryText;
                if (score >= 70) {
                    summaryTitle = <?php echo wp_json_encode(__('Your form is in good shape', 'formforge')); ?>;
                    summaryText  = <?php echo wp_json_encode(__('Conversion is above average. The recommendations below are fine-tuning suggestions.', 'formforge')); ?>;
                } else if (score >= 40) {
                    summaryTitle = <?php echo wp_json_encode(__('Room to improve', 'formforge')); ?>;
                    summaryText  = <?php echo wp_json_encode(__('The form works, but there are noticeable issues hurting conversion. Focus on the critical items first.', 'formforge')); ?>;
                } else {
                    summaryTitle = <?php echo wp_json_encode(__('Needs attention', 'formforge')); ?>;
                    summaryText  = <?php echo wp_json_encode(__('Form conversion is below baseline. Apply the recommendations below for a significant uplift.', 'formforge')); ?>;
                }

                var html = '<div class="ff-ai-score-wrap">';
                html += '<div class="ff-ai-score-column">';
                html += '<div class="ff-ai-score-circle">';
                html += '<svg viewBox="0 0 100 100"><circle class="ff-score-bg" cx="50" cy="50" r="42"/>';
                html += '<circle class="ff-score-fg" cx="50" cy="50" r="42" stroke="' + scoreColor + '" stroke-dasharray="' + circumference + '" stroke-dashoffset="' + offset + '"/></svg>';
                html += '<div class="ff-ai-score-value" style="color:' + scoreColor + '">' + score + '</div>';
                html += '</div>';
                html += '<div class="ff-ai-score-label"><?php echo esc_js(__('out of 100', 'formforge')); ?></div>';
                html += '</div>';
                html += '<div class="ff-ai-score-summary"><h3>' + esc(summaryTitle) + '</h3><p>' + esc(summaryText) + '</p></div>';
                html += '</div>';

                var analyzedAt = data.analyzed_at ? new Date(data.analyzed_at) : null;
                var analyzedLabel = analyzedAt && !isNaN(analyzedAt.getTime()) ? analyzedAt.toLocaleString() : '';
                var sampleBits = [];
                if (typeof meta.completed_submissions !== 'undefined') sampleBits.push(meta.completed_submissions + ' ' + i18n.completed);
                if (typeof meta.abandoned_or_partial_sessions !== 'undefined') sampleBits.push(meta.abandoned_or_partial_sessions + ' ' + i18n.abandoned);
                if (typeof meta.failed_payment_events !== 'undefined') sampleBits.push(meta.failed_payment_events + ' ' + i18n.failedPayments);
                html += '<div class="ff-ai-meta" style="display:flex;flex-wrap:wrap;gap:8px;margin:-8px 0 20px;">';
                if (data.cached) {
                    html += '<span class="ff-ai-meta-pill">' + esc(i18n.cached) + '</span>';
                }
                if (analyzedLabel) {
                    html += '<span class="ff-ai-meta-pill">' + esc(i18n.lastAnalyzed) + ': ' + esc(analyzedLabel) + '</span>';
                }
                if (sampleBits.length) {
                    html += '<span class="ff-ai-meta-pill">' + esc(i18n.sample) + ': ' + esc(sampleBits.join(', ')) + '</span>';
                }
                if (meta.confidence) {
                    html += '<span class="ff-ai-meta-pill">' + esc(i18n.confidence) + ': ' + esc(meta.confidence) + '</span>';
                }
                html += '</div>';
                if (meta.source_note) {
                    html += '<p style="margin:-8px 0 18px;color:#64748b;font-size:13px;line-height:1.5;">' + esc(meta.source_note) + '</p>';
                }

                if (insights.length) {
                    html += '<h4 style="margin:0 0 12px;font-size:14px;font-weight:700;color:#1e293b;"><?php echo esc_js(__('Recommendations', 'formforge')); ?></h4>';
                    html += '<div class="ff-ai-insights">';
                    for (var i = 0; i < insights.length; i++) {
                        var ins = insights[i];
                        var pr = parseInt(ins.priority, 10); if (isNaN(pr) || pr < 1 || pr > 3) pr = 3;
                        html += '<div class="ff-ai-insight-card" data-type="' + esc(ins.type) + '" data-field="' + esc(ins.field_id || '') + '">';
                        html += '<div class="ff-ai-insight-badge ff-ai-badge-' + pr + '">P' + pr + '</div>';
                        html += '<div class="ff-ai-insight-body">';
                        html += '<div class="ff-ai-insight-title">' + esc(ins.title) + '</div>';
                        var sourceLabel = ins.source === 'analytics' ? i18n.sourceAnalytics : (ins.source === 'heuristic' ? i18n.sourceHeuristic : i18n.sourceAi);
                        html += '<div style="display:flex;gap:6px;flex-wrap:wrap;margin:0 0 6px;">';
                        html += '<span class="ff-ai-source-pill">' + esc(sourceLabel) + '</span>';
                        if (ins.confidence) html += '<span class="ff-ai-source-pill">' + esc(i18n.confidence) + ': ' + esc(ins.confidence) + '</span>';
                        html += '</div>';
                        html += '<div class="ff-ai-insight-desc">' + esc(ins.description) + '</div>';
                        <?php if ($is_editor): ?>
                        // Editor context: actionable "apply" button (mutates the builder).
                        if (ins.action) {
                            html += '<button type="button" class="ff-ai-insight-action" data-action-type="' + esc(ins.type) + '" data-field-id="' + esc(ins.field_id || '') + '" data-action-title="' + esc(ins.title || '') + '" data-action-note="' + esc(ins.description || '') + '">';
	                            html += '<span class="dashicons dashicons-edit" style="font-size:14px;width:14px;height:14px;"></span> ' + esc(recommendationActionLabel(ins));
                            html += '</button>';
                        }
                        <?php else: ?>
                        // Submissions / Analytics context: no builder to mutate, so
                        // link to the editor where the recommendation can be applied.
                        if (ins.action) {
                            var editUrl = <?php echo wp_json_encode(admin_url('admin.php?page=formforge-new&form_id=')); ?> + formId;
                            if (ins.field_id) {
                                editUrl += '&ff_ai_field=' + encodeURIComponent(ins.field_id);
                                editUrl += '&ff_ai_action=' + encodeURIComponent(ins.type || '');
                                editUrl += '&ff_ai_title=' + encodeURIComponent(ins.title || '');
                                editUrl += '&ff_ai_note=' + encodeURIComponent(ins.description || '');
                                editUrl += '#formforge-fields-list';
                            }
                            html += '<a href="' + editUrl + '" class="ff-ai-insight-action" style="text-decoration:none;">';
	                            html += '<span class="dashicons dashicons-edit" style="font-size:14px;width:14px;height:14px;"></span> ' + esc(recommendationActionLabel(ins)) + ' →';
                            html += '</a>';
                        }
                        <?php endif; ?>
                        html += '</div></div>';
                    }
                    html += '</div>';
                }

                $results.html(html).show();
            }
            <?php if ($is_editor): ?>

            // Editor-only: apply a recommendation directly to the builder's
            // field JSON. Scoped to this panel so it can't fire on another.
            $panel.on('click', '.ff-ai-insight-action', function(){
                var $b = $(this);
                var actionType = $b.data('action-type');
                var fieldId = $b.data('field-id');
                if (!fieldId && actionType !== 'split_form') return;
                var originalLabel = $.trim($b.text());
                var actionTitle = $b.attr('data-action-title') || '';
                var actionNote = $b.attr('data-action-note') || '';

                function findFieldIndex(list, id) {
                    for (var i = 0; i < list.length; i++) {
                        if (String(list[i].id || '') === String(id || '')) return i;
                    }
                    return -1;
                }

	                function markActionApplied(message) {
	                    $b.text(originalLabel ? (originalLabel + ' - ' + message) : message).prop('disabled', true).css({opacity: 0.9, background: '#eff6ff', color: '#1d4ed8', borderColor: '#bfdbfe'});
	                    $b.closest('.ff-ai-insight-card').find('.ff-ai-action-confirmation').remove();
	                    $b.closest('.ff-ai-insight-card').append('<div class="ff-ai-action-confirmation">' + esc(message) + '</div>');
	                }

                function openTarget(message) {
                    if (fieldId && typeof window.formforgeOpenAiTargetField === 'function') {
                        window.formforgeOpenAiTargetField({
                            fieldId: fieldId,
                            title: actionTitle,
                            note: actionNote,
                            status: message
                        });
                    }
                }

                var fieldsJson = $('#formforge-fields-json').val();
                var fields;
                try { fields = JSON.parse(fieldsJson); } catch(e) { return; }
                var changed = false;
                var applyMessage = '';

                switch (actionType) {
                    case 'make_optional':
                        var optionalIndex = findFieldIndex(fields, fieldId);
                        if (optionalIndex >= 0) {
                            var wasRequired = !!fields[optionalIndex].required;
                            fields[optionalIndex].required = false;
                            changed = true;
                            applyMessage = wasRequired
                                ? <?php echo wp_json_encode(__('Changed required to optional', 'formforge')); ?>
                                : <?php echo wp_json_encode(__('Field was already optional', 'formforge')); ?>;
                        }
                        break;
                    case 'remove_field':
                        <?php /* translators: %s: field label */ ?>
                        var rmTpl = <?php echo wp_json_encode(__('Remove field "%s" from the form?', 'formforge')); ?>;
                        if (!confirm(rmTpl.replace('%s', fieldId))) return;
                        fields = fields.filter(function(f){ return f.id !== fieldId; });
                        changed = true;
                        applyMessage = <?php echo wp_json_encode(__('Field removed from form', 'formforge')); ?>;
                        break;
                    case 'add_placeholder':
                        var $fieldCard = $('.formforge-field-card').filter(function(){
                            var idx = $(this).data('index');
                            return fields[idx] && fields[idx].id === fieldId;
                        });
                        if ($fieldCard.length) { $fieldCard.find('.formforge-edit-field').trigger('click'); }
	                        applyMessage = <?php echo wp_json_encode(__('Opened in editor', 'formforge')); ?>;
                        markActionApplied(applyMessage);
                        openTarget(applyMessage);
                        return;
                    case 'split_form':
                        var settingsJson = $('#formforge-settings-json').val();
                        var settings;
                        try { settings = JSON.parse(settingsJson); } catch(e) { settings = {}; }
                        settings.multi_step = true;
                        $('#formforge-settings-json').val(JSON.stringify(settings));
                        applyMessage = <?php echo wp_json_encode(__('Multi-step mode enabled', 'formforge')); ?>;
                        markActionApplied(applyMessage);
                        return;
                    default:
	                        applyMessage = <?php echo wp_json_encode(__('Target field highlighted', 'formforge')); ?>;
                        openTarget(applyMessage);
                        markActionApplied(applyMessage);
                        return;
                }

                if (changed) {
                    $('#formforge-fields-json').val(JSON.stringify(fields));
                    if (typeof window.formforgeRebuildFields === 'function') {
                        window.formforgeRebuildFields(fields);
                    }
                    markActionApplied(applyMessage || <?php echo wp_json_encode(__('Recommendation applied', 'formforge')); ?>);
                    openTarget(applyMessage || <?php echo wp_json_encode(__('Recommendation applied', 'formforge')); ?>);
                    $.post(formforgeAdmin.ajaxUrl, {
                        action: 'formforge_ai_clear_cache',
                        nonce: formforgeAdmin.nonce,
                        form_id: formId
                    });
                }
            });
            <?php endif; ?>
        })(jQuery);
        </script>
        <?php
    }

    public function render_form_editor() {
        $form_id = intval($_GET['form_id'] ?? 0); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin UI state, no data mutation.
        // Use get_unfiltered — admin must be able to edit / delete every
        // saved form, including those above the Free soft-limit. Public
        // shortcode + REST keep using get so end-users still hit the cap.
        $form = $form_id ? FORMFORGE_Form_Builder::instance()->get_unfiltered($form_id) : null;
        $field_types = FORMFORGE_Field_Types::get_types();
        $shortcode_docs_url = defined('FORGE_DOCS_URL_FORMFORGE')
            ? rtrim(FORGE_DOCS_URL_FORMFORGE, '/') . '/docs/user-guide/adding-a-form-to-your-website/'
            : '';

        // bug 3: Lock Pro integration cards on Free plan so users
        // can't fill in API keys / toggles that the save handler silently
        // strips. Inlined CSS + JS to bypass any browser-cached admin.css.
        if (class_exists('FORMFORGE_License') && FORMFORGE_License::instance()->is_free()) {
            ?>
            <style>
                .formforge-builder .ff-settings-card--pro-locked { position: relative; }
                .formforge-builder .ff-settings-card--pro-locked > *:not(:first-child) {
                    pointer-events: none !important;
                    opacity: 0.45;
                    filter: grayscale(0.5);
                    user-select: none;
                }
                .formforge-builder .ff-settings-card--pro-locked input,
                .formforge-builder .ff-settings-card--pro-locked select,
                .formforge-builder .ff-settings-card--pro-locked textarea,
                .formforge-builder .ff-settings-card--pro-locked button {
                    pointer-events: none !important;
                    cursor: not-allowed !important;
                }
                .formforge-builder .ff-settings-card--pro-locked::after {
                    content: '🔒 Install Form Forge Pro to enable';
                    position: absolute;
                    top: 12px;
                    right: 16px;
                    background: #fff7ed;
                    color: #b45309;
                    border: 1px solid #fed7aa;
                    border-radius: 4px;
                    padding: 3px 8px;
                    font-size: 11px;
                    font-weight: 600;
                    text-transform: uppercase;
                    letter-spacing: 0.3px;
                    pointer-events: none;
                    z-index: 5;
                }
            </style>
            <script>
            (function () {
                function markProCards() {
                    var cards = document.querySelectorAll('.formforge-builder .ff-settings-card');
                    cards.forEach(function (card) {
                        if (card.classList.contains('ff-settings-card--pro-locked')) return;
                        var head = card.children[0];
                        if (!head) return;
                        var spans = head.querySelectorAll('span');
                        for (var i = 0; i < spans.length; i++) {
                            if (/^PRO$/i.test((spans[i].textContent || '').trim())) {
                                card.classList.add('ff-settings-card--pro-locked');
                                return;
                            }
                        }
                    });
                }
                if (document.readyState === 'loading') {
                    document.addEventListener('DOMContentLoaded', markProCards);
                } else {
                    markProCards();
                }
                setTimeout(markProCards, 500);
                setTimeout(markProCards, 1500);
            })();
            </script>
            <?php
        }
        ?>
        <div class="wrap formforge-editor-wrap">
            <?php FORMFORGE_Admin_Tabs::render('formforge-new'); ?>
            <?php FORMFORGE_License::instance()->render_upgrade_banner(); ?>
            <h1 style="display:flex;align-items:center;gap:12px;">
                <?php echo $form ? esc_html__('Edit Form', 'formforge') : esc_html__('New Form', 'formforge'); ?>
                <?php Forge_Docs_Link::render('formforge', 'the-form-builder-interface', __('Form builder', 'formforge'), __('Canvas, field palette, settings — the builder interface explained.', 'formforge')); ?>
                <?php if ($form): ?>
                <a href="<?php echo esc_url(admin_url('admin.php?page=formforge-new')); ?>" style="display:inline-flex;align-items:center;gap:4px;background:#3B82F6;color:#fff;text-decoration:none;padding:6px 14px;border-radius:6px;font-size:14px;font-weight:600;">
                    <span class="dashicons dashicons-plus-alt2" style="font-size:14px;width:14px;height:14px;"></span>
                    <?php esc_html_e('New Form', 'formforge'); ?>
                </a>
                <a href="<?php echo esc_url(admin_url('admin.php?page=formforge')); ?>" style="display:inline-flex;align-items:center;gap:4px;color:#64748b;text-decoration:none;padding:6px 14px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;font-weight:500;">
                    <?php esc_html_e('All Forms', 'formforge'); ?>
                </a>
                <?php endif; ?>
            </h1>

            <?php
            // keep the submissions entry point, but also expose the same
            // analysis in the editor so recommendations are actionable where
            // users actually change fields.
            if ($form) {
                $this->render_ai_analysis_widget($form_id, 'editor');
            }
            ?>

            <?php if (!$form):
                $all_templates = FORMFORGE_Templates::get_all();
                $categories = FORMFORGE_Templates::get_categories();
            ?>
            <!-- Start Options -->
            <div class="ff-start-options" style="display:flex;align-items:center;gap:14px;margin-bottom:16px;">
                <button type="button" id="ff-start-blank" class="ff-blank-btn" style="display:inline-flex;align-items:center;gap:8px;background:#fff;color:#3B82F6;border:2px solid #3B82F6;padding:10px 20px;border-radius:10px;font-size:14px;font-weight:700;cursor:pointer;transition:all 0.15s;box-shadow:0 1px 4px rgba(59,130,246,0.12);">
                    <span class="dashicons dashicons-plus-alt2" style="font-size:18px;width:18px;height:18px;"></span>
                    <?php esc_html_e('Start with blank form', 'formforge'); ?>
                </button>
                <span class="ff-or" style="font-size:14px;color:#94a3b8;font-style:italic;"><?php esc_html_e('or choose a template:', 'formforge'); ?></span>
            </div>
            <script>
            (function(){
                document.getElementById('ff-start-blank').addEventListener('click', function(){
                    // Hide AI hero and template section
                    var hero = document.querySelector('.ff-ai-hero');
                    var catSection = document.querySelector('.ff-cat-section');
                    var startOptions = document.querySelector('.ff-start-options');
                    if (hero) hero.style.display = 'none';
                    if (catSection) catSection.style.display = 'none';
                    if (startOptions) startOptions.style.display = 'none';

                    // Set default title
                    var titleInput = document.getElementById('formforge-title');
                    if (titleInput && !titleInput.value) {
                        titleInput.value = <?php echo wp_json_encode(__('Untitled Form', 'formforge')); ?>;
                    }

                    // Scroll to builder
                    var builder = document.querySelector('.formforge-builder');
                    if (builder) builder.scrollIntoView({behavior: 'smooth', block: 'start'});
                });
            })();
            </script>

            <!-- AI Generate — Hero Section -->
            <div class="ff-ai-hero"<?php echo FORMFORGE_License::instance()->is_free() ? ' style="opacity:0.7;position:relative;"' : ''; ?>>
                <div class="ff-ai-hero-inner">
                    <div class="ff-ai-hero-top">
                        <div class="ff-ai-hero-badge"><span class="dashicons dashicons-superhero-alt"></span> AI-Powered <?php if (FORMFORGE_License::instance()->is_free()): ?><span style="background:rgba(255,255,255,0.3);padding:1px 6px;border-radius:4px;font-size:14px;margin-left:4px;">PRO</span><?php endif; ?></div>
                        <h2><?php esc_html_e('Describe your form and AI will build it', 'formforge'); ?></h2>
                    </div>
                    <div class="ff-ai-hero-bar">
                        <input type="text" id="ff-ai-hero-prompt" placeholder="<?php esc_attr_e('e.g. contact form with name, email, message', 'formforge'); ?>"<?php echo FORMFORGE_License::instance()->is_free() ? ' disabled' : ''; ?>>
                        <button type="button" class="ff-ai-hero-btn" id="ff-ai-hero-generate" data-forge-ai-action="generate_form"<?php echo FORMFORGE_License::instance()->is_free() ? ' disabled style="opacity:0.6;cursor:not-allowed;"' : ''; ?>>
                            <?php if (FORMFORGE_License::instance()->is_free()): ?>
                                <span class="dashicons dashicons-lock" style="font-size:14px;width:14px;height:14px;"></span>
                                <?php esc_html_e('Upgrade to Pro', 'formforge'); ?>
                            <?php else: ?>
                                <span class="dashicons dashicons-controls-play"></span>
                                <?php esc_html_e('Generate', 'formforge'); ?> <?php echo class_exists('Forge_Credits_Widget') ? Forge_Credits_Widget::render_badge_html('generate_form', 'ff') : ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- trusted pre-built markup from Forge_Credits_Widget::render_badge_html ?>
                            <?php endif; ?>
                        </button>
                    </div>
                    <?php if (!FORMFORGE_License::instance()->is_free()): ?>
                    <!-- AI loading panel — replaces native button-only spinner -->
                    <div class="ff-ai-loading" id="ff-ai-loading" hidden>
                        <div class="ff-ai-loading-spinner"></div>
                        <div class="ff-ai-loading-text"><?php esc_html_e('AI is building your form… (~5–10 seconds)', 'formforge'); ?></div>
                    </div>

                    <!-- AI error panel — replaces alert popups -->
                    <div class="ff-ai-error" id="ff-ai-error" hidden role="alert" aria-live="polite">
                        <div class="ff-ai-error-row">
                            <span class="dashicons dashicons-warning ff-ai-error-icon" aria-hidden="true"></span>
                            <div class="ff-ai-error-body">
                                <div class="ff-ai-error-title" id="ff-ai-error-title"></div>
                                <div class="ff-ai-error-message" id="ff-ai-error-message"></div>
                            </div>
                            <button type="button" class="ff-ai-error-close" id="ff-ai-error-close" aria-label="<?php esc_attr_e('Dismiss', 'formforge'); ?>">
                                <span class="dashicons dashicons-no-alt"></span>
                            </button>
                        </div>
                        <div class="ff-ai-error-actions" id="ff-ai-error-actions"></div>
                    </div>
                    <?php endif; ?>
                </div>
                <?php if (FORMFORGE_License::instance()->is_free()): ?>
                <div style="position:absolute;top:0;left:0;right:0;bottom:0;cursor:pointer;z-index:10;" onclick="window.open('<?php echo esc_url(FORMFORGE_License::instance()->upgrade_url()); ?>','_blank')"></div>
                <?php endif; ?>
            </div>

            <!-- Template Categories -->
            <div class="ff-cat-section">
                <div class="ff-cat-header">
                    <h2><?php esc_html_e('Or start from a template', 'formforge'); ?></h2>
                    <span class="ff-cat-count"><?php echo esc_html(count($all_templates)); ?> templates in <?php echo esc_html(count($categories)); ?> categories</span>
                </div>
                <div class="ff-cat-grid">
                    <?php foreach ($categories as $cat): ?>
                    <div class="ff-cat-card" data-cat="<?php echo esc_attr($cat['slug']); ?>">
                        <div class="ff-cat-card-icon"><span class="dashicons dashicons-<?php echo esc_attr($cat['icon']); ?>"></span></div>
                        <div class="ff-cat-card-name"><?php echo esc_html($cat['label']); ?></div>
                        <div class="ff-cat-card-count"><?php echo esc_html($cat['count']); ?> templates</div>
                    </div>
                    <?php endforeach; ?>
                </div>
            </div>

            <!-- Template Modal (hidden) -->
            <div class="ff-modal-overlay" id="ff-tpl-modal" style="display:none;">
                <div class="ff-modal">
                    <div class="ff-modal-header">
                        <button type="button" class="ff-modal-back" id="ff-modal-back"><span class="dashicons dashicons-arrow-left-alt2"></span></button>
                        <h3 id="ff-modal-title"></h3>
                        <div class="ff-modal-search-wrap">
                            <span class="dashicons dashicons-search"></span>
                            <input type="text" id="ff-modal-search" placeholder="<?php esc_attr_e('Search...', 'formforge'); ?>" autocomplete="off">
                        </div>
                        <button type="button" class="ff-modal-close" id="ff-modal-close"><span class="dashicons dashicons-no-alt"></span></button>
                    </div>
                    <div class="ff-modal-body" id="ff-modal-body"></div>
                </div>
            </div>

            <style>
            /* AI Hero — full-width bar matching the template grid below it. */
            .ff-ai-hero{margin:16px 0;background:linear-gradient(135deg,#3B82F6 0%,#2563EB 100%);border-radius:14px;box-shadow:0 4px 20px rgba(59,130,246,0.2);}
            .ff-ai-hero-inner{padding:20px 24px;}
            .ff-ai-hero-top{text-align:center;margin-bottom:14px;}
            .ff-ai-hero-badge{display:inline-flex;align-items:center;gap:4px;background:rgba(255,255,255,0.15);padding:3px 10px;border-radius:20px;font-size:14px;font-weight:600;color:#fff;margin-bottom:6px;}
            .ff-ai-hero-badge .dashicons{font-size:14px;width:12px;height:12px;}
            .ff-ai-hero-top h2{margin:0!important;font-size:16px!important;font-weight:600!important;color:#fff!important;line-height:1.3!important;padding:0!important;border:none!important;}
            .ff-ai-hero-bar{display:flex;align-items:stretch;background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 2px 12px rgba(0,0,0,0.1);margin:0 8px;}
            .ff-admin .wrap .ff-ai-hero-bar input[type="text"],.ff-ai-hero-bar input[type="text"]{flex:1;border:none!important;padding:0 18px!important;font-size:14px!important;background:transparent!important;color:#1e293b!important;font-family:inherit;height:48px!important;box-shadow:none!important;border-radius:0!important;outline:none!important;margin:0!important;}
            .ff-ai-hero-bar input[type="text"]::placeholder{color:#BFDBFE;}
            .ff-ai-hero-btn{display:inline-flex;align-items:center;gap:6px;background:#3B82F6;color:#fff;border:none;padding:0 24px;font-size:14px;font-weight:700;cursor:pointer;transition:all 0.15s;white-space:nowrap;margin:0;border-radius:0;height:48px;}
            .ff-ai-hero-btn:hover{background:#2D74E8;}
            .ff-ai-hero-btn .dashicons{font-size:14px;width:14px;height:14px;}
            .ff-ai-hero-btn.loading{opacity:0.7;pointer-events:none;}
            .ff-ai-hero-btn.loading .dashicons{animation:ff-spin 1s linear infinite;}
            .ff-credits-badge{display:inline-block;background:rgba(255,255,255,0.25);color:#fff;font-size:14px;font-weight:600;padding:1px 5px;border-radius:3px;vertical-align:middle;margin-left:2px;letter-spacing:0.3px;opacity:0.8;}
            @keyframes ff-spin{to{transform:rotate(360deg);}}

            /* AI loading panel — friendly progress block under the hero bar.
 [hidden] override is mandatory because `.ff-ai-loading{display:flex}`
 has the same selector specificity as the user-agent
 `[hidden]{display:none}` rule but loads later, so without this
 override the spinner shows on initial page load before any click. */
            .ff-ai-loading{margin:12px 8px 0;padding:14px 18px;background:rgba(255,255,255,0.95);border-radius:10px;display:flex;align-items:center;gap:12px;}
            .ff-ai-loading[hidden]{display:none;}
            .ff-ai-loading-spinner{width:20px;height:20px;border:2px solid #DBEAFE;border-top-color:#3B82F6;border-radius:50%;flex-shrink:0;animation:ff-spin 0.8s linear infinite;}
            .ff-ai-loading-text{font-size:14px;color:#1e293b;font-weight:500;}

            /* AI error panel — replaces alert. Same [hidden] override
 principle as .ff-ai-loading above — defensive even though
 .ff-ai-error has no display override of its own today. */
            .ff-ai-error{margin:12px 8px 0;padding:14px 16px;background:#FEF2F2;border:1px solid #FECACA;border-left:4px solid #DC2626;border-radius:10px;color:#1e293b;}
            .ff-ai-error[hidden]{display:none;}
            .ff-ai-error-row{display:flex;align-items:flex-start;gap:12px;}
            .ff-ai-error-icon{color:#DC2626;font-size:20px;width:20px;height:20px;flex-shrink:0;margin-top:1px;}
            .ff-ai-error-body{flex:1;min-width:0;}
            .ff-ai-error-title{font-size:14px;font-weight:700;color:#991B1B;margin-bottom:4px;}
            .ff-ai-error-message{font-size:14px;color:#1f2937;line-height:1.45;}
            .ff-ai-error-close{background:none;border:none;cursor:pointer;color:#9CA3AF;padding:2px;flex-shrink:0;border-radius:4px;display:flex;align-items:center;justify-content:center;transition:all 0.15s;}
            .ff-ai-error-close:hover{color:#374151;background:rgba(0,0,0,0.05);}
            .ff-ai-error-close .dashicons{font-size:16px;width:16px;height:16px;}
            .ff-ai-error-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px;padding-left:32px;}
            .ff-ai-error-actions:empty{display:none;}
            .ff-ai-error-btn{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;border-radius:6px;font-size:14px;font-weight:600;cursor:pointer;text-decoration:none;border:1px solid transparent;transition:all 0.15s;line-height:1.2;}
            .ff-ai-error-btn-primary{background:#DC2626;color:#fff;border-color:#DC2626;}
            .ff-ai-error-btn-primary:hover{background:#B91C1C;border-color:#B91C1C;color:#fff;}
            .ff-ai-error-btn-secondary{background:#fff;color:#374151;border-color:#D1D5DB;}
            .ff-ai-error-btn-secondary:hover{background:#F9FAFB;border-color:#9CA3AF;color:#111827;}
            .ff-ai-error-btn[disabled]{opacity:0.6;cursor:not-allowed;pointer-events:none;}
            .ff-ai-error-btn .dashicons{font-size:14px;width:14px;height:14px;}

            /* Category Section */
            .ff-cat-section{margin:24px 0 20px;}
            .ff-cat-header{display:flex;align-items:baseline;gap:12px;margin-bottom:16px;}
            .ff-cat-header h2{margin:0;font-size:16px;font-weight:600;color:#1e293b;}
            .ff-cat-count{font-size:14px;color:#94a3b8;}
            .ff-cat-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:12px;}
            .ff-cat-card{background:#fff;border:1px solid #e2e8f0;border-radius:10px;padding:20px 16px;text-align:center;cursor:pointer;transition:all 0.2s;position:relative;overflow:hidden;}
            .ff-cat-card:hover{border-color:#3B82F6;background:#faf9ff;transform:translateY(-2px);box-shadow:0 4px 16px rgba(59,130,246,0.12);}
            .ff-cat-card-icon{width:44px;height:44px;margin:0 auto 10px;background:linear-gradient(135deg,#EFF6FF,#DBEAFE);border-radius:12px;display:flex;align-items:center;justify-content:center;}
            .ff-cat-card-icon .dashicons{font-size:22px;width:22px;height:22px;color:#3B82F6;}
            .ff-cat-card-name{font-size:14px;font-weight:600;color:#1e293b;margin-bottom:4px;}
            .ff-cat-card-count{font-size:14px;color:#94a3b8;}

            /* Modal */
            .ff-modal-overlay{position:fixed;inset:0;background:rgba(15,23,42,0.6);z-index:100000;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);animation:ff-fade-in 0.2s ease;}
            @keyframes ff-fade-in{from{opacity:0}to{opacity:1}}
            .ff-modal{background:#fff;border-radius:14px;width:90vw;max-width:900px;max-height:80vh;display:flex;flex-direction:column;box-shadow:0 20px 60px rgba(0,0,0,0.3);animation:ff-modal-in 0.25s ease;}
            @keyframes ff-modal-in{from{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}
            .ff-modal-header{display:flex;align-items:center;gap:12px;padding:16px 20px;border-bottom:1px solid #e2e8f0;flex-shrink:0;}
            .ff-modal-header h3{margin:0;font-size:16px;font-weight:600;flex:1;}
            .ff-modal-back,.ff-modal-close{background:none;border:none;cursor:pointer;padding:4px;border-radius:6px;color:#64748b;transition:all 0.15s;display:flex;align-items:center;justify-content:center;}
            .ff-modal-back:hover,.ff-modal-close:hover{background:#f1f5f9;color:#1e293b;}
            .ff-modal-back .dashicons,.ff-modal-close .dashicons{font-size:20px;width:20px;height:20px;}
            .ff-modal-search-wrap{position:relative;margin-left:auto;}
            .ff-modal-search-wrap .dashicons{position:absolute;left:10px;top:50%;transform:translateY(-50%);color:#94a3b8;font-size:16px;}
            .ff-modal-search-wrap input{padding:7px 12px 7px 32px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;width:200px;transition:border-color 0.15s;}
            .ff-modal-search-wrap input:focus{border-color:#3B82F6;outline:none;box-shadow:0 0 0 3px rgba(59,130,246,0.1);}
            .ff-modal-body{padding:20px;overflow-y:auto;flex:1;}
            .ff-modal-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:10px;}
            .ff-modal-card{display:flex;align-items:center;gap:12px;padding:14px 16px;border:1px solid #e2e8f0;border-radius:10px;cursor:pointer;transition:all 0.15s;background:#fff;}
            .ff-modal-card:hover{border-color:#3B82F6;background:#EFF6FF;box-shadow:0 2px 8px rgba(59,130,246,0.1);transform:translateY(-1px);}
            .ff-modal-card.creating{opacity:0.5;pointer-events:none;}
            .ff-modal-card-icon{width:40px;height:40px;display:flex;align-items:center;justify-content:center;background:#f1f5f9;border-radius:10px;flex-shrink:0;}
            .ff-modal-card-icon .dashicons{font-size:18px;width:18px;height:18px;color:#3B82F6;}
            .ff-modal-card-info{flex:1;min-width:0;}
            .ff-modal-card-title{font-size:14px;font-weight:600;color:#1e293b;}
            .ff-modal-card-desc{font-size:14px;color:#64748b;margin-top:2px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}
            .ff-modal-card-meta{font-size:14px;color:#94a3b8;white-space:nowrap;flex-shrink:0;}
            .ff-modal-empty{text-align:center;padding:40px;color:#94a3b8;font-size:14px;}
            </style>

            <script>
            (function($){
                var templates = <?php echo wp_json_encode($all_templates); ?>;
                var categories = <?php echo wp_json_encode($categories); ?>;
                var modal = document.getElementById('ff-tpl-modal');
                var modalBody = document.getElementById('ff-modal-body');
                var modalTitle = document.getElementById('ff-modal-title');
                var modalSearch = document.getElementById('ff-modal-search');

                /* Category card click → open modal */
                document.querySelectorAll('.ff-cat-card').forEach(function(card){
                    card.addEventListener('click', function(){ openModal(this.dataset.cat); });
                });

                function openModal(catSlug) {
                    var cat = categories[catSlug];
                    modalTitle.textContent = cat ? cat.label : 'All Templates';
                    modalSearch.value = '';

                    var html = '<div class="ff-modal-grid">';
                    for (var key in templates) {
                        var t = templates[key];
                        if (t.category !== catSlug) continue;
                        html += '<div class="ff-modal-card" data-key="'+key+'" data-search="'+(t.title+' '+t.description).toLowerCase()+'">'
                            +'<div class="ff-modal-card-icon"><span class="dashicons dashicons-'+t.icon+'"></span></div>'
                            +'<div class="ff-modal-card-info"><div class="ff-modal-card-title">'+t.title+'</div><div class="ff-modal-card-desc">'+t.description+'</div></div>'
                            +'<div class="ff-modal-card-meta">'+t.fields.length+' fields</div>'
                            +'</div>';
                    }
                    html += '</div>';
                    modalBody.innerHTML = html;
                    modal.style.display = 'flex';
                    document.body.style.overflow = 'hidden';
                    modalSearch.focus();
                }

                /* Close modal */
                function closeModal(){
                    modal.style.display = 'none';
                    document.body.style.overflow = '';
                }
                document.getElementById('ff-modal-close').addEventListener('click', closeModal);
                document.getElementById('ff-modal-back').addEventListener('click', closeModal);
                modal.addEventListener('click', function(e){ if(e.target===modal) closeModal(); });
                document.addEventListener('keydown', function(e){ if(e.key==='Escape') closeModal(); });

                /* Search within modal */
                modalSearch.addEventListener('input', function(){
                    var q = this.value.toLowerCase();
                    var cards = modalBody.querySelectorAll('.ff-modal-card');
                    var visible = 0;
                    cards.forEach(function(c){
                        var match = !q || c.dataset.search.indexOf(q) !== -1;
                        c.style.display = match ? '' : 'none';
                        if(match) visible++;
                    });
                    var empty = modalBody.querySelector('.ff-modal-empty');
                    if(empty) empty.remove();
                    if(!visible){
                        var grid = modalBody.querySelector('.ff-modal-grid');
                        grid.insertAdjacentHTML('afterend','<div class="ff-modal-empty">No templates found</div>');
                    }
                });

                /* Template card click → load fields into builder (no save) */
                modalBody.addEventListener('click', function(e){
                    var card = e.target.closest('.ff-modal-card');
                    if(!card) return;
                    var key = card.dataset.key, tpl = templates[key];
                    if(!tpl) return;
                    closeModal();
                    loadTemplateIntoBuilder(tpl.title, tpl.fields);
                });

                function loadTemplateIntoBuilder(title, tplFields) {
                    /* Keep AI hero visible so user can refine prompt; only hide categories */
                    document.querySelector('.ff-cat-section').style.display = 'none';

                    function tryLoad() {
                        if (typeof window.formforgeLoadTemplate === 'function') {
                            window.formforgeLoadTemplate(title, tplFields);
                            var b = document.querySelector('.formforge-builder');
                            if(b) b.scrollIntoView({behavior:'smooth', block:'start'});
                        } else {
                            setTimeout(tryLoad, 50);
                        }
                    }
                    tryLoad();
                }

                /* AI Generate from hero — load into builder via REST */
                /* In-modal error UX (R3-AI-#4): replace alert with friendly
 * inline error block + retry / alt-action buttons. */
                var $aiBtn = $('#ff-ai-hero-generate');
                var $aiPrompt = $('#ff-ai-hero-prompt');
                var $aiLoading = $('#ff-ai-loading');
                var $aiError = $('#ff-ai-error');
                var $aiErrorTitle = $('#ff-ai-error-title');
                var $aiErrorMessage = $('#ff-ai-error-message');
                var $aiErrorActions = $('#ff-ai-error-actions');
                var aiBtnDefault = $aiBtn.html();
                var ffAiPromptKey = 'formforge_ai_last_prompt';

                /* Restore last prompt if user closed modal mid-task */
                try {
                    var saved = window.sessionStorage && window.sessionStorage.getItem(ffAiPromptKey);
                    if (saved && !$aiPrompt.val()) $aiPrompt.val(saved);
                } catch(e) {}

                /* Persist prompt as user types */
                $aiPrompt.on('input', function(){
                    try { if (window.sessionStorage) window.sessionStorage.setItem(ffAiPromptKey, $(this).val()); } catch(e) {}
                });

                /* Friendly error message map. Worker / WP error codes →
 * user-facing copy. The default-fallback uses the server
 * message verbatim (already sanitized by WP REST). */
                function classifyAiError(jqXHR, payload) {
                    var status = jqXHR ? jqXHR.status : 0;
                    var rawMessage = (payload && payload.message) ? String(payload.message) : '';
                    var rawError   = (payload && payload.error)   ? String(payload.error)   : '';
                    var combined   = (rawError + ' ' + rawMessage).toLowerCase();

                    var dashboardUrl = (typeof formforgeAdmin !== 'undefined' && formforgeAdmin.upgradeUrl)
                        ? formforgeAdmin.upgradeUrl : 'admin.php?page=forge-suite';
                    var billingUrl = 'admin.php?page=forge-suite';

                    /* Credits exhausted (HTTP 402 from Worker, surfaced via REST 200 + error field) */
                    if (rawError === 'credits_exhausted' || combined.indexOf('insufficient credits') !== -1 || combined.indexOf('credits exhausted') !== -1 || status === 402) {
                        var resetMsg = '<?php echo esc_js(__('Not enough credits for AI generation. Buy more or upgrade to Pro.', 'formforge')); ?>';
                        if (payload && payload.reset_date) {
                            try {
                                var d = new Date(payload.reset_date).toLocaleDateString();
                                resetMsg += ' <?php echo esc_js(__('Resets:', 'formforge')); ?> ' + d + '.';
                            } catch(e) {}
                        }
                        return {
                            title: '<?php echo esc_js(__('Out of credits', 'formforge')); ?>',
                            message: resetMsg,
                            retry: false,
                            actions: [
                                { label: '<?php echo esc_js(__('Buy credits', 'formforge')); ?>', url: billingUrl, style: 'primary' },
                                { label: '<?php echo esc_js(__('Upgrade plan', 'formforge')); ?>', url: dashboardUrl, style: 'secondary' }
                            ]
                        };
                    }
                    /* Unauthorized / no license / Pro-only */
                    if (status === 401 || status === 403 || rawError === 'pro_only' || combined.indexOf('unauthorized') !== -1 || combined.indexOf('requires pro') !== -1) {
                        return {
                            title: '<?php echo esc_js(__('Active subscription required', 'formforge')); ?>',
                            message: '<?php echo esc_js(__('Sign in to your Forge Suite account or activate a license to use AI generation.', 'formforge')); ?>',
                            retry: false,
                            actions: [
                                { label: '<?php echo esc_js(__('Open Dashboard', 'formforge')); ?>', url: 'admin.php?page=forge-suite', style: 'primary' }
                            ]
                        };
                    }
                    /* Rate limit */
                    if (status === 429 || combined.indexOf('rate limit') !== -1 || combined.indexOf('too many') !== -1) {
                        return {
                            title: '<?php echo esc_js(__('Too many requests', 'formforge')); ?>',
                            message: '<?php echo esc_js(__('Wait a minute and try again. The service limits generation frequency.', 'formforge')); ?>',
                            retry: true,
                            cooldown: 60
                        };
                    }
                    /* Network error / 0 status / blocked / offline */
                    if (status === 0 || combined.indexOf('failed to fetch') !== -1 || combined.indexOf('network') !== -1) {
                        return {
                            title: '<?php echo esc_js(__('Cannot reach the server', 'formforge')); ?>',
                            message: '<?php echo esc_js(__('Check your internet connection and try again.', 'formforge')); ?>',
                            retry: true
                        };
                    }
                    /* Server 5xx */
                    if (status >= 500 && status < 600) {
                        return {
                            title: '<?php echo esc_js(__('Service temporarily unavailable', 'formforge')); ?>',
                            message: '<?php echo esc_js(__('We are already on it — please try again in a couple of minutes.', 'formforge')); ?>',
                            retry: true
                        };
                    }
                    /* Validation / 400 */
                    if (status === 400 || combined.indexOf('invalid payload') !== -1 || combined.indexOf('empty_prompt') !== -1) {
                        return {
                            title: '<?php echo esc_js(__('Could not understand the request', 'formforge')); ?>',
                            message: '<?php echo esc_js(__('Try again with a more specific form description (fields, types, required-ness).', 'formforge')); ?>',
                            retry: true
                        };
                    }
                    /* Route not reachable (404 / non-JSON HTML). The AI
 * generation REST route isn't answering — Pro add-on
 * inactive, REST API disabled, or a server rewrite that
 * doesn't route /wp-json/. Show that instead of a bare
 * "Unknown error". */
                    if (status === 404) {
                        return {
                            title: '<?php echo esc_js(__('AI generation unavailable', 'formforge')); ?>',
                            message: '<?php echo esc_js(__('The AI service could not be reached. Make sure Form Forge Pro is active and the WordPress REST API is enabled, then try again.', 'formforge')); ?>',
                            retry: true
                        };
                    }
                    /* No active license — the Worker reported the license is
 * not active even though the local plan let the request
 * through (e.g. license cancelled/expired while still in
 * the 24h grace window, or a forced-Pro dev state with no
 * real key). Came back as a 200+message or a non-403 status,
 * so it misses the 401/403 branch above. Surface the real
 * cause instead of a generic "Unknown error". */
                    if (rawError === 'no_license' || rawError === 'license_required' || rawError === 'no_active_license' || rawError === 'license_inactive'
                        || combined.indexOf('no active license') !== -1 || combined.indexOf('no license') !== -1
                        || combined.indexOf('not licensed') !== -1 || combined.indexOf('license required') !== -1
                        || combined.indexOf('inactive license') !== -1 || combined.indexOf('invalid license') !== -1) {
                        return {
                            title: '<?php echo esc_js(__('Active subscription required', 'formforge')); ?>',
                            message: '<?php echo esc_js(__('Your license isn’t active. Sign in to your Forge Suite account or activate a license to use AI generation.', 'formforge')); ?>',
                            retry: false,
                            actions: [
                                { label: '<?php echo esc_js(__('Open Dashboard', 'formforge')); ?>', url: 'admin.php?page=forge-suite', style: 'primary' }
                            ]
                        };
                    }
                    /* Default fallback — show the server message (or error code)
 * if any, so the user always gets something more specific
 * than a bare "Unknown error". */
                    return {
                        title: '<?php echo esc_js(__('Could not generate the form', 'formforge')); ?>',
                        message: rawMessage || rawError || '<?php echo esc_js(__('Unknown error. Try again or contact support.', 'formforge')); ?>',
                        retry: true
                    };
                }

                /* Render error block (replaces alert) */
                function renderAiError(spec) {
                    $aiLoading.attr('hidden', true);
                    $aiErrorTitle.text(spec.title);
                    $aiErrorMessage.text(spec.message);
                    $aiErrorActions.empty();

                    if (spec.retry) {
                        var $retry = $('<button type="button" class="ff-ai-error-btn ff-ai-error-btn-primary"></button>')
                            .html('<span class="dashicons dashicons-image-rotate"></span> <?php echo esc_js(__('Try again', 'formforge')); ?>')
                            .on('click', function(){
                                $aiError.attr('hidden', true);
                                runAiGenerate();
                            });
                        if (spec.cooldown && spec.cooldown > 0) {
                            $retry.prop('disabled', true);
                            var remaining = spec.cooldown;
                            <?php /* translators: %d: seconds remaining before retry is allowed */ ?>
                            var retryTpl = '<?php echo esc_js(__('Try again (%d)', 'formforge')); ?>';
                            var retryReady = '<?php echo esc_js(__('Try again', 'formforge')); ?>';
                            $retry.html('<span class="dashicons dashicons-clock"></span> ' + retryTpl.replace('%d', remaining));
                            var iv = setInterval(function(){
                                remaining--;
                                if (remaining <= 0) {
                                    clearInterval(iv);
                                    $retry.prop('disabled', false).html('<span class="dashicons dashicons-image-rotate"></span> ' + retryReady);
                                } else {
                                    $retry.html('<span class="dashicons dashicons-clock"></span> ' + retryTpl.replace('%d', remaining));
                                }
                            }, 1000);
                        }
                        $aiErrorActions.append($retry);
                    }
                    if (spec.actions && spec.actions.length) {
                        spec.actions.forEach(function(a){
                            var cls = 'ff-ai-error-btn ff-ai-error-btn-' + (a.style || 'secondary');
                            var $a = $('<a target="_blank" rel="noopener"></a>')
                                .attr('href', a.url).attr('class', cls).text(a.label);
                            $aiErrorActions.append($a);
                        });
                    }
                    $aiError.removeAttr('hidden');

                    /* Notify extensions (developer hook) */
                    try {
                        $(document).trigger('formforge:ai-error', [{ code: spec.title, message: spec.message }]);
                    } catch(e) {}
                }

                $('#ff-ai-error-close').on('click', function(){ $aiError.attr('hidden', true); });

                /* Single source of truth for "fire the AI request". Used by
 * initial click + Retry button. */
                function runAiGenerate() {
                    var prompt = $aiPrompt.val().trim();
                    if(!prompt) { $aiPrompt.focus(); return; }
                    $aiError.attr('hidden', true);
                    $aiLoading.removeAttr('hidden');
                    $aiBtn.addClass('loading').html('<span class="dashicons dashicons-update"></span> Generating...');
                    $.ajax({
                        url: formforgeAdmin.restUrl + '/generate',
                        method: 'POST',
                        contentType: 'application/json',
                        data: JSON.stringify({ prompt: prompt }),
                        headers: { 'X-WP-Nonce': formforgeAdmin.restNonce },
                        success: function(r){
                            $aiBtn.removeClass('loading').html(aiBtnDefault);
                            $aiLoading.attr('hidden', true);
                            /* WP REST returns 200 even for credits_exhausted /
 * empty fields cases — handle them as errors. */
                            if (r && r.error === 'credits_exhausted') {
                                renderAiError(classifyAiError(null, r));
                                return;
                            }
                            if(r && r.fields && r.fields.length){
                                /* Success — clear stored prompt + hide error */
                                try { if (window.sessionStorage) window.sessionStorage.removeItem(ffAiPromptKey); } catch(e) {}
                                loadTemplateIntoBuilder(prompt.substring(0,60), r.fields);
                            } else {
                                renderAiError(classifyAiError(null, r));
                            }
                        },
                        error: function(jqXHR){
                            $aiBtn.removeClass('loading').html(aiBtnDefault);
                            $aiLoading.attr('hidden', true);
                            var payload = null;
                            try { payload = jqXHR && jqXHR.responseJSON ? jqXHR.responseJSON : (jqXHR && jqXHR.responseText ? JSON.parse(jqXHR.responseText) : null); } catch(e) {}
                            renderAiError(classifyAiError(jqXHR, payload));
                        }
                    });
                }

                $aiBtn.on('click', runAiGenerate);
            })(jQuery);
            </script>
            <?php endif; ?>

            <div class="formforge-editor-grid">
                <!-- Left: Field palette -->
                <div class="formforge-palette">
                    <h3 style="margin:0 0 4px;font-size:14px;font-weight:700;color:#1e293b;"><?php esc_html_e('Fields', 'formforge'); ?></h3>
                    <p class="description" style="margin:0 0 10px;font-size:14px;"><?php esc_html_e('Drag into the form area or click to add', 'formforge'); ?></p>
                    <?php
                    $categories = ['basic' => __('Basic', 'formforge'), 'choice' => __('Choice', 'formforge'), 'advanced' => __('Advanced', 'formforge'), 'layout' => __('Layout', 'formforge')];
                    foreach ($categories as $cat => $cat_label):
                    ?>
                        <h4><?php echo esc_html($cat_label); ?></h4>
                        <div class="formforge-field-buttons formforge-draggable-source">
                        <?php foreach ($field_types as $type => $info):
                            if (($info['category'] ?? '') !== $cat) continue;
                            $feature_gate = $this->field_type_feature_gate($type);
                            $is_locked_field = $feature_gate !== '' && !FORMFORGE_License::instance()->can($feature_gate);
                            $field_class = 'formforge-add-field ' . ($is_locked_field ? 'formforge-sidebar-locked' : 'formforge-sidebar-draggable');
                        ?>
                            <div class="<?php echo esc_attr($field_class); ?>"<?php if (!$is_locked_field): ?> data-type="<?php echo esc_attr($type); ?>" data-label="<?php echo esc_attr($info['label']); ?>"<?php endif; ?><?php if ($is_locked_field): ?> aria-disabled="true" title="<?php esc_attr_e('Requires Form Forge Pro', 'formforge'); ?>"<?php endif; ?>>
                                <span class="dashicons dashicons-<?php echo esc_attr($info['icon']); ?>"></span>
                                <?php echo esc_html($info['label']); ?>
                                <?php if ($is_locked_field): ?>
                                    <span class="formforge-pro-chip"><?php esc_html_e('PRO', 'formforge'); ?></span>
                                <?php endif; ?>
                            </div>
                        <?php endforeach; ?>
                        </div>
                    <?php endforeach; ?>
                </div>

                <!-- Center: Form builder -->
                <div class="formforge-builder">
                    <div class="formforge-form-title-wrap">
                        <input type="text" id="formforge-title" class="widefat" value="<?php echo esc_attr($form ? $form->title : ''); ?>" placeholder="<?php esc_attr_e('Form Title', 'formforge'); ?>">
                    </div>

                    <div id="formforge-fields-list" class="formforge-fields-sortable">
                        <?php if ($form && !empty($form->fields)):
                            foreach ($form->fields as $i => $field):
                                $this->render_field_card($field, $i);
                            endforeach;
                        else: ?>
                            <div class="formforge-empty-state" id="formforge-empty">
                                <p><?php esc_html_e('Drag fields here or click fields to start building your form.', 'formforge'); ?></p>
                            </div>
                        <?php endif; ?>
                    </div>

                    <?php
                    // Form Settings: Conversational Mode Toggle
                    $conv_can_use = FORMFORGE_License::instance()->can('conversational');
                    $conv_enabled = $conv_can_use && !empty($form->settings['conversational']);
                    ?>
                    <div class="ff-form-settings-bar" style="display:flex;align-items:center;gap:12px;padding:12px 16px;margin-bottom:12px;background:<?php echo esc_attr($conv_enabled ? '#EFF6FF' : '#f8fafc'); ?>;border:1px solid <?php echo esc_attr($conv_enabled ? '#BFDBFE' : '#e2e8f0'); ?>;border-radius:10px;transition:all 0.2s;" id="ff-conv-toggle-bar">
                        <label style="display:flex;align-items:center;gap:8px;cursor:<?php echo esc_attr($conv_can_use ? 'pointer' : 'not-allowed'); ?>;font-size:14px;font-weight:600;color:#334155;margin:0;">
                            <span class="dashicons dashicons-format-chat" style="color:#3B82F6;font-size:18px;width:18px;height:18px;"></span>
                            <?php esc_html_e('Conversational Mode', 'formforge'); ?>
                            <input type="checkbox" id="ff-conv-toggle" <?php checked($conv_enabled); ?> <?php disabled(!$conv_can_use); ?> style="margin:0;">
                            <?php if (!$conv_can_use): ?>
                                <span class="formforge-pro-chip"><?php esc_html_e('PRO', 'formforge'); ?></span>
                            <?php endif; ?>
                        </label>
                        <?php Forge_Docs_Link::render('formforge', 'conversational-mode-typeform-style-pro', __('Conversational mode (PRO)', 'formforge'), __('Typeform-style one-question-per-screen experience.', 'formforge')); ?>
                        <span id="ff-conv-note" style="font-size:14px;color:#64748b;<?php echo esc_attr(($conv_enabled || !$conv_can_use) ? '' : 'display:none;'); ?>">
                            <?php if ($conv_can_use): ?>
                                <?php esc_html_e('Form will display in conversational style &mdash; one question per screen.', 'formforge'); ?>
                            <?php else: ?>
                                <?php esc_html_e('Requires Form Forge Pro.', 'formforge'); ?>
                            <?php endif; ?>
                        </span>
                    </div>
                    <script>
                    (function(){
                        var canConversational = <?php echo $conv_can_use ? 'true' : 'false'; ?>;
                        var toggle = document.getElementById('ff-conv-toggle');
                        var bar = document.getElementById('ff-conv-toggle-bar');
                        var note = document.getElementById('ff-conv-note');
                        if (!toggle) return;
                        toggle.addEventListener('change', function(){
                            if (!canConversational) {
                                this.checked = false;
                                return;
                            }
                            var on = this.checked;
                            var settingsJson = document.getElementById('formforge-settings-json').value;
                            var settings;
                            try { settings = JSON.parse(settingsJson); } catch(e) { settings = {}; }
                            settings.conversational = on;
                            document.getElementById('formforge-settings-json').value = JSON.stringify(settings);
                            bar.style.background = on ? '#EFF6FF' : '#f8fafc';
                            bar.style.borderColor = on ? '#BFDBFE' : '#e2e8f0';
                            bar.querySelector('.dashicons').style.color = '#3B82F6';
                            note.style.display = on ? '' : 'none';
                        });
                    })();
                    </script>

                    <!-- Form Settings Card (submit button text, success message, redirect URL) -->
                    <?php
                    $submit_text     = esc_attr($form->settings['submit_text'] ?? __('Submit', 'formforge'));
                    $success_message = esc_textarea($form->settings['success_message'] ?? __('Thank you! Your submission has been received.', 'formforge'));
                    $redirect_url    = esc_attr($form->settings['redirect_url'] ?? '');
                    $submit_action   = sanitize_key($form->settings['submit_action'] ?? (!empty($form->settings['redirect_url']) ? 'redirect' : 'message'));
                    if (!in_array($submit_action, ['message', 'redirect', 'custom_js'], true)) $submit_action = 'message';
                    $custom_js       = esc_textarea($form->settings['custom_js'] ?? '');
                    ?>
                    <div class="ff-settings-card" style="margin-bottom:12px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden;">
                        <div style="display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid #e2e8f0;background:#f8fafc;cursor:pointer;" id="ff-formsettings-toggle-head">
                            <span class="dashicons dashicons-admin-generic" style="color:#3B82F6;font-size:16px;width:16px;height:16px;"></span>
                            <strong style="font-size:14px;color:#1e293b;"><?php esc_html_e('Form Settings', 'formforge'); ?></strong>
                            <span class="dashicons dashicons-arrow-down-alt2" id="ff-formsettings-chevron" style="margin-left:auto;font-size:14px;width:14px;height:14px;color:#94a3b8;transition:transform 0.2s;"></span>
                        </div>
                        <div id="ff-formsettings-body" style="padding:16px;display:none;">
                            <div style="margin-bottom:12px;">
                                <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Submit button text', 'formforge'); ?></label>
                                <input type="text" id="ff-submit-text" value="<?php echo esc_attr($submit_text); ?>" placeholder="<?php esc_attr_e('Submit', 'formforge'); ?>" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
                            </div>
                            <div style="margin-bottom:12px;">
                                <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('After submit', 'formforge'); ?></label>
                                <select id="ff-submit-action" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
                                    <option value="message" <?php selected($submit_action, 'message'); ?>><?php esc_html_e('Show success message', 'formforge'); ?></option>
                                    <option value="redirect" <?php selected($submit_action, 'redirect'); ?>><?php esc_html_e('Redirect to URL', 'formforge'); ?></option>
                                    <option value="custom_js" <?php selected($submit_action, 'custom_js'); ?>><?php esc_html_e('Run safe page action', 'formforge'); ?></option>
                                </select>
                            </div>
                            <div id="ff-submit-action-message" style="margin-bottom:12px;<?php echo esc_attr($submit_action === 'message' ? '' : 'display:none;'); ?>">
                                <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Success message', 'formforge'); ?></label>
                                <textarea id="ff-success-message" rows="2" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;resize:vertical;" placeholder="<?php esc_attr_e('Thank you! Your submission has been received.', 'formforge'); ?>"><?php echo esc_textarea($success_message); ?></textarea>
                                <p style="margin:4px 0 0;font-size:12px;color:#94a3b8;"><?php esc_html_e('Shown after successful submission. Leave empty for default.', 'formforge'); ?></p>
                            </div>
                            <div id="ff-submit-action-redirect" style="<?php echo esc_attr($submit_action === 'redirect' ? '' : 'display:none;'); ?>">
                                <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Redirect URL (optional)', 'formforge'); ?></label>
                                <input type="text" id="ff-redirect-url" value="<?php echo esc_attr($redirect_url); ?>" placeholder="/thank-you" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
                                <p style="margin:4px 0 0;font-size:12px;color:#94a3b8;"><?php esc_html_e('Root-relative or same-site absolute URLs only.', 'formforge'); ?></p>
                            </div>
                            <div id="ff-submit-action-custom" style="<?php echo esc_attr($submit_action === 'custom_js' ? '' : 'display:none;'); ?>">
                                <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Safe custom action', 'formforge'); ?></label>
                                <textarea id="ff-custom-js" rows="3" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;resize:vertical;" placeholder="document.getElementById('modal').classList.add('is-active');"><?php echo esc_textarea($custom_js); ?></textarea>
                                <p style="margin:4px 0 0;font-size:12px;color:#94a3b8;"><?php esc_html_e('Supported: add, remove, or toggle a CSS class on an element by ID or selector. External requests and arbitrary scripts are blocked.', 'formforge'); ?></p>
                            </div>
                        </div>
                    </div>
                    <script>
                    (function(){
                        var head = document.getElementById('ff-formsettings-toggle-head');
                        var body = document.getElementById('ff-formsettings-body');
                        var chevron = document.getElementById('ff-formsettings-chevron');
                        if (head) {
                            head.addEventListener('click', function(){
                                var open = body.style.display !== 'none';
                                body.style.display = open ? 'none' : 'block';
                                chevron.style.transform = open ? '' : 'rotate(180deg)';
                            });
                        }
                        function setSubmitActionVisibility() {
                            var action = document.getElementById('ff-submit-action');
                            var val = action ? action.value : 'message';
                            var msg = document.getElementById('ff-submit-action-message');
                            var redir = document.getElementById('ff-submit-action-redirect');
                            var custom = document.getElementById('ff-submit-action-custom');
                            if (msg) msg.style.display = val === 'message' ? '' : 'none';
                            if (redir) redir.style.display = val === 'redirect' ? '' : 'none';
                            if (custom) custom.style.display = val === 'custom_js' ? '' : 'none';
                        }
                        window.formforgeSyncFormSettings = function(){
                            var settingsInput = document.getElementById('formforge-settings-json');
                            if (!settingsInput) return {};
                            var settings;
                            try { settings = JSON.parse(settingsInput.value); } catch(e) { settings = {}; }

                            var subTxt = document.getElementById('ff-submit-text');
                            var action = document.getElementById('ff-submit-action');
                            var sucMsg = document.getElementById('ff-success-message');
                            var redir  = document.getElementById('ff-redirect-url');
                            var custom = document.getElementById('ff-custom-js');
                            if (subTxt) settings.submit_text = subTxt.value;
                            if (action) settings.submit_action = action.value;
                            if (sucMsg) settings.success_message = sucMsg.value;
                            if (redir) settings.redirect_url = redir.value;
                            if (custom) settings.custom_js = custom.value;

                            settingsInput.value = JSON.stringify(settings);
                            return settings;
                        };
                        var actionSelect = document.getElementById('ff-submit-action');
                        if (actionSelect) actionSelect.addEventListener('change', setSubmitActionVisibility);
                        setSubmitActionVisibility();

                        // Hook into save: merge form settings into settings JSON
                        jQuery(function(){
                        var saveBtn = document.getElementById('formforge-save');
                        if (saveBtn) {
                            saveBtn.addEventListener('click', function(){
                                window.formforgeSyncFormSettings();
                            }, true); // capture phase so it runs before the save handler
                        }
                        });
                    })();
                    </script>

                    <!-- audit-r3: Abandonment Tracking (per-form, default ON) -->
                    <?php
                    // Default ON: if the key is missing (newly created form, never
                    // touched the toggle) we treat it as enabled so funnel
                    // analytics work out of the box. Owners who opt out flip
                    // the toggle, persisting the explicit `false`.
                    $track_abandonment = !isset($form->settings['track_abandonment'])
                        ? true
                        : (bool) $form->settings['track_abandonment'];
                    // Capture values stays default OFF — value-level capture is
                    // a separate explicit opt-in for GDPR-friendly behavior.
                    $track_capture_values = !empty($form->settings['track_abandonment_capture_values']);
                    ?>
                    <div class="ff-settings-card" style="margin-bottom:12px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden;">
                        <div style="display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid #e2e8f0;background:#f8fafc;cursor:pointer;" id="ff-abandon-toggle-head">
                            <span class="dashicons dashicons-chart-line" style="color:#3B82F6;font-size:16px;width:16px;height:16px;"></span>
                            <strong style="font-size:14px;color:#1e293b;"><?php esc_html_e('Abandonment Tracking', 'formforge'); ?></strong>
                            <?php if (!FORMFORGE_License::instance()->can('abandonment')): ?>
                                <span style="font-size:14px;color:#94a3b8;background:#f1f5f9;padding:1px 6px;border-radius:4px;margin-left:4px;">PRO</span>
                            <?php endif; ?>
                            <span class="dashicons dashicons-arrow-down-alt2" id="ff-abandon-chevron" style="margin-left:auto;font-size:14px;width:14px;height:14px;color:#94a3b8;transition:transform 0.2s;"></span>
                        </div>
                        <div id="ff-abandon-body" style="padding:16px;display:none;">
                            <label class="ff-toggle-label" style="display:flex;align-items:center;gap:8px;font-size:14px;margin-bottom:10px;cursor:pointer;">
                                <input type="checkbox" id="ff-track-abandonment" <?php checked($track_abandonment); ?>>
                                <span><?php esc_html_e('Track form abandonment', 'formforge'); ?></span>
                            </label>
                            <p style="margin:0 0 12px;font-size:14px;color:#64748b;line-height:1.5;">
                                <?php esc_html_e('Track when users start filling the form but don\'t submit. Useful for funnel analytics. Captures: form ID, last touched field, timestamp, partial form data (no PII unless explicitly enabled).', 'formforge'); ?>
                            </p>
                            <div style="padding:10px 12px;border:1px solid #FCD34D;background:#FFFBEB;border-radius:6px;margin-bottom:12px;font-size:14px;color:#78350F;">
                                <strong><?php esc_html_e('GDPR consideration:', 'formforge'); ?></strong>
                                <?php esc_html_e('Enable only if you\'ve informed users in your privacy policy that partial form interactions are tracked.', 'formforge'); ?>
                            </div>
                            <div id="ff-track-capture-wrap" style="<?php echo esc_attr($track_abandonment ? '' : 'display:none;'); ?>padding-top:6px;border-top:1px solid #f1f5f9;">
                                <label class="ff-toggle-label" style="display:flex;align-items:center;gap:8px;font-size:14px;margin-bottom:6px;cursor:pointer;">
                                    <input type="checkbox" id="ff-track-capture-values" <?php checked($track_capture_values); ?>>
                                    <span><?php esc_html_e('Capture partial values (explicit opt-in)', 'formforge'); ?></span>
                                </label>
                                <p style="margin:0;font-size:14px;color:#94a3b8;line-height:1.4;">
                                    <?php esc_html_e('When OFF: only metadata (form ID, list of touched field names, timestamp). When ON: the actual values the visitor typed are stored alongside metadata. Leave OFF unless you need value-level analysis and have explicit consent.', 'formforge'); ?>
                                </p>
                            </div>
                            <?php if ($form && $form->id): ?>
                                <p style="margin:12px 0 0;font-size:14px;">
                                    <a href="<?php echo esc_url(admin_url('admin.php?page=formforge-analytics&form_id=' . (int) $form->id)); ?>" style="color:#3B82F6;text-decoration:none;">
                                        <span class="dashicons dashicons-chart-area" style="font-size:14px;width:14px;height:14px;vertical-align:text-bottom;"></span>
                                        <?php esc_html_e('View abandonment data', 'formforge'); ?> &rarr;
                                    </a>
                                </p>
                            <?php endif; ?>
                        </div>
                    </div>
                    <script>
                    (function(){
                        var head = document.getElementById('ff-abandon-toggle-head');
                        var body = document.getElementById('ff-abandon-body');
                        var chevron = document.getElementById('ff-abandon-chevron');
                        if (head) {
                            head.addEventListener('click', function(){
                                var open = body.style.display !== 'none';
                                body.style.display = open ? 'none' : 'block';
                                chevron.style.transform = open ? '' : 'rotate(180deg)';
                            });
                        }
                        var trackCb = document.getElementById('ff-track-abandonment');
                        var captureWrap = document.getElementById('ff-track-capture-wrap');
                        if (trackCb && captureWrap) {
                            trackCb.addEventListener('change', function(){
                                captureWrap.style.display = this.checked ? '' : 'none';
                                // When the master toggle goes OFF, force-clear the sub-toggle too,
                                // so a stale "capture values" flag doesn't survive disabling tracking.
                                if (!this.checked) {
                                    var captureCb = document.getElementById('ff-track-capture-values');
                                    if (captureCb) captureCb.checked = false;
                                }
                            });
                        }
                        jQuery(function(){
                        var saveBtn = document.getElementById('formforge-save');
                        if (saveBtn) {
                            saveBtn.addEventListener('click', function(){
                                var settingsInput = document.getElementById('formforge-settings-json');
                                var settings;
                                try { settings = JSON.parse(settingsInput.value); } catch(e) { settings = {}; }
                                // PHP json_encode of an empty assoc array becomes "[]", which
                                // round-trips into a JS Array. Stringifying an Array with only
                                // non-index props re-emits "[]" and the new keys are silently
                                // dropped. Coerce to plain object so the assignments below stick.
                                if (Array.isArray(settings)) settings = {};
                                var trk = document.getElementById('ff-track-abandonment');
                                var cap = document.getElementById('ff-track-capture-values');
                                settings.track_abandonment = trk ? trk.checked : false;
                                // capture_values must imply tracking ON, but we still record the bit
                                // exactly as the UI shows it so reload restores the state.
                                settings.track_abandonment_capture_values = cap ? cap.checked : false;
                                settingsInput.value = JSON.stringify(settings);
                            }, true); // capture phase
                        }
                        });
                    })();
                    </script>

                    <!-- Notification Settings Card -->
                    <?php
                    $notify_admin        = !empty($form->settings['notify_admin']);
                    $notify_email        = esc_attr($form->settings['notify_email'] ?? get_option('admin_email'));
                    $auto_reply          = !empty($form->settings['auto_reply']);
                    $auto_reply_field    = esc_attr($form->settings['auto_reply_field'] ?? '');
                    $auto_reply_subject  = esc_attr($form->settings['auto_reply_subject'] ?? __('Thank you for your submission', 'formforge'));
                    $auto_reply_message  = esc_textarea($form->settings['auto_reply_message'] ?? __('Thank you for contacting us. We have received your submission and will get back to you soon.', 'formforge'));
                    ?>
                    <div class="ff-settings-card" style="margin-bottom:12px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden;">
                        <div style="display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid #e2e8f0;background:#f8fafc;cursor:pointer;" id="ff-notif-toggle-head">
                            <span class="dashicons dashicons-email-alt" style="color:#3B82F6;font-size:16px;width:16px;height:16px;"></span>
                            <strong style="font-size:14px;color:#1e293b;"><?php esc_html_e('Email Notifications', 'formforge'); ?></strong>
                            <span class="dashicons dashicons-arrow-down-alt2" id="ff-notif-chevron" style="margin-left:auto;font-size:14px;width:14px;height:14px;color:#94a3b8;transition:transform 0.2s;"></span>
                        </div>
                        <div id="ff-notif-body" style="padding:16px;display:none;">
                            <label class="ff-toggle-label" style="display:flex;align-items:center;gap:8px;font-size:14px;margin-bottom:10px;cursor:pointer;">
                                <input type="checkbox" id="ff-notify-admin" name="notify_admin" <?php checked($notify_admin); ?>>
                                <span><?php esc_html_e('Notify admin on submission', 'formforge'); ?></span>
                            </label>
                            <div id="ff-notify-email-wrap" style="margin-bottom:12px;<?php echo esc_attr($notify_admin ? '' : 'display:none;'); ?>">
                                <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Notification email', 'formforge'); ?></label>
                                <input type="email" id="ff-notify-email" name="notify_email" value="<?php echo esc_attr($notify_email); ?>" placeholder="admin@example.com" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
                            </div>
                            <label class="ff-toggle-label" style="display:flex;align-items:center;gap:8px;font-size:14px;margin-bottom:10px;cursor:pointer;">
                                <input type="checkbox" id="ff-auto-reply" name="auto_reply" <?php checked($auto_reply); ?>>
                                <span><?php esc_html_e('Send auto-reply to submitter', 'formforge'); ?></span>
                            </label>
                            <div id="ff-auto-reply-wrap" style="<?php echo esc_attr($auto_reply ? '' : 'display:none;'); ?>padding-left:4px;">
                                <div style="margin-bottom:10px;">
                                    <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Email field (for auto-reply address)', 'formforge'); ?></label>
                                    <select id="ff-auto-reply-field" name="auto_reply_field" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
                                        <!-- populated by JS from form fields -->
                                    </select>
                                </div>
                                <div style="margin-bottom:10px;">
                                    <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Subject', 'formforge'); ?></label>
                                    <input type="text" id="ff-auto-reply-subject" name="auto_reply_subject" value="<?php echo esc_attr($auto_reply_subject); ?>" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
                                </div>
                                <div>
                                    <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Message', 'formforge'); ?></label>
                                    <textarea id="ff-auto-reply-message" name="auto_reply_message" rows="4" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;resize:vertical;"><?php echo esc_textarea($auto_reply_message); ?></textarea>
                                </div>
                            </div>
                        </div>
                    </div>
                    <script>
                    (function(){
                        // Collapsible notification panel
                        var head = document.getElementById('ff-notif-toggle-head');
                        var body = document.getElementById('ff-notif-body');
                        var chevron = document.getElementById('ff-notif-chevron');
                        if (head) {
                            head.addEventListener('click', function(){
                                var open = body.style.display !== 'none';
                                body.style.display = open ? 'none' : 'block';
                                chevron.style.transform = open ? '' : 'rotate(180deg)';
                            });
                        }

                        // Show/hide notify email when checkbox changes
                        var notifyAdminCb = document.getElementById('ff-notify-admin');
                        var notifyEmailWrap = document.getElementById('ff-notify-email-wrap');
                        if (notifyAdminCb) {
                            notifyAdminCb.addEventListener('change', function(){
                                notifyEmailWrap.style.display = this.checked ? '' : 'none';
                            });
                        }

                        // Show/hide auto-reply fields when checkbox changes
                        var autoReplyCb = document.getElementById('ff-auto-reply');
                        var autoReplyWrap = document.getElementById('ff-auto-reply-wrap');
                        if (autoReplyCb) {
                            autoReplyCb.addEventListener('change', function(){
                                autoReplyWrap.style.display = this.checked ? '' : 'none';
                            });
                        }

                        // Populate auto-reply field dropdown from form fields
                        function populateEmailFields(fields) {
                            var select = document.getElementById('ff-auto-reply-field');
                            if (!select) return;
                            var current = select.value || <?php echo wp_json_encode($auto_reply_field, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT); ?>;
                            select.innerHTML = '<option value=""><?php echo esc_js(__('Select email field...', 'formforge')); ?></option>';
                            fields.forEach(function(f){
                                if (f.type === 'email' || f.type === 'text') {
                                    var opt = document.createElement('option');
                                    opt.value = f.id;
                                    opt.textContent = f.label || f.id;
                                    if (f.id === current) opt.selected = true;
                                    select.appendChild(opt);
                                }
                            });
                        }

                        // Initial population
                        var fieldsJson = document.getElementById('formforge-fields-json');
                        if (fieldsJson) {
                            try { populateEmailFields(JSON.parse(fieldsJson.value)); } catch(e) {}
                            // Re-populate when fields change
                            var observer = new MutationObserver(function(){
                                try { populateEmailFields(JSON.parse(fieldsJson.value)); } catch(e) {}
                            });
                            observer.observe(fieldsJson, { attributes: true, attributeFilter: ['value'] });
                        }

                        // Hook into save: merge notification settings into settings JSON
                        jQuery(function(){  // FF-rl: defer attach so #formforge-save (rendered later) is found
                        var saveBtn = document.getElementById('formforge-save');
                        if (saveBtn) {
                            saveBtn.addEventListener('click', function(){
                                var settingsInput = document.getElementById('formforge-settings-json');
                                var settings;
                                try { settings = JSON.parse(settingsInput.value); } catch(e) { settings = {}; }

                                settings.notify_admin = document.getElementById('ff-notify-admin') ? document.getElementById('ff-notify-admin').checked : false;
                                settings.notify_email = document.getElementById('ff-notify-email') ? document.getElementById('ff-notify-email').value : '';
                                settings.auto_reply   = document.getElementById('ff-auto-reply') ? document.getElementById('ff-auto-reply').checked : false;
                                settings.auto_reply_field   = document.getElementById('ff-auto-reply-field') ? document.getElementById('ff-auto-reply-field').value : '';
                                settings.auto_reply_subject = document.getElementById('ff-auto-reply-subject') ? document.getElementById('ff-auto-reply-subject').value : '';
                                settings.auto_reply_message = document.getElementById('ff-auto-reply-message') ? document.getElementById('ff-auto-reply-message').value : '';

                                settingsInput.value = JSON.stringify(settings);
                            }, true); // capture phase so it runs before the save handler
                        }
                        });
                    })();
                    </script>

                    <!-- Slack Notification (per-form) -->
                    <?php
                    $slack_url = esc_attr($form->settings['slack_webhook_url'] ?? '');
                    ?>
                    <div class="ff-settings-card" style="margin-bottom:12px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden;">
                        <div style="display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid #e2e8f0;background:#f8fafc;cursor:pointer;" id="ff-slack-toggle-head">
                            <span class="dashicons dashicons-format-chat" style="color:#3B82F6;font-size:16px;width:16px;height:16px;"></span>
                            <strong style="font-size:14px;color:#1e293b;"><?php esc_html_e('Slack', 'formforge'); ?></strong>
                            <?php if (!FORMFORGE_License::instance()->can('slack')): ?>
                                <span style="font-size:14px;color:#94a3b8;background:#f1f5f9;padding:1px 6px;border-radius:4px;margin-left:4px;">PRO</span>
                            <?php endif; ?>
                            <span class="dashicons dashicons-arrow-down-alt2" id="ff-slack-chevron" style="margin-left:auto;font-size:14px;width:14px;height:14px;color:#94a3b8;transition:transform 0.2s;"></span>
                        </div>
                        <div id="ff-slack-body" style="padding:16px;display:none;">
                            <div style="margin-bottom:10px;">
                                <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Webhook URL (overrides global)', 'formforge'); ?></label>
                                <input type="url" id="ff-slack-webhook-url" value="<?php echo esc_attr($slack_url); ?>" placeholder="https://hooks.slack.com/services/..." style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
                            </div>
                            <p style="margin:0;font-size:14px;color:#94a3b8;"><?php esc_html_e('Leave empty to use global Slack webhook from Settings.', 'formforge'); ?></p>
                        </div>
                    </div>
                    <script>
                    (function(){
                        var head = document.getElementById('ff-slack-toggle-head');
                        var body = document.getElementById('ff-slack-body');
                        var chevron = document.getElementById('ff-slack-chevron');
                        if (head) {
                            head.addEventListener('click', function(){
                                var open = body.style.display !== 'none';
                                body.style.display = open ? 'none' : 'block';
                                chevron.style.transform = open ? '' : 'rotate(180deg)';
                            });
                        }
                        jQuery(function(){  // FF-rl: defer attach so #formforge-save (rendered later) is found
                        var saveBtn = document.getElementById('formforge-save');
                        if (saveBtn) {
                            saveBtn.addEventListener('click', function(){
                                var settingsInput = document.getElementById('formforge-settings-json');
                                var settings;
                                try { settings = JSON.parse(settingsInput.value); } catch(e) { settings = {}; }
                                settings.slack_webhook_url = document.getElementById('ff-slack-webhook-url') ? document.getElementById('ff-slack-webhook-url').value : '';
                                settingsInput.value = JSON.stringify(settings);
                            }, true);
                        }
                        });
                    })();
                    </script>

                    <!-- Discord Notification (per-form) -->
                    <?php
                    $discord_url = esc_attr($form->settings['discord_webhook_url'] ?? '');
                    ?>
                    <div class="ff-settings-card" style="margin-bottom:12px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden;">
                        <div style="display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid #e2e8f0;background:#f8fafc;cursor:pointer;" id="ff-discord-toggle-head">
                            <span class="dashicons dashicons-format-status" style="color:#3B82F6;font-size:16px;width:16px;height:16px;"></span>
                            <strong style="font-size:14px;color:#1e293b;"><?php esc_html_e('Discord', 'formforge'); ?></strong>
                            <?php if (!FORMFORGE_License::instance()->can('discord')): ?>
                                <span style="font-size:14px;color:#94a3b8;background:#f1f5f9;padding:1px 6px;border-radius:4px;margin-left:4px;">PRO</span>
                            <?php endif; ?>
                            <span class="dashicons dashicons-arrow-down-alt2" id="ff-discord-chevron" style="margin-left:auto;font-size:14px;width:14px;height:14px;color:#94a3b8;transition:transform 0.2s;"></span>
                        </div>
                        <div id="ff-discord-body" style="padding:16px;display:none;">
                            <div style="margin-bottom:10px;">
                                <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Webhook URL (overrides global)', 'formforge'); ?></label>
                                <input type="url" id="ff-discord-webhook-url" value="<?php echo esc_attr($discord_url); ?>" placeholder="https://discord.com/api/webhooks/..." style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
                            </div>
                            <p style="margin:0;font-size:14px;color:#94a3b8;"><?php esc_html_e('Leave empty to use global Discord webhook from Settings.', 'formforge'); ?></p>
                        </div>
                    </div>
                    <script>
                    (function(){
                        var head = document.getElementById('ff-discord-toggle-head');
                        var body = document.getElementById('ff-discord-body');
                        var chevron = document.getElementById('ff-discord-chevron');
                        if (head) {
                            head.addEventListener('click', function(){
                                var open = body.style.display !== 'none';
                                body.style.display = open ? 'none' : 'block';
                                chevron.style.transform = open ? '' : 'rotate(180deg)';
                            });
                        }
                        jQuery(function(){  // FF-rl: defer attach so #formforge-save (rendered later) is found
                        var saveBtn = document.getElementById('formforge-save');
                        if (saveBtn) {
                            saveBtn.addEventListener('click', function(){
                                var settingsInput = document.getElementById('formforge-settings-json');
                                var settings;
                                try { settings = JSON.parse(settingsInput.value); } catch(e) { settings = {}; }
                                settings.discord_webhook_url = document.getElementById('ff-discord-webhook-url') ? document.getElementById('ff-discord-webhook-url').value : '';
                                settingsInput.value = JSON.stringify(settings);
                            }, true);
                        }
                        });
                    })();
                    </script>

                    <!-- Telegram (per-form) -->
                    <?php
                    $tg_enabled = !empty($form->settings['telegram_enabled']);
                    $tg_chat_id = esc_attr($form->settings['telegram_chat_id'] ?? '');
                    ?>
                    <div class="ff-settings-card" style="margin-bottom:12px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden;">
                        <div style="display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid #e2e8f0;background:#f8fafc;cursor:pointer;" id="ff-telegram-toggle-head">
                            <span class="dashicons dashicons-format-chat" style="color:#3B82F6;font-size:16px;width:16px;height:16px;"></span>
                            <strong style="font-size:14px;color:#1e293b;"><?php esc_html_e('Telegram', 'formforge'); ?></strong>
                            <?php if (!FORMFORGE_License::instance()->can('telegram')): ?>
                                <span style="font-size:14px;color:#94a3b8;background:#f1f5f9;padding:1px 6px;border-radius:4px;margin-left:4px;">PRO</span>
                            <?php endif; ?>
                            <span class="dashicons dashicons-arrow-down-alt2" id="ff-telegram-chevron" style="margin-left:auto;font-size:14px;width:14px;height:14px;color:#94a3b8;transition:transform 0.2s;"></span>
                        </div>
                        <div id="ff-telegram-body" style="padding:16px;display:none;">
                            <label class="ff-toggle-label" style="display:flex;align-items:center;gap:8px;font-size:14px;margin-bottom:10px;cursor:pointer;">
                                <input type="checkbox" id="ff-telegram-enabled" <?php checked($tg_enabled); ?>>
                                <span><?php esc_html_e('Send submissions to Telegram', 'formforge'); ?></span>
                            </label>
                            <div style="margin-bottom:10px;">
                                <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Chat ID (overrides global)', 'formforge'); ?></label>
                                <input type="text" id="ff-telegram-chat-id" value="<?php echo esc_attr($tg_chat_id); ?>" placeholder="123456789 or -1002345678901" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;font-family:monospace;">
                            </div>
                            <p style="margin:0 0 6px;font-size:12px;color:#94a3b8;"><?php esc_html_e('Global Settings → Telegram stores the default destination. This toggle decides whether this form sends notifications.', 'formforge'); ?></p>
                            <p style="margin:0;font-size:12px;color:#94a3b8;"><?php esc_html_e('Leave Chat ID empty to use the global destination. Enter a Chat ID here only when this form should override it. Bot is @ForgeAPINotifyBot — start a chat with the bot or add it to a group, then send /start to obtain the Chat ID.', 'formforge'); ?></p>
                        </div>
                    </div>
                    <script>
                    (function(){
                        var head = document.getElementById('ff-telegram-toggle-head');
                        var body = document.getElementById('ff-telegram-body');
                        var chevron = document.getElementById('ff-telegram-chevron');
                        if (head) {
                            head.addEventListener('click', function(){
                                var open = body.style.display !== 'none';
                                body.style.display = open ? 'none' : 'block';
                                chevron.style.transform = open ? '' : 'rotate(180deg)';
                            });
                        }
                        jQuery(function(){
                        var saveBtn = document.getElementById('formforge-save');
                        if (saveBtn) {
                            saveBtn.addEventListener('click', function(){
                                var settingsInput = document.getElementById('formforge-settings-json');
                                var settings;
                                try { settings = JSON.parse(settingsInput.value); } catch(e) { settings = {}; }
                                var en = document.getElementById('ff-telegram-enabled');
                                var cid = document.getElementById('ff-telegram-chat-id');
                                if (en) settings.telegram_enabled = en.checked;
                                if (cid) settings.telegram_chat_id = cid.value;
                                settingsInput.value = JSON.stringify(settings);
                            }, true);
                        }
                        });
                    })();
                    </script>

                    <!-- WhatsApp (per-form, FC-44) -->
                    <?php $wa_enabled = !empty($form->settings['whatsapp_enabled']); ?>
                    <div class="ff-settings-card" style="margin-bottom:12px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden;">
	                        <div style="display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid #e2e8f0;background:#f8fafc;cursor:pointer;" id="ff-whatsapp-toggle-head">
	                            <span class="dashicons dashicons-whatsapp" style="color:#3B82F6;font-size:16px;width:16px;height:16px;"></span>
                            <strong style="font-size:14px;color:#1e293b;"><?php esc_html_e('WhatsApp', 'formforge'); ?></strong>
                            <?php if (!FORMFORGE_License::instance()->can('whatsapp')): ?>
                                <span style="font-size:14px;color:#94a3b8;background:#f1f5f9;padding:1px 6px;border-radius:4px;margin-left:4px;">PRO</span>
                            <?php endif; ?>
                            <span class="dashicons dashicons-arrow-down-alt2" id="ff-whatsapp-chevron" style="margin-left:auto;font-size:14px;width:14px;height:14px;color:#94a3b8;transition:transform 0.2s;"></span>
                        </div>
                        <div id="ff-whatsapp-body" style="padding:16px;display:none;">
                            <label class="ff-toggle-label" style="display:flex;align-items:center;gap:8px;font-size:14px;margin-bottom:10px;cursor:pointer;">
                                <input type="checkbox" id="ff-whatsapp-enabled" <?php checked($wa_enabled); ?>>
                                <span><?php esc_html_e('Send submissions to WhatsApp', 'formforge'); ?></span>
                            </label>
                            <p style="margin:0 0 6px;font-size:12px;color:#94a3b8;"><?php esc_html_e('Connection is configured globally in Form Forge → Settings → WhatsApp. This toggle decides whether THIS form sends notifications.', 'formforge'); ?></p>
                            <p style="margin:0;font-size:12px;color:#94a3b8;"><?php esc_html_e('WhatsApp sends a short Meta-approved alert only: form name, submitter, and time. The full submission remains in Form Forge → Submissions.', 'formforge'); ?></p>
                        </div>
                    </div>
                    <script>
                    (function(){
                        var head = document.getElementById('ff-whatsapp-toggle-head');
                        var body = document.getElementById('ff-whatsapp-body');
                        var chevron = document.getElementById('ff-whatsapp-chevron');
                        if (head) {
                            head.addEventListener('click', function(){
                                var open = body.style.display !== 'none';
                                body.style.display = open ? 'none' : 'block';
                                chevron.style.transform = open ? '' : 'rotate(180deg)';
                            });
                        }
                        jQuery(function(){
                        var saveBtn = document.getElementById('formforge-save');
                        if (saveBtn) {
                            saveBtn.addEventListener('click', function(){
                                var settingsInput = document.getElementById('formforge-settings-json');
                                var settings;
                                try { settings = JSON.parse(settingsInput.value); } catch(e) { settings = {}; }
                                var en = document.getElementById('ff-whatsapp-enabled');
                                if (en) settings.whatsapp_enabled = en.checked;
                                settingsInput.value = JSON.stringify(settings);
                            }, true);
                        }
                        });
                    })();
                    </script>

                    <!-- Webhook (per-form) -->
                    <?php
                    $webhook_url = esc_attr($form->settings['webhook_url'] ?? '');
                    $webhook_headers = esc_textarea($form->settings['webhook_headers'] ?? '');
                    ?>
                    <div class="ff-settings-card" style="margin-bottom:12px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden;">
                        <div style="display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid #e2e8f0;background:#f8fafc;cursor:pointer;" id="ff-webhook-toggle-head">
                            <span class="dashicons dashicons-rest-api" style="color:#3B82F6;font-size:16px;width:16px;height:16px;"></span>
                            <strong style="font-size:14px;color:#1e293b;"><?php esc_html_e('Webhook', 'formforge'); ?></strong>
                            <?php if (!FORMFORGE_License::instance()->can('webhooks')): ?>
                                <span style="font-size:14px;color:#94a3b8;background:#f1f5f9;padding:1px 6px;border-radius:4px;margin-left:4px;">PRO</span>
                            <?php endif; ?>
                            <span class="dashicons dashicons-arrow-down-alt2" id="ff-webhook-chevron" style="margin-left:auto;font-size:14px;width:14px;height:14px;color:#94a3b8;transition:transform 0.2s;"></span>
                        </div>
                        <div id="ff-webhook-body" style="padding:16px;display:none;">
                            <div style="margin-bottom:10px;">
                                <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Webhook URL', 'formforge'); ?></label>
                                <input type="url" id="ff-webhook-url" value="<?php echo esc_attr($webhook_url); ?>" placeholder="https://hooks.zapier.com/hooks/catch/..." style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
                            </div>
                            <div>
                                <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Custom headers (one per line, format: Name: value)', 'formforge'); ?></label>
                                <textarea id="ff-webhook-headers" rows="3" placeholder="Authorization: Bearer xyz" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;font-family:monospace;resize:vertical;"><?php echo esc_textarea($webhook_headers); ?></textarea>
                            </div>
                            <p style="margin:8px 0 0;font-size:12px;color:#94a3b8;"><?php esc_html_e('Form Forge will POST a JSON payload (form_id, form_title, submission_id, fields) on every successful submission. SSRF protection blocks private IPs. Failed deliveries are retried with exponential backoff.', 'formforge'); ?></p>
                        </div>
                    </div>
                    <script>
                    (function(){
                        var head = document.getElementById('ff-webhook-toggle-head');
                        var body = document.getElementById('ff-webhook-body');
                        var chevron = document.getElementById('ff-webhook-chevron');
                        if (head) {
                            head.addEventListener('click', function(){
                                var open = body.style.display !== 'none';
                                body.style.display = open ? 'none' : 'block';
                                chevron.style.transform = open ? '' : 'rotate(180deg)';
                            });
                        }
                        jQuery(function(){
                        var saveBtn = document.getElementById('formforge-save');
                        if (saveBtn) {
                            saveBtn.addEventListener('click', function(){
                                var settingsInput = document.getElementById('formforge-settings-json');
                                var settings;
                                try { settings = JSON.parse(settingsInput.value); } catch(e) { settings = {}; }
                                var url = document.getElementById('ff-webhook-url');
                                var hdr = document.getElementById('ff-webhook-headers');
                                if (url) settings.webhook_url     = url.value;
                                if (hdr) settings.webhook_headers = hdr.value;
                                settingsInput.value = JSON.stringify(settings);
                            }, true);
                        }
                        });
                    })();
                    </script>

                    <!-- Mailchimp (per-form) -->
                    <?php
                    $mc_enabled = !empty($form->settings['mailchimp_enabled']);
                    $mc_list_id = esc_attr($form->settings['mailchimp_list_id'] ?? '');
                    $mc_email_field = esc_attr($form->settings['mailchimp_email_field'] ?? '');
                    ?>
                    <div class="ff-settings-card" style="margin-bottom:12px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden;">
                        <div style="display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid #e2e8f0;background:#f8fafc;cursor:pointer;" id="ff-mc-toggle-head">
                            <span class="dashicons dashicons-email" style="color:#3B82F6;font-size:16px;width:16px;height:16px;"></span>
                            <strong style="font-size:14px;color:#1e293b;"><?php esc_html_e('Mailchimp', 'formforge'); ?></strong>
                            <?php if (!FORMFORGE_License::instance()->can('mailchimp')): ?>
                                <span style="font-size:14px;color:#94a3b8;background:#f1f5f9;padding:1px 6px;border-radius:4px;margin-left:4px;">PRO</span>
                            <?php endif; ?>
                            <span class="dashicons dashicons-arrow-down-alt2" id="ff-mc-chevron" style="margin-left:auto;font-size:14px;width:14px;height:14px;color:#94a3b8;transition:transform 0.2s;"></span>
                        </div>
                        <div id="ff-mc-body" style="padding:16px;display:none;">
                            <label class="ff-toggle-label" style="display:flex;align-items:center;gap:8px;font-size:14px;margin-bottom:12px;cursor:pointer;">
                                <input type="checkbox" id="ff-mc-enabled" <?php checked($mc_enabled); ?>>
                                <span><?php esc_html_e('Subscribe to Mailchimp list on submission', 'formforge'); ?></span>
                            </label>
                            <div id="ff-mc-fields" style="<?php echo esc_attr($mc_enabled ? '' : 'display:none;'); ?>">
                                <div style="margin-bottom:10px;">
                                    <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('List ID', 'formforge'); ?></label>
                                    <input type="text" id="ff-mc-list-id" value="<?php echo esc_attr($mc_list_id); ?>" placeholder="abc123def4" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
                                    <p style="margin:4px 0 0;font-size:14px;color:#94a3b8;"><?php esc_html_e('Find this in Mailchimp: Audience &rarr; Settings &rarr; Audience ID', 'formforge'); ?></p>
                                </div>
                                <div>
                                    <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Email Field', 'formforge'); ?></label>
                                    <select id="ff-mc-email-field" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
                                        <option value=""><?php esc_html_e('Auto-detect email field', 'formforge'); ?></option>
                                    </select>
                                </div>
                            </div>
                        </div>
                    </div>
                    <script>
                    (function(){
                        var head = document.getElementById('ff-mc-toggle-head');
                        var body = document.getElementById('ff-mc-body');
                        var chevron = document.getElementById('ff-mc-chevron');
                        if (head) {
                            head.addEventListener('click', function(){
                                var open = body.style.display !== 'none';
                                body.style.display = open ? 'none' : 'block';
                                chevron.style.transform = open ? '' : 'rotate(180deg)';
                            });
                        }
                        var cb = document.getElementById('ff-mc-enabled');
                        var fields = document.getElementById('ff-mc-fields');
                        if (cb) {
                            cb.addEventListener('change', function(){
                                fields.style.display = this.checked ? '' : 'none';
                            });
                        }

	                        // Populate email field dropdown
	                        function currentFormFields() {
	                            if (window.formforgeGetFields) {
	                                return window.formforgeGetFields();
	                            }
	                            var fieldsJson = document.getElementById('formforge-fields-json');
	                            if (!fieldsJson) return [];
	                            try { return JSON.parse(fieldsJson.value || '[]'); } catch(e) { return []; }
	                        }
	                        function populateMcEmailFields(formFields) {
	                            var select = document.getElementById('ff-mc-email-field');
	                            if (!select) return;
	                            var current = select.value || <?php echo wp_json_encode($mc_email_field, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT); ?>;
	                            select.innerHTML = '<option value=""><?php echo esc_js(__('Auto-detect email field', 'formforge')); ?></option>';
	                            (formFields || []).forEach(function(f){
	                                if (f.type === 'email') {
	                                    var opt = document.createElement('option');
	                                    opt.value = f.id;
	                                    opt.textContent = f.label || f.id;
	                                    if (f.id === current) opt.selected = true;
	                                    select.appendChild(opt);
	                                }
	                            });
	                        }
	                        populateMcEmailFields(currentFormFields());
	                        if (window.jQuery) {
	                            jQuery(document).on('formforge:fields-changed', function(e, nextFields){
	                                populateMcEmailFields(nextFields || currentFormFields());
	                            });
	                        }

                        jQuery(function(){  // FF-rl: defer attach so #formforge-save (rendered later) is found
                        var saveBtn = document.getElementById('formforge-save');
                        if (saveBtn) {
                            saveBtn.addEventListener('click', function(){
                                var settingsInput = document.getElementById('formforge-settings-json');
                                var settings;
                                try { settings = JSON.parse(settingsInput.value); } catch(e) { settings = {}; }
                                settings.mailchimp_enabled = document.getElementById('ff-mc-enabled') ? document.getElementById('ff-mc-enabled').checked : false;
                                settings.mailchimp_list_id = document.getElementById('ff-mc-list-id') ? document.getElementById('ff-mc-list-id').value : '';
                                settings.mailchimp_email_field = document.getElementById('ff-mc-email-field') ? document.getElementById('ff-mc-email-field').value : '';
                                settingsInput.value = JSON.stringify(settings);
                            }, true);
                        }
                        });
                    })();
                    </script>

                    <!-- Google Sheets (per-form) -->
                    <?php $gs_enabled = !empty($form->settings['gsheets_enabled']); ?>
                    <div class="ff-settings-card" style="margin-bottom:12px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden;">
                        <div style="display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid #e2e8f0;background:#f8fafc;cursor:pointer;" id="ff-gs-toggle-head">
                            <span class="dashicons dashicons-media-spreadsheet" style="color:#3B82F6;font-size:16px;width:16px;height:16px;"></span>
                            <strong style="font-size:14px;color:#1e293b;"><?php esc_html_e('Google Sheets', 'formforge'); ?></strong>
                            <?php if (!FORMFORGE_License::instance()->can('google_sheets')): ?>
                                <span style="font-size:14px;color:#94a3b8;background:#f1f5f9;padding:1px 6px;border-radius:4px;margin-left:4px;">PRO</span>
                            <?php endif; ?>
                            <span class="dashicons dashicons-arrow-down-alt2" id="ff-gs-chevron" style="margin-left:auto;font-size:14px;width:14px;height:14px;color:#94a3b8;transition:transform 0.2s;"></span>
                        </div>
                        <div id="ff-gs-body" style="padding:16px;display:none;">
                            <label class="ff-toggle-label" style="display:flex;align-items:center;gap:8px;font-size:14px;margin-bottom:12px;cursor:pointer;">
                                <input type="checkbox" id="ff-gs-enabled" <?php checked($gs_enabled); ?>>
                                <span><?php esc_html_e('Append submission row to Google Sheet', 'formforge'); ?></span>
                            </label>
                            <div id="ff-gs-fields" style="<?php echo esc_attr($gs_enabled ? '' : 'display:none;'); ?>">
                                <div id="ff-gs-picker-block" style="margin-bottom:10px;">
                                    <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Spreadsheet', 'formforge'); ?></label>
                                    <!-- Hidden fields store the selection. We keep the text input class so the save handler below picks it up; it's just not rendered as a plain box anymore. -->
                                    <input type="hidden" id="ff-gs-spreadsheet-id" value="<?php echo esc_attr($form->settings['gsheets_spreadsheet_id'] ?? ''); ?>">
                                    <input type="hidden" id="ff-gs-sheet-name" value="<?php echo esc_attr($form->settings['gsheets_sheet_name'] ?? 'Sheet1'); ?>">
                                    <input type="hidden" id="ff-gs-spreadsheet-title" value="<?php echo esc_attr($form->settings['gsheets_spreadsheet_title'] ?? ''); ?>">

                                    <div id="ff-gs-current" style="<?php echo esc_attr(!empty($form->settings['gsheets_spreadsheet_id']) ? '' : 'display:none;'); ?>padding:10px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;margin-bottom:8px;">
                                        <div style="display:flex;align-items:center;gap:8px;">
                                            <span class="dashicons dashicons-media-spreadsheet" style="color:#0f9d58;"></span>
                                            <strong id="ff-gs-current-title" style="font-size:14px;color:#1e293b;"><?php echo esc_html($form->settings['gsheets_spreadsheet_title'] ?? '—'); ?></strong>
                                            <a id="ff-gs-current-link" href="<?php echo !empty($form->settings['gsheets_spreadsheet_id']) ? esc_url('https://docs.google.com/spreadsheets/d/' . $form->settings['gsheets_spreadsheet_id'] . '/edit') : '#'; ?>" target="_blank" rel="noopener" style="font-size:14px;color:#3B82F6;margin-left:auto;">
                                                <?php esc_html_e('Open', 'formforge'); ?> ↗
                                            </a>
                                        </div>
                                        <div style="font-size:14px;color:#94a3b8;margin-top:4px;"><?php esc_html_e('Tab:', 'formforge'); ?> <span id="ff-gs-current-tab"><?php echo esc_html($form->settings['gsheets_sheet_name'] ?? 'Sheet1'); ?></span></div>
                                    </div>

                                    <div style="display:flex;gap:6px;flex-wrap:wrap;">
                                        <button type="button" class="button button-primary" id="ff-gs-pick">
                                            <span class="dashicons dashicons-search" style="margin-top:3px;"></span>
                                            <?php esc_html_e('Choose spreadsheet', 'formforge'); ?>
                                        </button>
                                        <button type="button" class="button" id="ff-gs-create">
                                            <span class="dashicons dashicons-plus-alt2" style="margin-top:3px;"></span>
                                            <?php esc_html_e('Create new', 'formforge'); ?>
                                        </button>
                                        <button type="button" class="button" id="ff-gs-clear" style="color:#b91c1c !important;border-color:#fecaca !important;background:#fff !important;<?php echo esc_attr(empty($form->settings['gsheets_spreadsheet_id']) ? 'display:none;' : ''); ?>">
                                            <span class="dashicons dashicons-no-alt" style="margin-top:3px;"></span>
                                            <?php esc_html_e('Clear', 'formforge'); ?>
                                        </button>
                                    </div>
                                    <p style="margin:8px 0 0;font-size:14px;color:#94a3b8;">
                                        <?php esc_html_e('Pick a Google Sheet via the Picker — the plugin only gets access to this specific file (drive.file scope).', 'formforge'); ?>
                                    </p>
                                </div>
                                <p style="margin:8px 0 0;font-size:14px;color:#94a3b8;"><?php esc_html_e('Connect Google in Settings first. Each submission adds a row: [ID, Date, Field1, Field2, ...].', 'formforge'); ?></p>
                            </div>
                        </div>
                    </div>
                    <script>
                    (function(){
                        var head = document.getElementById('ff-gs-toggle-head');
                        var body = document.getElementById('ff-gs-body');
                        var chevron = document.getElementById('ff-gs-chevron');
                        if (head) head.addEventListener('click', function(){ var o = body.style.display !== 'none'; body.style.display = o ? 'none' : 'block'; chevron.style.transform = o ? '' : 'rotate(180deg)'; });
                        var cb = document.getElementById('ff-gs-enabled');
                        if (cb) cb.addEventListener('change', function(){ document.getElementById('ff-gs-fields').style.display = this.checked ? '' : 'none'; });
 // when the editor picks a spreadsheet via the
                        // Google Picker, auto-tick the enabled checkbox + open
                        // the fields block. Forgetting to tick the checkbox
                        // was the silent reason rows weren't appearing in the
                        // chosen sheet.
                        var idInput = document.getElementById('ff-gs-spreadsheet-id');
                        if (idInput) {
                            new MutationObserver(function(){
                                if (idInput.value && cb && !cb.checked) {
                                    cb.checked = true;
                                    cb.dispatchEvent(new Event('change'));
                                }
                            }).observe(idInput, { attributes: true, attributeFilter: ['value'] });
                            // Also catch programmatic .value = '...' assignments by polling on
                            // a custom 'ff:picker:selected' event fired from google-picker.js.
                            document.addEventListener('ff:picker:selected', function(){
                                if (idInput.value && cb && !cb.checked) {
                                    cb.checked = true;
                                    cb.dispatchEvent(new Event('change'));
                                }
                            });
                        }
                        jQuery(function(){  // FF-rl: defer attach so #formforge-save (rendered later) is found
                        var saveBtn = document.getElementById('formforge-save');
                        if (saveBtn) {
                            saveBtn.addEventListener('click', function(){
                                var si = document.getElementById('formforge-settings-json');
                                var s; try { s = JSON.parse(si.value); } catch(e) { s = {}; }
 // at save time, if a spreadsheet is selected, also flip
                                // gsheets_enabled true. Auto-correct in case the user pasted
                                // an ID without ticking, or the picker callback didn't fire.
                                var spreadsheetId = document.getElementById('ff-gs-spreadsheet-id') ? document.getElementById('ff-gs-spreadsheet-id').value : '';
                                var enabledRaw = document.getElementById('ff-gs-enabled') ? document.getElementById('ff-gs-enabled').checked : false;
                                s.gsheets_enabled = enabledRaw || !!spreadsheetId;
                                s.gsheets_spreadsheet_id = spreadsheetId;
                                s.gsheets_sheet_name = document.getElementById('ff-gs-sheet-name') ? document.getElementById('ff-gs-sheet-name').value : 'Sheet1';
                                s.gsheets_spreadsheet_title = document.getElementById('ff-gs-spreadsheet-title') ? document.getElementById('ff-gs-spreadsheet-title').value : '';
                                si.value = JSON.stringify(s);
                            }, true);
                        }
                        });
                    })();
                    </script>

                    <!-- Google Calendar (per-form) -->
                    <?php $gc_enabled = !empty($form->settings['calendar_enabled']); ?>
                    <div class="ff-settings-card" style="margin-bottom:12px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden;">
                        <div style="display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid #e2e8f0;background:#f8fafc;cursor:pointer;" id="ff-gc-toggle-head">
                            <span class="dashicons dashicons-calendar-alt" style="color:#3B82F6;font-size:16px;width:16px;height:16px;"></span>
                            <strong style="font-size:14px;color:#1e293b;"><?php esc_html_e('Google Calendar', 'formforge'); ?></strong>
                            <?php if (!FORMFORGE_License::instance()->can('google_calendar')): ?>
                                <span style="font-size:14px;color:#94a3b8;background:#f1f5f9;padding:1px 6px;border-radius:4px;margin-left:4px;">PRO</span>
                            <?php endif; ?>
                            <span class="dashicons dashicons-arrow-down-alt2" id="ff-gc-chevron" style="margin-left:auto;font-size:14px;width:14px;height:14px;color:#94a3b8;transition:transform 0.2s;"></span>
                        </div>
                        <div id="ff-gc-body" style="padding:16px;display:none;">
                            <label class="ff-toggle-label" style="display:flex;align-items:center;gap:8px;font-size:14px;margin-bottom:12px;cursor:pointer;">
                                <input type="checkbox" id="ff-gc-enabled" <?php checked($gc_enabled); ?>>
                                <span><?php esc_html_e('Create calendar event on submission', 'formforge'); ?></span>
                            </label>
                            <div id="ff-gc-fields" style="<?php echo esc_attr($gc_enabled ? '' : 'display:none;'); ?>">
                                <div style="margin-bottom:10px;">
                                    <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Calendar', 'formforge'); ?></label>
                                    <select id="ff-gc-calendar-id" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;background:#fff;">
                                        <option value=""><?php esc_html_e('— select a calendar —', 'formforge'); ?></option>
                                        <?php if (!empty($form->settings['calendar_id'])): ?>
                                            <option value="<?php echo esc_attr($form->settings['calendar_id']); ?>" selected>
                                                <?php echo esc_html($form->settings['calendar_name'] ?? $form->settings['calendar_id']); ?>
                                            </option>
                                        <?php endif; ?>
                                    </select>
                                    <input type="hidden" id="ff-gc-calendar-name" value="<?php echo esc_attr($form->settings['calendar_name'] ?? ''); ?>">
                                    <p style="margin:4px 0 0;font-size:14px;color:#94a3b8;">
                                        <button type="button" class="button-link" id="ff-gc-refresh-calendars" style="color:#3B82F6;padding:0;">
                                            <?php esc_html_e('Load my calendars', 'formforge'); ?>
                                        </button>
                                    </p>
                                </div>
                                <p style="margin:0 0 10px;padding:8px 10px;background:#EFF6FF;border:1px solid #DBEAFE;border-radius:6px;font-size:13px;color:#1e40af;">
                                    <?php esc_html_e('Working hours, slot duration and available days are configured on the Calendar Picker field itself (Edit field → Availability). With Google connected, busy slots are filtered on top of those rules.', 'formforge'); ?>
                                </p>
                                <!-- Hidden inputs preserve any legacy form-level values for back-compat. -->
                                <input type="hidden" id="ff-gc-work-start"    value="<?php echo esc_attr($form->settings['calendar_work_start']    ?? ''); ?>">
                                <input type="hidden" id="ff-gc-work-end"      value="<?php echo esc_attr($form->settings['calendar_work_end']      ?? ''); ?>">
                                <input type="hidden" id="ff-gc-slot-duration" value="<?php echo esc_attr($form->settings['calendar_slot_duration'] ?? ''); ?>">
                                <div style="margin-bottom:10px;">
                                    <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Event title template', 'formforge'); ?></label>
                                    <input type="text" id="ff-gc-event-title" value="<?php echo esc_attr($form->settings['calendar_event_title'] ?? 'Booking: {name}'); ?>" placeholder="Booking: {name}" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
                                    <p style="margin:4px 0 0;font-size:14px;color:#94a3b8;"><?php esc_html_e('Use {fieldlabel} to insert submitted values. Example: "Call with {name} ({email})"', 'formforge'); ?></p>
                                </div>
                                <p style="margin:0;font-size:14px;color:#94a3b8;">
                                    <?php esc_html_e('Requires a calendar-picker field in the form (PRO). Connect Google in Settings first.', 'formforge'); ?>
                                </p>
                            </div>
                        </div>
                    </div>
                    <script>
                    (function(){
                        var head = document.getElementById('ff-gc-toggle-head');
                        var body = document.getElementById('ff-gc-body');
                        var chevron = document.getElementById('ff-gc-chevron');
                        if (head) head.addEventListener('click', function(){ var o = body.style.display !== 'none'; body.style.display = o ? 'none' : 'block'; chevron.style.transform = o ? '' : 'rotate(180deg)'; });
                        var cb = document.getElementById('ff-gc-enabled');
                        if (cb) cb.addEventListener('change', function(){ document.getElementById('ff-gc-fields').style.display = this.checked ? '' : 'none'; });

                        var refresh = document.getElementById('ff-gc-refresh-calendars');
                        var select  = document.getElementById('ff-gc-calendar-id');
                        var nameInput = document.getElementById('ff-gc-calendar-name');
                        if (refresh && select) {
                            refresh.addEventListener('click', function(){
                                refresh.textContent = '<?php echo esc_js(__('Loading…', 'formforge')); ?>';
                                var fd = new FormData();
                                fd.append('action', 'formforge_calendar_list');
                                fd.append('nonce', formforgeAdmin.nonce);
                                fetch(formforgeAdmin.ajaxUrl, { method: 'POST', body: fd, credentials: 'same-origin' })
                                    .then(function(r){ return r.json(); })
                                    .then(function(r){
                                        refresh.textContent = '<?php echo esc_js(__('Load my calendars', 'formforge')); ?>';
                                        if (!r || !r.success) { alert((r && r.data && r.data.error) || '<?php echo esc_js(__('Failed to load calendars.', 'formforge')); ?>'); return; }
                                        var current = select.value;
                                        while (select.firstChild) select.removeChild(select.firstChild);
                                        var placeholder = document.createElement('option');
                                        placeholder.value = ''; placeholder.textContent = '<?php echo esc_js(__('— select a calendar —', 'formforge')); ?>';
                                        select.appendChild(placeholder);
                                        (r.data.calendars || []).forEach(function(c){
                                            var o = document.createElement('option');
                                            o.value = c.id;
                                            o.textContent = c.summary + (c.primary ? ' (primary)' : '');
                                            if (c.id === current) o.selected = true;
                                            select.appendChild(o);
                                        });
                                    })
                                    .catch(function(){
                                        refresh.textContent = '<?php echo esc_js(__('Load my calendars', 'formforge')); ?>';
                                        alert('<?php echo esc_js(__('Network error.', 'formforge')); ?>');
                                    });
                            });
                            select.addEventListener('change', function(){
                                var opt = select.options[select.selectedIndex];
                                nameInput.value = opt ? opt.textContent : '';
 // auto-tick calendar_enabled when a real
                                // calendar is selected. Forgetting to tick was
                                // the silent reason events weren't being
                                // created on submission.
                                var enabledCb = document.getElementById('ff-gc-enabled');
                                if (enabledCb && select.value && !enabledCb.checked) {
                                    enabledCb.checked = true;
                                    enabledCb.dispatchEvent(new Event('change'));
                                }
                            });
                        }

                        jQuery(function(){  // FF-rl: defer attach so #formforge-save (rendered later) is found
                        var saveBtn = document.getElementById('formforge-save');
                        if (saveBtn) {
                            saveBtn.addEventListener('click', function(){
                                var si = document.getElementById('formforge-settings-json');
                                var s; try { s = JSON.parse(si.value); } catch(e) { s = {}; }
 // at save time, if a calendar is selected, also flip
                                // calendar_enabled true. Auto-correct in case user picked a
                                // calendar but forgot to tick enabled.
                                var calId = document.getElementById('ff-gc-calendar-id') ? document.getElementById('ff-gc-calendar-id').value : '';
                                var calEnabledRaw = document.getElementById('ff-gc-enabled') ? document.getElementById('ff-gc-enabled').checked : false;
                                s.calendar_enabled = calEnabledRaw || !!calId;
                                s.calendar_id = calId;
                                s.calendar_name = document.getElementById('ff-gc-calendar-name') ? document.getElementById('ff-gc-calendar-name').value : '';
 // work_start / work_end / slot_duration moved to the
                                // calendar_picker FIELD. Only persist legacy form-level
                                // values when explicitly present (hidden inputs carrying
                                // older saved values), so existing forms aren't disrupted.
                                var ws = document.getElementById('ff-gc-work-start')?.value;
                                if (ws) s.calendar_work_start = parseInt(ws, 10);
                                var we = document.getElementById('ff-gc-work-end')?.value;
                                if (we) s.calendar_work_end = parseInt(we, 10);
                                var sd = document.getElementById('ff-gc-slot-duration')?.value;
                                if (sd) s.calendar_slot_duration = parseInt(sd, 10);
                                s.calendar_event_title = document.getElementById('ff-gc-event-title') ? document.getElementById('ff-gc-event-title').value : 'Booking: {name}';
                                si.value = JSON.stringify(s);
                            }, true);
                        }
                        });
                    })();
                    </script>

                    <!-- HubSpot CRM (per-form) -->
                    <?php $hs_enabled = !empty($form->settings['hubspot_enabled']); ?>
                    <div class="ff-settings-card" style="margin-bottom:12px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden;">
                        <div style="display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid #e2e8f0;background:#f8fafc;cursor:pointer;" id="ff-hs-toggle-head">
                            <span class="dashicons dashicons-businessman" style="color:#3B82F6;font-size:16px;width:16px;height:16px;"></span>
                            <strong style="font-size:14px;color:#1e293b;"><?php esc_html_e('HubSpot CRM', 'formforge'); ?></strong>
                            <?php if (!FORMFORGE_License::instance()->can('hubspot')): ?>
                                <span style="font-size:14px;color:#94a3b8;background:#f1f5f9;padding:1px 6px;border-radius:4px;margin-left:4px;">PRO</span>
                            <?php endif; ?>
                            <span class="dashicons dashicons-arrow-down-alt2" id="ff-hs-chevron" style="margin-left:auto;font-size:14px;width:14px;height:14px;color:#94a3b8;transition:transform 0.2s;"></span>
                        </div>
                        <div id="ff-hs-body" style="padding:16px;display:none;">
                            <label class="ff-toggle-label" style="display:flex;align-items:center;gap:8px;font-size:14px;margin-bottom:12px;cursor:pointer;">
                                <input type="checkbox" id="ff-hs-enabled" <?php checked($hs_enabled); ?>>
                                <span><?php esc_html_e('Send lead to HubSpot on submission', 'formforge'); ?></span>
                            </label>
                            <div id="ff-hs-fields" style="<?php echo esc_attr($hs_enabled ? '' : 'display:none;'); ?>">
                                <label class="ff-toggle-label" style="display:flex;align-items:center;gap:8px;font-size:14px;margin-bottom:10px;cursor:pointer;">
                                    <input type="checkbox" id="ff-hs-create-deal" <?php checked(!empty($form->settings['hubspot_create_deal'])); ?>>
                                    <span><?php esc_html_e('Also create a Deal', 'formforge'); ?></span>
                                </label>
                                <div id="ff-hs-deal-fields" style="<?php echo esc_attr(!empty($form->settings['hubspot_create_deal']) ? '' : 'display:none;'); ?>">
                                    <div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:10px;">
                                        <div>
                                            <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Pipeline ID', 'formforge'); ?></label>
                                            <input type="text" id="ff-hs-pipeline" value="<?php echo esc_attr($form->settings['hubspot_pipeline'] ?? 'default'); ?>" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
                                        </div>
                                        <div>
                                            <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Stage ID', 'formforge'); ?></label>
                                            <input type="text" id="ff-hs-stage" value="<?php echo esc_attr($form->settings['hubspot_stage'] ?? ''); ?>" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
                                        </div>
                                    </div>
                                </div>
                                <p style="margin:0;font-size:14px;color:#94a3b8;"><?php esc_html_e('Connect HubSpot in Settings first. Email field auto-detected.', 'formforge'); ?></p>
                            </div>
                        </div>
                    </div>
                    <script>
                    (function(){
                        var head = document.getElementById('ff-hs-toggle-head');
                        var body = document.getElementById('ff-hs-body');
                        var chevron = document.getElementById('ff-hs-chevron');
                        if (head) head.addEventListener('click', function(){ var o = body.style.display !== 'none'; body.style.display = o ? 'none' : 'block'; chevron.style.transform = o ? '' : 'rotate(180deg)'; });
                        var cb = document.getElementById('ff-hs-enabled');
                        if (cb) cb.addEventListener('change', function(){ document.getElementById('ff-hs-fields').style.display = this.checked ? '' : 'none'; });
                        var dealCb = document.getElementById('ff-hs-create-deal');
                        if (dealCb) dealCb.addEventListener('change', function(){ document.getElementById('ff-hs-deal-fields').style.display = this.checked ? '' : 'none'; });
                        jQuery(function(){  // FF-rl: defer attach so #formforge-save (rendered later) is found
                        var saveBtn = document.getElementById('formforge-save');
                        if (saveBtn) {
                            saveBtn.addEventListener('click', function(){
                                var si = document.getElementById('formforge-settings-json');
                                var s; try { s = JSON.parse(si.value); } catch(e) { s = {}; }
                                s.hubspot_enabled = document.getElementById('ff-hs-enabled') ? document.getElementById('ff-hs-enabled').checked : false;
                                s.hubspot_create_deal = document.getElementById('ff-hs-create-deal') ? document.getElementById('ff-hs-create-deal').checked : false;
                                s.hubspot_pipeline = document.getElementById('ff-hs-pipeline') ? document.getElementById('ff-hs-pipeline').value : '';
                                s.hubspot_stage = document.getElementById('ff-hs-stage') ? document.getElementById('ff-hs-stage').value : '';
                                si.value = JSON.stringify(s);
                            }, true);
                        }
                        });
                    })();
                    </script>

                    <!-- Post Submission (per-form) -->
                    <?php
                    $ps_enabled = !empty($form->settings['post_submission_enabled']);
                    $ps_type = esc_attr($form->settings['post_submission_type'] ?? 'post');
                    $ps_status = esc_attr($form->settings['post_submission_status'] ?? 'draft');
                    $ps_title_field = esc_attr($form->settings['post_title_field'] ?? '');
                    $ps_content_field = esc_attr($form->settings['post_content_field'] ?? '');
                    $post_types = get_post_types(['public' => true], 'objects');
                    ?>
                    <div class="ff-settings-card" style="margin-bottom:12px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden;">
                        <div style="display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid #e2e8f0;background:#f8fafc;cursor:pointer;" id="ff-postsub-toggle-head">
                            <span class="dashicons dashicons-admin-post" style="color:#3B82F6;font-size:16px;width:16px;height:16px;"></span>
                            <strong style="font-size:14px;color:#1e293b;"><?php esc_html_e('Post Submission', 'formforge'); ?></strong>
                            <?php if (!FORMFORGE_License::instance()->can('post_submission')): ?>
                                <span style="font-size:14px;color:#94a3b8;background:#f1f5f9;padding:1px 6px;border-radius:4px;margin-left:4px;">PRO</span>
                            <?php endif; ?>
                            <span class="dashicons dashicons-arrow-down-alt2" id="ff-postsub-chevron" style="margin-left:auto;font-size:14px;width:14px;height:14px;color:#94a3b8;transition:transform 0.2s;"></span>
                        </div>
                        <div id="ff-postsub-body" style="padding:16px;display:none;">
                            <label class="ff-toggle-label" style="display:flex;align-items:center;gap:8px;font-size:14px;margin-bottom:12px;cursor:pointer;">
                                <input type="checkbox" id="ff-postsub-enabled" <?php checked($ps_enabled); ?>>
                                <span><?php esc_html_e('Create a WordPress post from submission', 'formforge'); ?></span>
                            </label>
                            <div id="ff-postsub-fields" style="<?php echo esc_attr($ps_enabled ? '' : 'display:none;'); ?>">
                                <div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:10px;">
                                    <div>
                                        <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Post Type', 'formforge'); ?></label>
                                        <select id="ff-postsub-type" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
                                            <?php foreach ($post_types as $pt): ?>
                                                <option value="<?php echo esc_attr($pt->name); ?>" <?php selected($ps_type, $pt->name); ?>><?php echo esc_html($pt->labels->singular_name); ?></option>
                                            <?php endforeach; ?>
                                        </select>
                                    </div>
                                    <div>
                                        <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Post Status', 'formforge'); ?></label>
                                        <select id="ff-postsub-status" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
                                            <option value="draft" <?php selected($ps_status, 'draft'); ?>><?php esc_html_e('Draft', 'formforge'); ?></option>
                                            <option value="pending" <?php selected($ps_status, 'pending'); ?>><?php esc_html_e('Pending Review', 'formforge'); ?></option>
                                            <option value="publish" <?php selected($ps_status, 'publish'); ?>><?php esc_html_e('Published', 'formforge'); ?></option>
                                        </select>
                                    </div>
                                </div>
                                <div style="margin-bottom:10px;">
                                    <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Title Field', 'formforge'); ?></label>
                                    <select id="ff-postsub-title-field" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
                                        <option value=""><?php esc_html_e('Select field...', 'formforge'); ?></option>
                                    </select>
                                </div>
                                <div>
                                    <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Content Field', 'formforge'); ?></label>
                                    <select id="ff-postsub-content-field" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
                                        <option value=""><?php esc_html_e('Select field...', 'formforge'); ?></option>
                                    </select>
                                </div>
                            </div>
                        </div>
                    </div>
                    <script>
                    (function(){
                        var head = document.getElementById('ff-postsub-toggle-head');
                        var body = document.getElementById('ff-postsub-body');
                        var chevron = document.getElementById('ff-postsub-chevron');
                        if (head) {
                            head.addEventListener('click', function(){
                                var open = body.style.display !== 'none';
                                body.style.display = open ? 'none' : 'block';
                                chevron.style.transform = open ? '' : 'rotate(180deg)';
                            });
                        }
                        var cb = document.getElementById('ff-postsub-enabled');
                        var fields = document.getElementById('ff-postsub-fields');
                        if (cb) {
                            cb.addEventListener('change', function(){
                                fields.style.display = this.checked ? '' : 'none';
                            });
                        }

                        // Populate field dropdowns
                        function populatePostSubFields(formFields) {
                            var titleSel = document.getElementById('ff-postsub-title-field');
                            var contentSel = document.getElementById('ff-postsub-content-field');
                            if (!titleSel || !contentSel) return;
                            var curTitle = titleSel.value || <?php echo wp_json_encode($ps_title_field, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT); ?>;
                            var curContent = contentSel.value || <?php echo wp_json_encode($ps_content_field, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT); ?>;
                            titleSel.innerHTML = '<option value=""><?php echo esc_js(__('Select field...', 'formforge')); ?></option>';
                            contentSel.innerHTML = '<option value=""><?php echo esc_js(__('Select field...', 'formforge')); ?></option>';
                            formFields.forEach(function(f){
                                if (['heading','html_block','divider','page_break','hidden'].indexOf(f.type) !== -1) return;
                                var opt1 = document.createElement('option');
                                opt1.value = f.id;
                                opt1.textContent = f.label || f.id;
                                if (f.id === curTitle) opt1.selected = true;
                                titleSel.appendChild(opt1);
                                var opt2 = document.createElement('option');
                                opt2.value = f.id;
                                opt2.textContent = f.label || f.id;
                                if (f.id === curContent) opt2.selected = true;
                                contentSel.appendChild(opt2);
                            });
                        }
                        function currentPostSubFields() {
                            if (window.formforgeGetFields) {
                                return window.formforgeGetFields();
                            }
                            var fieldsJson = document.getElementById('formforge-fields-json');
                            if (!fieldsJson) return [];
                            try { return JSON.parse(fieldsJson.value || '[]'); } catch(e) { return []; }
                        }
                        jQuery(function(){
                            populatePostSubFields(currentPostSubFields());
                            jQuery(document).on('formforge:fields-changed', function(e, nextFields){
                                populatePostSubFields(nextFields || currentPostSubFields());
                            });
                        });

                        jQuery(function(){  // FF-rl: defer attach so #formforge-save (rendered later) is found
                        var saveBtn = document.getElementById('formforge-save');
                        if (saveBtn) {
                            saveBtn.addEventListener('click', function(){
                                var settingsInput = document.getElementById('formforge-settings-json');
                                var settings;
                                try { settings = JSON.parse(settingsInput.value); } catch(e) { settings = {}; }
                                settings.post_submission_enabled = document.getElementById('ff-postsub-enabled') ? document.getElementById('ff-postsub-enabled').checked : false;
                                settings.post_submission_type = document.getElementById('ff-postsub-type') ? document.getElementById('ff-postsub-type').value : 'post';
                                settings.post_submission_status = document.getElementById('ff-postsub-status') ? document.getElementById('ff-postsub-status').value : 'draft';
                                settings.post_title_field = document.getElementById('ff-postsub-title-field') ? document.getElementById('ff-postsub-title-field').value : '';
                                settings.post_content_field = document.getElementById('ff-postsub-content-field') ? document.getElementById('ff-postsub-content-field').value : '';
                                settingsInput.value = JSON.stringify(settings);
                            }, true);
                        }
                        });
                    })();
	                    </script>

	                    <!-- User Registration (per-form) -->
	                    <?php
	                    $ur_enabled = !empty($form->settings['create_user']);
	                    $ur_role = sanitize_key($form->settings['user_role'] ?? 'subscriber');
	                    $ur_email_field = esc_attr($form->settings['user_email_field'] ?? '');
	                    $ur_name_field = esc_attr($form->settings['user_name_field'] ?? '');
	                    $ur_can_use = FORMFORGE_License::instance()->can('user_registration');
	                    $ur_roles = [
	                        'subscriber'  => __('Subscriber', 'formforge'),
	                        'contributor' => __('Contributor', 'formforge'),
	                        'author'      => __('Author', 'formforge'),
	                    ];
	                    if (!isset($ur_roles[$ur_role])) {
	                        $ur_role = 'subscriber';
	                    }
	                    ?>
	                    <div class="ff-settings-card" style="margin-bottom:12px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden;">
	                        <div style="display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid #e2e8f0;background:#f8fafc;cursor:pointer;" id="ff-userreg-toggle-head">
	                            <span class="dashicons dashicons-admin-users" style="color:#3B82F6;font-size:16px;width:16px;height:16px;"></span>
	                            <strong style="font-size:14px;color:#1e293b;"><?php esc_html_e('User Registration', 'formforge'); ?></strong>
	                            <?php if (!$ur_can_use): ?>
	                                <span style="font-size:14px;color:#94a3b8;background:#f1f5f9;padding:1px 6px;border-radius:4px;margin-left:4px;">PRO</span>
	                            <?php endif; ?>
	                            <span class="dashicons dashicons-arrow-down-alt2" id="ff-userreg-chevron" style="margin-left:auto;font-size:14px;width:14px;height:14px;color:#94a3b8;transition:transform 0.2s;"></span>
	                        </div>
	                        <div id="ff-userreg-body" style="padding:16px;display:none;">
	                            <label class="ff-toggle-label" style="display:flex;align-items:center;gap:8px;font-size:14px;margin-bottom:12px;cursor:pointer;">
	                                <input type="checkbox" id="ff-userreg-enabled" <?php checked($ur_enabled && $ur_can_use); ?> <?php disabled(!$ur_can_use); ?>>
	                                <span><?php esc_html_e('Create a WordPress user after successful submission', 'formforge'); ?></span>
	                            </label>
	                            <?php if (!$ur_can_use): ?>
	                                <p style="margin:0 0 12px;font-size:13px;color:#64748b;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;padding:8px 10px;"><?php esc_html_e('User Registration requires Form Forge Pro.', 'formforge'); ?></p>
	                            <?php endif; ?>
	                            <div id="ff-userreg-fields" style="<?php echo esc_attr(($ur_enabled && $ur_can_use) ? '' : 'display:none;'); ?>">
	                                <div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:10px;">
	                                    <div>
	                                        <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Email Field', 'formforge'); ?></label>
	                                        <select id="ff-userreg-email-field" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
	                                            <option value=""><?php esc_html_e('Select email field...', 'formforge'); ?></option>
	                                        </select>
	                                    </div>
	                                    <div>
	                                        <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('Name Field', 'formforge'); ?></label>
	                                        <select id="ff-userreg-name-field" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
	                                            <option value=""><?php esc_html_e('Auto-detect name field', 'formforge'); ?></option>
	                                        </select>
	                                    </div>
	                                </div>
	                                <div>
	                                    <label style="display:block;font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:4px;"><?php esc_html_e('User Role', 'formforge'); ?></label>
	                                    <select id="ff-userreg-role" style="width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
	                                        <?php foreach ($ur_roles as $role_key => $role_label): ?>
	                                            <option value="<?php echo esc_attr($role_key); ?>" <?php selected($ur_role, $role_key); ?>><?php echo esc_html($role_label); ?></option>
	                                        <?php endforeach; ?>
	                                    </select>
	                                </div>
	                            </div>
	                        </div>
	                    </div>
	                    <script>
	                    (function(){
	                        var head = document.getElementById('ff-userreg-toggle-head');
	                        var body = document.getElementById('ff-userreg-body');
	                        var chevron = document.getElementById('ff-userreg-chevron');
	                        if (head) {
	                            head.addEventListener('click', function(){
	                                var open = body.style.display !== 'none';
	                                body.style.display = open ? 'none' : 'block';
	                                chevron.style.transform = open ? '' : 'rotate(180deg)';
	                            });
	                        }
	                        var cb = document.getElementById('ff-userreg-enabled');
	                        var fields = document.getElementById('ff-userreg-fields');
	                        if (cb && fields) {
	                            cb.addEventListener('change', function(){
	                                fields.style.display = this.checked ? '' : 'none';
	                            });
	                        }
	                        function getUserRegFields() {
	                            if (window.formforgeGetFields) {
	                                return window.formforgeGetFields();
	                            }
	                            var fieldsJson = document.getElementById('formforge-fields-json');
	                            if (!fieldsJson) return [];
	                            try { return JSON.parse(fieldsJson.value || '[]'); } catch(e) { return []; }
	                        }
	                        function addUserRegOption(select, field, selected) {
	                            var opt = document.createElement('option');
	                            opt.value = field.id;
	                            opt.textContent = field.label || field.id;
	                            if (field.id === selected) opt.selected = true;
	                            select.appendChild(opt);
	                        }
	                        function populateUserRegFields(formFields) {
	                            var emailSel = document.getElementById('ff-userreg-email-field');
	                            var nameSel = document.getElementById('ff-userreg-name-field');
	                            if (!emailSel || !nameSel) return;
	                            var curEmail = emailSel.value || <?php echo wp_json_encode($ur_email_field, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT); ?>;
	                            var curName = nameSel.value || <?php echo wp_json_encode($ur_name_field, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT); ?>;
	                            emailSel.innerHTML = '<option value=""><?php echo esc_js(__('Select email field...', 'formforge')); ?></option>';
	                            nameSel.innerHTML = '<option value=""><?php echo esc_js(__('Auto-detect name field', 'formforge')); ?></option>';
	                            formFields.forEach(function(f){
	                                if (!f || !f.id || ['heading','html_block','divider','page_break','hidden'].indexOf(f.type) !== -1) return;
	                                if (f.type === 'email') {
	                                    addUserRegOption(emailSel, f, curEmail);
	                                }
	                                addUserRegOption(nameSel, f, curName);
	                            });
	                        }
	                        jQuery(function(){
	                            populateUserRegFields(getUserRegFields());
	                            jQuery(document).on('formforge:fields-changed', function(e, nextFields){
	                                populateUserRegFields(nextFields || getUserRegFields());
	                            });
	                            var saveBtn = document.getElementById('formforge-save');
	                            if (saveBtn) {
	                                saveBtn.addEventListener('click', function(){
	                                    var settingsInput = document.getElementById('formforge-settings-json');
	                                    var settings;
	                                    try { settings = JSON.parse(settingsInput.value); } catch(e) { settings = {}; }
	                                    settings.create_user = document.getElementById('ff-userreg-enabled') ? document.getElementById('ff-userreg-enabled').checked : false;
	                                    settings.user_email_field = document.getElementById('ff-userreg-email-field') ? document.getElementById('ff-userreg-email-field').value : '';
	                                    settings.user_name_field = document.getElementById('ff-userreg-name-field') ? document.getElementById('ff-userreg-name-field').value : '';
	                                    settings.user_role = document.getElementById('ff-userreg-role') ? document.getElementById('ff-userreg-role').value : 'subscriber';
	                                    settingsInput.value = JSON.stringify(settings);
	                                }, true);
	                            }
	                        });
	                    })();
	                    </script>

	                    <div class="formforge-save-bar">
	                        <button type="button" class="button button-primary button-hero" id="formforge-save"><?php esc_html_e('Save Form', 'formforge'); ?></button>
                        <?php if ($form): ?>
                            <span class="ff-shortcode-cluster">
                                <span class="ff-shortcode-wrap">
                                    <code class="ff-shortcode-code" id="ff-shortcode-text">[formforge id="<?php echo esc_html($form->id); ?>"]</code>
                                    <button type="button" class="ff-shortcode-copy" id="ff-copy-shortcode" title="<?php esc_attr_e('Copy shortcode', 'formforge'); ?>" aria-label="<?php esc_attr_e('Copy shortcode', 'formforge'); ?>">
                                        <span class="dashicons dashicons-clipboard"></span>
                                    </button>
                                </span>
                                <span class="ff-shortcode-help">
                                    <span class="ff-shortcode-help-text"><?php esc_html_e('Copy this shortcode and paste it into a page, post, widget, or page builder module.', 'formforge'); ?></span>
                                    <?php if ($shortcode_docs_url): ?>
                                        <a href="<?php echo esc_url($shortcode_docs_url); ?>" target="_blank" rel="noopener" class="forge-tip" aria-label="<?php echo esc_attr(__('Embedding forms', 'formforge') . ' - ' . __('Learn where to paste the shortcode and how to test the published form.', 'formforge')); ?>" data-tip-title="<?php esc_attr_e('Embedding forms', 'formforge'); ?>" data-tip-summary="<?php esc_attr_e('Learn where to paste the shortcode and how to test the published form.', 'formforge'); ?>">
                                            <span class="forge-tip__icon" aria-hidden="true">?</span>
                                            <span class="forge-tip__panel" role="tooltip">
                                                <strong class="forge-tip__title"><?php esc_html_e('Embedding forms', 'formforge'); ?></strong>
                                                <span class="forge-tip__summary"><?php esc_html_e('Learn where to paste the shortcode and how to test the published form.', 'formforge'); ?></span>
                                                <span class="forge-tip__cta"><?php esc_html_e('Read the docs', 'formforge'); ?> &rarr;</span>
                                            </span>
                                        </a>
                                    <?php endif; ?>
                                </span>
                            </span>
                            <script>
                            document.getElementById('ff-copy-shortcode').addEventListener('click', function(){
                                var text = document.getElementById('ff-shortcode-text').textContent;
                                navigator.clipboard.writeText(text).then(function(){
                                    var btn = document.getElementById('ff-copy-shortcode');
                                    btn.innerHTML = '<span class="dashicons dashicons-yes-alt"></span>';
                                    btn.classList.add('copied');
                                    setTimeout(function(){
                                        btn.innerHTML = '<span class="dashicons dashicons-clipboard"></span>';
                                        btn.classList.remove('copied');
                                    }, 1500);
                                });
                            });
                            </script>
                        <?php endif; ?>
                    </div>
                </div>

                <!-- Right: Form inspector -->
                <div class="formforge-inspector" id="formforge-inspector">
                    <section class="formforge-field-settings ff-inspector-panel" id="formforge-settings-panel" style="display:none;">
                        <div class="ff-inspector-panel-head">
                            <span class="dashicons dashicons-edit"></span>
                            <h3><?php esc_html_e('Field Settings', 'formforge'); ?></h3>
                            <button type="button" class="ff-inspector-collapse" aria-expanded="true" aria-controls="formforge-settings-content" aria-label="<?php esc_attr_e('Field Settings', 'formforge'); ?>">
                                <span class="dashicons dashicons-arrow-down-alt2" aria-hidden="true"></span>
                            </button>
                        </div>
                        <div id="formforge-settings-content" class="ff-inspector-panel-body"></div>
                    </section>

                    <section class="ff-inspector-panel" id="ff-selected-style-panel" aria-live="polite">
                        <div class="ff-inspector-panel-head">
                            <span class="dashicons dashicons-art"></span>
                            <h3><?php esc_html_e('Element Style', 'formforge'); ?></h3>
                            <button type="button" class="ff-inspector-collapse" aria-expanded="true" aria-controls="ff-selected-style-body" aria-label="<?php esc_attr_e('Element Style', 'formforge'); ?>">
                                <span class="dashicons dashicons-arrow-down-alt2" aria-hidden="true"></span>
                            </button>
                        </div>
                        <div id="ff-selected-style-body" class="ff-inspector-panel-body">
                            <p class="ff-style-empty" id="ff-selected-style-empty"><?php esc_html_e('Click an element in Live preview to style it.', 'formforge'); ?></p>
                            <div id="ff-selected-style-content" style="display:none;">
                                <div class="ff-selected-style-target" id="ff-selected-style-target"></div>
                                <div class="ff-selected-style-fields" id="ff-selected-style-fields"></div>
                            </div>
                        </div>
                    </section>

                    <section class="ff-preview-card ff-inspector-panel" aria-label="<?php esc_attr_e('Live preview', 'formforge'); ?>">
                        <div class="ff-preview-head">
                            <strong><?php esc_html_e('Live preview', 'formforge'); ?></strong>
                            <div class="ff-preview-actions">
                                <button type="button" class="button" id="ff-refresh-preview"><?php esc_html_e('Refresh', 'formforge'); ?></button>
                                <button type="button" class="ff-inspector-collapse" aria-expanded="true" aria-controls="formforge-preview-frame" aria-label="<?php esc_attr_e('Live preview', 'formforge'); ?>">
                                    <span class="dashicons dashicons-arrow-down-alt2" aria-hidden="true"></span>
                                </button>
                            </div>
                        </div>
                        <p class="ff-preview-note"><?php esc_html_e('Preview uses Form Forge default frontend styles. Your site theme or global CSS may still change the final published appearance.', 'formforge'); ?></p>
                        <div id="formforge-preview-frame" class="ff-preview-frame" aria-live="polite">
                            <iframe id="formforge-preview-iframe" class="ff-preview-iframe" title="<?php esc_attr_e('Live preview', 'formforge'); ?>" sandbox="allow-same-origin"></iframe>
                        </div>
                    </section>
                </div>
            </div>

            <input type="hidden" id="formforge-form-id" value="<?php echo esc_attr($form_id); ?>">
            <input type="hidden" id="formforge-fields-json" value="<?php echo esc_attr($form ? wp_json_encode($form->fields) : '[]'); ?>">
            <input type="hidden" id="formforge-settings-json" value="<?php echo esc_attr($form ? wp_json_encode($form->settings) : '{}'); ?>">
            <!-- Conditional logic data is preserved across saves even though
 the form editor has no UI to edit it yet. Without this, the
 admin.js save call would post '' and wipe rules created
 programmatically (e.g. via the AI insight actions or imports). -->
            <input type="hidden" id="formforge-conditional-json" value="<?php echo esc_attr($form ? wp_json_encode($form->conditional_logic) : '[]'); ?>">
        </div>
        <?php
    }

    private function render_field_card($field, $index) {
        $type = $field['type'] ?? 'text';
        $label = $field['label'] ?? ucfirst($type);
        $types = FORMFORGE_Field_Types::get_types();
        $type_label = $types[$type]['label'] ?? ucwords(str_replace('_', ' ', $type));
        $required = !empty($field['required']) ? '<span style="color:#DC2626;">*</span>' : '';
        ?>
        <div class="formforge-field-card" data-index="<?php echo (int) $index; ?>" data-type="<?php echo esc_attr($type); ?>" data-field-id="<?php echo esc_attr($field['id'] ?? ''); ?>">
            <div class="formforge-field-card-header">
                <span class="formforge-field-drag-handle">&#9776;</span>
                <span class="formforge-field-type-badge"><?php echo esc_html($type_label); ?></span>
                <strong><?php echo esc_html($label); ?> <?php echo $required; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- trusted pre-built markup (literal <span> or empty) ?></strong>
                <span class="formforge-field-actions">
                    <button type="button" class="formforge-edit-field" title="Edit">&#9998;</button>
                    <button type="button" class="formforge-delete-field" title="Delete">&times;</button>
                </span>
            </div>
        </div>
        <?php
    }

    public function render_submissions() {
        global $wpdb;
        $form_id = intval($_GET['form_id'] ?? 0); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin UI state, no data mutation.
        $forms = FORMFORGE_Form_Builder::instance()->get_all();

        // paginate the submissions list. Default 20 per page,
        // user-selectable 10/20/50/100 via query string. Numeric pagination
        // links rendered with paginate_links under the table.
        $per_page = intval($_GET['per_page'] ?? 20); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin UI state, no data mutation.
        if (!in_array($per_page, [10, 20, 50, 100], true)) $per_page = 20;
        $current_page = max(1, intval($_GET['paged'] ?? 1)); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin UI state, no data mutation.
        $offset = ($current_page - 1) * $per_page;

        $subs = [];
        $total = 0;
        if ($form_id) {
            $total = (int) $wpdb->get_var($wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- custom plugin table; direct query is intended.
                "SELECT COUNT(*) FROM {$wpdb->prefix}formforge_submissions WHERE form_id = %d", $form_id
            ));
            $subs = $wpdb->get_results($wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- custom plugin table; direct query is intended.
                "SELECT * FROM {$wpdb->prefix}formforge_submissions WHERE form_id = %d ORDER BY created_at DESC LIMIT %d OFFSET %d",
                $form_id, $per_page, $offset
            ));
        }
        $total_pages = max(1, (int) ceil($total / $per_page));
        ?>
        <div class="wrap ff-admin">
            <?php FORMFORGE_Admin_Tabs::render('formforge-submissions'); ?>
            <?php FORMFORGE_License::instance()->render_upgrade_banner(); ?>
            <h1><?php esc_html_e('Submissions', 'formforge'); ?><?php if (class_exists('Forge_Docs_Link')) Forge_Docs_Link::render('formforge', 'viewing-and-managing-submissions', __('Managing submissions', 'formforge'), __('View, filter, export, resend — everything submission-side.', 'formforge')); ?></h1>

            <div style="display:flex;gap:10px;align-items:center;margin:16px 0;flex-wrap:wrap;">
                <form method="get" style="display:flex;gap:10px;align-items:center;">
                    <input type="hidden" name="page" value="formforge-submissions">
                    <select name="form_id">
                        <option value=""><?php esc_html_e('Select form...', 'formforge'); ?></option>
                    <?php foreach ($forms as $f): ?>
                        <option value="<?php echo (int) $f->id; ?>" <?php selected($form_id, $f->id); ?>><?php echo esc_html($f->title); ?></option>
                    <?php endforeach; ?>
                </select>
                    <?php if ($form_id): ?>
                        <select name="per_page" title="<?php esc_attr_e('Submissions per page', 'formforge'); ?>">
                            <?php foreach ([10,20,50,100] as $n): ?>
                                <option value="<?php echo esc_attr($n); ?>" <?php selected($per_page, $n); ?>><?php echo esc_html($n); ?> / page</option>
                            <?php endforeach; ?>
                        </select>
                    <?php endif; ?>
                    <input type="submit" class="button" value="<?php esc_attr_e('View', 'formforge'); ?>">
                </form>
                <?php if ($form_id && !empty($subs)): ?>
                    <a href="<?php echo esc_url(wp_nonce_url(admin_url('admin.php?page=formforge-submissions&ff_export_csv=1&form_id=' . $form_id), 'ff_export_csv')); ?>" class="button" style="display:inline-flex;align-items:center;">
                        <span class="dashicons dashicons-download" style="font-size:14px;width:14px;height:14px;display:inline-flex;align-items:center;justify-content:center;line-height:1;margin-right:2px;"></span>
                        <?php esc_html_e('Export CSV', 'formforge'); ?>
                    </a>
                    <?php
                    /*
 * (R3): top-level "Print / PDF" is a BULK PDF export of
 * the same filtered submission set Export CSV uses — NOT
 * window.print, NOT a single "latest visible" submission.
 * It points at the nonce-protected
 * admin-ajax.php?action=formforge_download_bulk_pdf endpoint,
 * carrying the identical form_id scope as the CSV link above,
 * and streams one clean application/pdf with every filtered
 * submission on its own page (no WP admin chrome).
 */
                    ?>
                    <a href="<?php echo esc_url(admin_url('admin-ajax.php?action=formforge_download_bulk_pdf&form_id=' . $form_id . '&nonce=' . wp_create_nonce('formforge_bulk_pdf'))); ?>" class="button" style="display:inline-flex;align-items:center;">
                        <span class="dashicons dashicons-pdf" style="font-size:14px;width:14px;height:14px;display:inline-flex;align-items:center;justify-content:center;line-height:1;margin-right:2px;"></span>
                        <?php esc_html_e('Print / PDF', 'formforge'); ?>
                    </a>
                <?php endif; ?>
            </div>

            <?php
            // PRIMARY entry point for AI Submission Analysis. The feature
            // analyzes collected submissions / conversion data, so the
            // Submissions page is its natural home (it used to live only on the
            // form editor, which confused testers). The shared widget gates
            // itself on the submissions_ai capability + shows a clear message
            // when there isn't enough data yet.
            if ($form_id) {
                $this->render_ai_analysis_widget($form_id, 'submissions');
            }
            ?>

            <?php if ($form_id && !empty($subs)):
                // map of field_id => field config for radio label lookup.
                // R4: built via the shared build_field_config_map and
                // fed into resolve_field_label so the table header, the
                // View modal, and the PDF all resolve labels identically.
                // Admin Submissions view must read any saved form (even past
                // Free soft-limit) so submissions for forms 6..N still render.
                $form_obj = FORMFORGE_Form_Builder::instance()->get_unfiltered($form_id);
                $field_config_map = $this->build_field_config_map($form_obj);
                $columns = $this->get_submission_columns($subs, $form_obj);
            ?>
            <?php $max_cols = min(4, count($columns)); ?>
            <table class="widefat striped">
                <thead><tr>
                    <th style="width:40px;">#</th>
                    <?php foreach (array_slice($columns, 0, $max_cols) as $col):
                        // R4: resolve via the shared helper. $col is a
                        // bare field id; pass a minimal entry so the helper
                        // falls through to the live form definition's label.
                        $label = $this->resolve_field_label($col, ['label' => ''], $field_config_map);
                    ?>
                        <th><?php echo esc_html($label); ?></th>
                    <?php endforeach; ?>
                    <th><?php esc_html_e('Date', 'formforge'); ?></th>
                    <th style="width:60px;"><?php esc_html_e('Status', 'formforge'); ?></th>
                    <th style="width:50px;"></th>
                </tr></thead>
                <tbody>
                <?php foreach ($subs as $sub):
                    $data = json_decode($sub->data, true) ?: [];
                    $view_data = $this->prepare_submission_view_data($data, $sub->id, $field_config_map);
                    // surface payment metadata in the View modal —
                    // payment_status colored chip + payment_intent_id linked
                    // to Stripe Dashboard + amount/currency + updated_at.
                    $payment_details = $this->prepare_payment_details($data, $sub);
                    // r4: render table cells from the SAME normalised
                    // data the View modal uses ($view_data). The raw $data of
                    // a legacy-abandoned row keys captured values as
                    // `formforge_field_<id>`, so a direct $data[$col] lookup
                    // (where $col is the plain field id) missed and the row
                    // showed empty cells. filter_submission_display_data
                    // inside prepare_submission_view_data normalises both
                    // the legacy flat shape and the canonical object shape to
                    // `<id> => {label,value,type}`, so this is now the single
                    // value-extraction path for table + modal alike.
                    $cell_data = $view_data;
                    $payment_chip = $this->payment_status_chip_style($sub->payment_status ?? '');
                ?>
                    <tr>
                        <td><?php echo esc_html($sub->id); ?></td>
                        <?php foreach (array_slice($columns, 0, $max_cols) as $col):
                            $field_data = isset($cell_data[$col]) && is_array($cell_data[$col]) ? $cell_data[$col] : [];
                            $val = isset($field_data['value']) ? $field_data['value'] : '';
                            $col_type = isset($field_data['type']) ? $field_data['type'] : '';
                            if (is_array($val)) {
                                $val = implode(', ', $val);
                            } elseif ($col_type === 'radio' && $val !== '') {
                                $field_config = $field_config_map[$col] ?? [];
                                if (!empty($field_config['options'])) {
                                    foreach ($field_config['options'] as $option) {
                                        if (isset($option['value']) && $option['value'] === $val) {
                                            $val = $option['label'] ?? $val;
                                            break;
                                        }
                                    }
                                }
                            }
                        ?>
                            <td><?php echo esc_html(mb_substr($val, 0, 40)); ?></td>
                        <?php endforeach; ?>
                        <td><?php echo esc_html(date_i18n(get_option('date_format') . ' H:i', strtotime($sub->created_at))); ?></td>
	                        <td>
	                            <span style="padding:2px 6px;border-radius:3px;font-size:14px;background:<?php echo $sub->status === 'new' ? '#EFF6FF' : '#F1F5F9'; ?>;color:<?php echo $sub->status === 'new' ? '#3B82F6' : '#64748B'; ?>;"><?php echo esc_html($sub->status); ?></span>
	                            <?php if (!empty($sub->payment_status)): ?>
	                                <span style="display:inline-block;margin-top:4px;padding:2px 6px;border-radius:3px;font-size:12px;background:<?php echo esc_attr($payment_chip['bg']); ?>;color:<?php echo esc_attr($payment_chip['fg']); ?>;"><?php echo esc_html($sub->payment_status); ?></span>
	                            <?php endif; ?>
	                        </td>
                        <td style="white-space:nowrap;">
                            <a href="#" class="ff-view-sub" data-id="<?php echo (int) $sub->id; ?>" data-json="<?php echo esc_attr(wp_json_encode($view_data)); ?>" data-payment="<?php echo esc_attr(wp_json_encode($payment_details)); ?>" style="color:#3B82F6;font-size:14px;font-weight:600;text-decoration:none;"><?php esc_html_e('View', 'formforge'); ?></a>
                            <a href="<?php echo esc_url(admin_url('admin-ajax.php?action=formforge_download_pdf&form_id=' . $form_id . '&submission_id=' . $sub->id . '&nonce=' . wp_create_nonce('formforge_pdf'))); ?>" target="_blank" style="display:inline-flex;align-items:center;gap:3px;padding:2px 7px;background:#F1F5F9;border:1px solid #E2E8F0;border-radius:5px;color:#3B82F6;font-size:14px;font-weight:600;text-decoration:none;margin-left:4px;vertical-align:middle;" onmouseover="this.style.background='#EFF6FF';this.style.borderColor='#BFDBFE'" onmouseout="this.style.background='#F1F5F9';this.style.borderColor='#E2E8F0'" title="<?php esc_attr_e('Download PDF', 'formforge'); ?>"><span class="dashicons dashicons-pdf" style="font-size:14px;width:12px;height:12px;line-height:12px;"></span><?php esc_html_e('PDF', 'formforge'); ?></a>
                            <button type="button" class="button-link-delete ff-delete-sub" data-id="<?php echo (int) $sub->id; ?>" data-nonce="<?php echo esc_attr(wp_create_nonce('formforge_delete_submission_' . (int) $sub->id)); ?>" style="margin-left:6px;font-size:14px;font-weight:600;vertical-align:middle;"><?php esc_html_e('Delete', 'formforge'); ?></button>
                        </td>
                    </tr>
                <?php endforeach; ?>
                </tbody>
            </table>

            <?php
            // pagination controls below the table.
            if ($total > $per_page) {
                $base = add_query_arg(['page'=>'formforge-submissions','form_id'=>$form_id,'per_page'=>$per_page], admin_url('admin.php'));
                $links = paginate_links([
                    'base'      => $base . '&paged=%#%',
                    'format'    => '',
                    'current'   => $current_page,
                    'total'     => $total_pages,
                    'prev_text' => '&laquo;',
                    'next_text' => '&raquo;',
                    'type'      => 'array',
                ]);
                if (is_array($links)) {
                    echo '<div class="tablenav" style="margin-top:12px;display:flex;justify-content:space-between;align-items:center;">';
                    echo '<div class="displaying-num" style="color:#64748b;font-size:13px;">'
                       /* translators: %s: number of items */
                       . esc_html(sprintf(_n('%s item', '%s items', $total, 'formforge'), number_format_i18n($total)))
                       /* translators: 1: current page number, 2: total number of pages */
                       . ' &middot; ' . esc_html(sprintf(__('Page %1$d of %2$d', 'formforge'), $current_page, $total_pages))
                       . '</div>';
                    echo '<div class="tablenav-pages" style="display:flex;gap:4px;">' . implode(' ', $links) . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- trusted markup from paginate_links
                    echo '</div>';
                }
            }
            ?>

            <!-- Submission Detail Modal -->
            <div id="ff-sub-modal" style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);z-index:100000;display:none;align-items:center;justify-content:center;">
                <div style="background:#fff;border-radius:16px;width:600px;max-height:80vh;overflow-y:auto;box-shadow:0 24px 64px rgba(0,0,0,0.2);padding:24px;">
                    <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;">
                        <h3 style="margin:0;font-size:16px;font-weight:700;">Submission Details</h3>
                        <button type="button" id="ff-sub-close" style="background:none;border:none;font-size:24px;color:#94a3b8;cursor:pointer;line-height:1;">&times;</button>
                    </div>
                    <div id="ff-sub-content"></div>
                </div>
            </div>
            <script>
            jQuery(function($){
 // pick a color-coded chip for a payment status. Falls
                // through to a neutral gray for unknown / blank states so we
                // never break the modal when the column is empty.
                function ffPaymentChipColors(status) {
                    switch (String(status || '').toLowerCase()) {
                        case 'succeeded': return { bg: '#ECFDF5', fg: '#047857' };
                        case 'pending':   return { bg: '#FEF3C7', fg: '#92400E' };
                        case 'failed':    return { bg: '#FEE2E2', fg: '#B91C1C' };
                        case 'refunded':  return { bg: '#EDE9FE', fg: '#6D28D9' };
                        default:          return { bg: '#F1F5F9', fg: '#475569' };
                    }
                }
                function ffEscape(s) { return $('<span>').text(s == null ? '' : String(s)).html(); }
                function ffFormatPaymentDate(iso) {
                    if (!iso) return '';
                    var d = new Date(iso);
                    if (isNaN(d.getTime())) return iso;
                    try { return d.toLocaleString(); } catch (e) { return iso; }
                }
                function ffPaymentSectionHtml(payment) {
                    if (!payment || typeof payment !== 'object') return '';
                    var hasAny = payment.status || payment.payment_id || (payment.amount && Number(payment.amount) > 0);
                    if (!hasAny) return '';
                    var chip = ffPaymentChipColors(payment.status);
                    var html = '<div style="margin-top:18px;padding-top:12px;border-top:1px solid #e2e8f0;">';
                    html += '<div style="font-size:13px;font-weight:700;color:#1e293b;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:8px;"><?php echo esc_js(__('Payment details', 'formforge')); ?></div>';
                    html += '<table style="width:100%;border-collapse:collapse;">';
                    if (payment.status) {
                        html += '<tr style="border-bottom:1px solid #f1f5f9;"><td style="padding:8px 12px;font-weight:600;color:#1e293b;width:35%;">' + ffEscape('<?php echo esc_js(__('Status', 'formforge')); ?>') + '</td>';
                        html += '<td style="padding:8px 12px;color:#475569;"><span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:12px;font-weight:600;background:' + chip.bg + ';color:' + chip.fg + ';">' + ffEscape(payment.status) + '</span></td></tr>';
                    }
                    if (payment.amount_display) {
                        html += '<tr style="border-bottom:1px solid #f1f5f9;"><td style="padding:8px 12px;font-weight:600;color:#1e293b;">' + ffEscape('<?php echo esc_js(__('Amount', 'formforge')); ?>') + '</td>';
                        html += '<td style="padding:8px 12px;color:#475569;font-weight:600;">' + ffEscape(payment.amount_display) + '</td></tr>';
                    }
                    if (payment.payment_id) {
                        var idCell;
                        if (payment.dashboard_url) {
                            idCell = '<a href="' + ffEscape(payment.dashboard_url) + '" target="_blank" rel="noopener" style="color:#3B82F6;text-decoration:none;font-family:monospace;font-size:13px;">' + ffEscape(payment.payment_id) + ' ↗</a>';
                        } else {
                            idCell = '<span style="font-family:monospace;font-size:13px;color:#475569;">' + ffEscape(payment.payment_id) + '</span>';
                        }
                        html += '<tr style="border-bottom:1px solid #f1f5f9;"><td style="padding:8px 12px;font-weight:600;color:#1e293b;">' + ffEscape('<?php echo esc_js(__('Payment ID', 'formforge')); ?>') + '</td>';
                        html += '<td style="padding:8px 12px;color:#475569;">' + idCell + '</td></tr>';
                    }
                    var when = ffFormatPaymentDate(payment.updated_at);
                    if (when) {
                        html += '<tr style="border-bottom:1px solid #f1f5f9;"><td style="padding:8px 12px;font-weight:600;color:#1e293b;">' + ffEscape('<?php echo esc_js(__('Payment date', 'formforge')); ?>') + '</td>';
                        html += '<td style="padding:8px 12px;color:#475569;">' + ffEscape(when) + '</td></tr>';
                    }
                    html += '</table></div>';
                    return html;
                }

                $('.ff-view-sub').on('click', function(e){
                    e.preventDefault();
                    var data = JSON.parse($(this).attr('data-json'));
 // payment metadata travels on a separate data-payment
                    // attribute so the field rows aren't polluted with a
                    // synthetic entry. Older rows render with no payment
                    // section because the attr is missing or empty.
                    var payment = null;
                    try {
                        var raw = $(this).attr('data-payment');
                        payment = raw ? JSON.parse(raw) : null;
                    } catch(err) { payment = null; }
                    var html = '<table style="width:100%;border-collapse:collapse;">';
                    for (var key in data) {
                        var field = data[key];
                        var label = field.label || key;
                        var val = field.value;
                        if (Array.isArray(val)) val = val.join(', ');
                        var valueHtml = $('<span>').text(val || '—').html();
                        if (field.type === 'file_upload' && field.download_url) {
                            valueHtml = '<a href="' + $('<span>').text(field.download_url).html() + '" class="button button-small" target="_blank" rel="noopener">' + $('<span>').text(val || 'Download file').html() + '</a>';
                        }
 // render payment fields as the masked id rather
                        // than the raw intent so the section below has the
                        // authoritative link + amount; here just show the id
                        // in monospace so it's easy to copy.
                        if (field.type === 'payment' && val) {
                            valueHtml = '<span style="font-family:monospace;font-size:13px;">' + $('<span>').text(val).html() + '</span>';
                        }
                        html += '<tr style="border-bottom:1px solid #f1f5f9;"><td style="padding:10px 12px;font-weight:600;color:#1e293b;width:35%;vertical-align:top;">' + $('<span>').text(label).html() + '</td><td style="padding:10px 12px;color:#475569;">' + valueHtml + '</td></tr>';
                    }
                    html += '</table>';
                    html += ffPaymentSectionHtml(payment);
                    $('#ff-sub-content').html(html);
                    $('#ff-sub-modal').css('display','flex');
                });
                $(document).on('keydown', function(e){
                    if (e.key === 'Escape') $('#ff-sub-modal').hide();
                });
                $('#ff-sub-close, #ff-sub-modal').on('click', function(e){
                    if (e.target === this) $('#ff-sub-modal').hide();
                });
                $('.ff-delete-sub').on('click', function(e){
                    e.preventDefault();
                    var $button = $(this);
                    var id = $button.data('id');
                    if (!confirm('<?php echo esc_js(__('Delete this submission? Attached private files will also be removed.', 'formforge')); ?>')) return;
                    $button.prop('disabled', true);
                    $.post(ajaxurl, {
                        action: 'formforge_delete_submission',
                        submission_id: id,
                        _wpnonce: $button.data('nonce')
                    }).done(function(resp){
                        if (resp && resp.success) {
                            $button.closest('tr').remove();
                        } else {
                            alert((resp && resp.data && resp.data.message) ? resp.data.message : '<?php echo esc_js(__('Could not delete submission.', 'formforge')); ?>');
                            $button.prop('disabled', false);
                        }
                    }).fail(function(){
                        alert('<?php echo esc_js(__('Could not delete submission.', 'formforge')); ?>');
                        $button.prop('disabled', false);
                    });
                });
            });
            </script>
            <?php elseif ($form_id): ?>
                <p><?php esc_html_e('No submissions yet.', 'formforge'); ?></p>
            <?php endif; ?>
        </div>
        <?php
    }

    public function render_settings() {
        $forge_api = FORMFORGE_Forge_API::instance();

        // Handle Mailchimp connect/disconnect
        if (isset($_POST['formforge_mailchimp_connect']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['formforge_nonce'] ?? '')), 'formforge_settings')) {
            $api_key = sanitize_text_field(wp_unslash($_POST['mailchimp_api_key'] ?? ''));
            if (!$this->can_pro_feature('mailchimp')) {
                echo '<div class="notice notice-error"><p>' . esc_html__('Requires Form Forge Pro.', 'formforge') . '</p></div>';
            } elseif (!empty($api_key)) {
                if (!$this->is_valid_mailchimp_api_key($api_key)) {
                    echo '<div class="notice notice-error"><p>' . esc_html__('Enter a Mailchimp API key in the format key-usX.', 'formforge') . '</p></div>';
                } else {
                    $result = $forge_api->mailchimp_connect($api_key);
                    if (is_wp_error($result)) {
                        echo '<div class="notice notice-error"><p>' . esc_html($this->normalize_connect_error('mailchimp', $result)) . '</p></div>';
                    } else {
                        echo '<div class="notice notice-success"><p>' . esc_html__('Mailchimp connected successfully.', 'formforge') . '</p></div>';
                    }
                }
            }
        }
        if (isset($_POST['formforge_mailchimp_disconnect']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['formforge_nonce'] ?? '')), 'formforge_settings')) {
            $forge_api->mailchimp_disconnect();
            echo '<div class="notice notice-success"><p>' . esc_html__('Mailchimp disconnected.', 'formforge') . '</p></div>';
        }

        // Handle Google disconnect
        if (isset($_POST['formforge_google_disconnect']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['formforge_nonce'] ?? '')), 'formforge_settings')) {
            $disconnect_result = $forge_api->google_disconnect();
            if (is_wp_error($disconnect_result)) {
                echo '<div class="notice notice-error"><p>' . esc_html($disconnect_result->get_error_message()) . '</p></div>';
            }
            // Clear stale per-form selections — without this, every form that
            // referenced the disconnected account kept showing the old
            // spreadsheet/calendar in the form-builder UI even though the
            // OAuth token was revoked. The integration would silently fail
            // on submission, and the editor had to manually open every form
            // and clear the picker.
            $cleared = $this->clear_all_forms_google_settings();
            if ($cleared > 0) {
                echo '<div class="notice notice-success"><p>' . sprintf(
                    /* translators: %d = number of forms cleared */
                    esc_html(_n(
                        'Google disconnected. Cleared Sheets/Calendar selection from %d form.',
                        'Google disconnected. Cleared Sheets/Calendar selection from %d forms.',
                        $cleared,
                        'formforge'
                    )),
                    (int) $cleared
                ) . '</p></div>';
            } else {
                echo '<div class="notice notice-success"><p>' . esc_html__('Google disconnected.', 'formforge') . '</p></div>';
            }
        }

        // HubSpot *connect* is now a dedicated AJAX flow
        // (wp_ajax_formforge_hubspot_connect → FORMFORGE_Core::ajax_hubspot_connect),
        // so it no longer posts the whole Settings <form>. The result renders
        // inline in the HubSpot card. Only *disconnect* stays a server submit.
        if (isset($_POST['formforge_hubspot_disconnect']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['formforge_nonce'] ?? '')), 'formforge_settings')) {
            $forge_api->hubspot_disconnect();
            echo '<div class="notice notice-success"><p>' . esc_html__('HubSpot disconnected.', 'formforge') . '</p></div>';
        }

        if (isset($_POST['formforge_save_settings']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['formforge_nonce'] ?? '')), 'formforge_settings')) {
            $save_errors = [];
            $can_advanced_antispam = $this->can_pro_feature('advanced_antispam');
            $can_maps              = $this->can_pro_feature('yandex_geocoder');
            $can_payment           = $this->can_pro_feature('payment');
            // Slack & Discord are branded Pro integrations with their
            // own slugs (generic `webhooks` is Free). Gate each global URL save
            // on its own capability so a Free/downgraded POST can't persist a
            // branded webhook URL the UI has disabled.
            $can_slack             = $this->can_pro_feature('slack');
            $can_discord           = $this->can_pro_feature('discord');
            $can_telegram          = $this->can_pro_feature('telegram');
            $can_whatsapp          = $this->can_pro_feature('whatsapp');
            // Pull the existing settings so we can keep stored secrets when
            // the password input was submitted EMPTY (we render those fields
            // with a placeholder rather than echoing ciphertext into the DOM).
            $existing      = get_option('formforge_settings', []);
            $existing_strp = get_option('formforge_stripe_settings', []);
            // Preserve-on-empty helper for masked-secret inputs. If the
            // submitted field is non-empty we encrypt the new value; if it's
            // empty we keep whatever was already in storage (which is
            // either ciphertext, legacy plaintext, or empty).
            $preserve_secret = function ($posted_key, $stored_value) {
                $posted = isset($_POST[$posted_key]) ? sanitize_text_field(wp_unslash($_POST[$posted_key])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified in render_settings before this closure runs.
                if ($posted === '') return $stored_value; // unchanged
                return class_exists('FORMFORGE_Crypto') ? FORMFORGE_Crypto::encrypt($posted) : $posted;
            };

            $posted_settings = isset($_POST['formforge_settings']) && is_array($_POST['formforge_settings'])
                ? wp_unslash($_POST['formforge_settings']) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Structured settings array unslashed here, sanitized per-field below (sanitize_text_field / esc_url_raw / absint per key).
                : [];
            $map_provider = sanitize_text_field($posted_settings['map_provider'] ?? 'google');
            if (!in_array($map_provider, ['google', 'yandex'], true)) {
                $map_provider = 'google';
            }
            $legacy_map_api_key = (string) ($existing['map_api_key'] ?? '');
            $existing_map_provider = (string) ($existing['map_provider'] ?? 'google');
            $existing_google_api_key = (string) ($existing['map_google_api_key'] ?? '');
            $existing_yandex_js_key = (string) ($existing['map_yandex_js_api_key'] ?? '');
            if ($existing_google_api_key === '' && $existing_map_provider === 'google') {
                $existing_google_api_key = $legacy_map_api_key;
            }
            if ($existing_yandex_js_key === '' && $existing_map_provider === 'yandex') {
                $existing_yandex_js_key = $legacy_map_api_key;
            }
            $posted_legacy_map_api_key = sanitize_text_field($posted_settings['map_api_key'] ?? '');
            $map_google_api_key = array_key_exists('map_google_api_key', $posted_settings)
                ? sanitize_text_field($posted_settings['map_google_api_key'])
                : ($map_provider === 'google' && $posted_legacy_map_api_key !== '' ? $posted_legacy_map_api_key : $existing_google_api_key);
            $map_yandex_js_key = array_key_exists('map_yandex_js_api_key', $posted_settings)
                ? sanitize_text_field($posted_settings['map_yandex_js_api_key'])
                : ($map_provider === 'yandex' && $posted_legacy_map_api_key !== '' ? $posted_legacy_map_api_key : $existing_yandex_js_key);

            $settings = is_array($existing) ? $existing : [];
            $settings = array_merge($settings, [
                'default_notification_email' => sanitize_email($posted_settings['default_notification_email'] ?? get_option('admin_email')),
	                'email_sender_name'          => sanitize_text_field($posted_settings['email_sender_name'] ?? get_bloginfo('name')),
            ]);
            if ($can_advanced_antispam) {
                $settings['recaptcha_site_key']   = sanitize_text_field(wp_unslash($_POST['recaptcha_site_key'] ?? ''));
                $settings['recaptcha_secret_key'] = $preserve_secret('recaptcha_secret_key', $existing['recaptcha_secret_key'] ?? '');
                $settings['recaptcha_threshold']  = floatval($_POST['recaptcha_threshold'] ?? 0.5);
            }
            if ($can_maps) {
                $settings['map_provider']            = $map_provider;
                $settings['map_api_key']             = $map_provider === 'yandex' ? $map_yandex_js_key : $map_google_api_key;
                $settings['map_google_api_key']      = $map_google_api_key;
                $settings['map_yandex_js_api_key']   = $map_yandex_js_key;
                $settings['map_yandex_geocoder_key'] = sanitize_text_field($posted_settings['map_yandex_geocoder_key'] ?? '');
                $settings['map_yandex_suggest_key']  = sanitize_text_field($posted_settings['map_yandex_suggest_key'] ?? '');
            }
            if ($can_whatsapp) {
                $settings['whatsapp_global_enabled'] = isset($posted_settings['whatsapp_global_enabled']) ? 1 : 0;
                $settings['whatsapp_phone_e164']     = $this->sanitize_whatsapp_recipient_phone($posted_settings['whatsapp_phone_e164'] ?? '');
            }

            // Save global Telegram Chat ID
            if ($can_telegram && isset($_POST['telegram_chat_id'])) {
                update_option('formforge_telegram_chat_id', sanitize_text_field(wp_unslash($_POST['telegram_chat_id'])));
            }

            // Save global Slack Webhook URL
            if ($can_slack && isset($_POST['slack_webhook_url'])) {
                $slack_url = $this->sanitize_service_webhook_url(esc_url_raw(wp_unslash($_POST['slack_webhook_url'])), 'slack');
                if (is_wp_error($slack_url)) {
                    $save_errors[] = $slack_url->get_error_message();
                } else {
                    update_option('formforge_slack_webhook_url', $slack_url);
                }
            }

            // Save global Discord Webhook URL
            if ($can_discord && isset($_POST['discord_webhook_url'])) {
                $discord_url = $this->sanitize_service_webhook_url(esc_url_raw(wp_unslash($_POST['discord_webhook_url'])), 'discord');
                if (is_wp_error($discord_url)) {
                    $save_errors[] = $discord_url->get_error_message();
                } else {
                    update_option('formforge_discord_webhook_url', $discord_url);
                }
            }

            // Save Stripe settings — secrets pass through preserve_secret
            // so empty inputs keep the stored ciphertext; non-empty inputs
            // are FORMFORGE_Crypto::encrypt'd before persistence.
            if ($can_payment && isset($_POST['stripe_mode'])) {
                $stripe_settings = [
                    'mode'                 => sanitize_text_field(wp_unslash($_POST['stripe_mode'] ?? 'test')),
                    'test_publishable_key' => sanitize_text_field(wp_unslash($_POST['stripe_test_publishable_key'] ?? '')),
                    'test_secret_key'      => $preserve_secret('stripe_test_secret_key',  $existing_strp['test_secret_key']  ?? ''),
                    'live_publishable_key' => sanitize_text_field(wp_unslash($_POST['stripe_live_publishable_key'] ?? '')),
                    'live_secret_key'      => $preserve_secret('stripe_live_secret_key',  $existing_strp['live_secret_key']  ?? ''),
                    'webhook_secret'       => $preserve_secret('stripe_webhook_secret',   $existing_strp['webhook_secret']   ?? ''),
                ];
                update_option('formforge_stripe_settings', $stripe_settings);
            }
            update_option('formforge_settings', $settings);
            echo '<div class="notice notice-success"><p>' . esc_html__('Settings saved.', 'formforge') . '</p></div>';
            foreach ($save_errors as $save_error) {
                echo '<div class="notice notice-error"><p>' . esc_html($save_error) . '</p></div>';
            }
        }
        $s = get_option('formforge_settings', []);
        ?>
        <div class="wrap" style="max-width:860px;">
            <?php FORMFORGE_Admin_Tabs::render('formforge-settings'); ?>
            <?php FORMFORGE_License::instance()->render_upgrade_banner(); ?>
            <div style="display:flex;align-items:center;gap:10px;margin-bottom:20px;padding-bottom:14px;border-bottom:2px solid var(--ff-primary,#3B82F6);">
                <span class="dashicons dashicons-forms" style="color:var(--ff-primary,#3B82F6);font-size:24px;width:24px;height:24px;"></span>
                <h1 style="margin:0;font-size:22px;font-weight:700;"><?php esc_html_e('Form Forge Settings', 'formforge'); ?><?php Forge_Docs_Link::render('formforge', 'settings-overview', __('Settings overview', 'formforge'), __('Global options — API keys, integrations, notifications, anti-spam.', 'formforge')); ?></h1>
            </div>

            <form method="post" id="formforge-global-settings-form">
                <?php wp_nonce_field('formforge_settings', 'formforge_nonce'); ?>

                <div style="display:flex;flex-direction:column;gap:20px;">

                <!-- AI Card -->
                <div class="ff-settings-card">
                    <div class="ff-settings-card-head"><span class="dashicons dashicons-superhero-alt"></span><h2><?php esc_html_e('AI Form Generation', 'formforge'); ?><?php Forge_Docs_Link::render('formforge', 'ai-form-generation-pro', __('AI form generation', 'formforge'), __('Describe a form in plain language — AI builds it in seconds.', 'formforge')); ?></h2></div>
                    <div class="ff-settings-card-body">
                        <?php if (FORMFORGE_License::instance()->can('ai_generation')): ?>
                            <div style="display:flex;align-items:center;gap:8px;font-size:14px;color:#059669;">
                                <span class="dashicons dashicons-yes-alt" style="font-size:20px;width:20px;height:20px;color:#059669;"></span>
                                <?php esc_html_e('AI form generation is active. Powered by Forge AI.', 'formforge'); ?>
                            </div>
                        <?php else: ?>
                            <div style="font-size:14px;color:#64748b;">
                                <?php esc_html_e('AI form generation requires a Pro license. All AI features are processed securely through the Forge API — no external API keys needed.', 'formforge'); ?>
                                <a href="<?php echo esc_url(FORMFORGE_License::instance()->upgrade_url()); ?>" target="_blank" style="color:#3B82F6;font-weight:600;margin-left:4px;"><?php esc_html_e('Upgrade to Pro', 'formforge'); ?></a>
                            </div>
                        <?php endif; ?>
                    </div>
                </div>

                <!-- reCAPTCHA Card -->
                <div class="<?php echo esc_attr($this->settings_card_class('advanced_antispam')); ?>">
                    <div class="ff-settings-card-head"><span class="dashicons dashicons-shield"></span><h2><?php esc_html_e('Google reCAPTCHA v3', 'formforge'); ?><?php Forge_Docs_Link::render('formforge', 'spam-protection', __('reCAPTCHA v3', 'formforge'), __('Invisible bot detection — add site + secret keys from Google.', 'formforge')); ?></h2></div>
                    <div class="ff-settings-card-body">
                        <p style="margin:0 0 12px;font-size:14px;color:#64748b;"><?php esc_html_e('Protect forms from spam bots.', 'formforge'); ?> <a href="https://www.google.com/recaptcha/admin" target="_blank">Get keys &rarr;</a></p>
                        <div class="ff-settings-grid3">
                            <div>
                                <label class="ff-settings-label"><?php esc_html_e('Site Key', 'formforge'); ?></label>
                                <input type="text" name="recaptcha_site_key" value="<?php echo esc_attr($s['recaptcha_site_key'] ?? ''); ?>" class="ff-settings-input" placeholder="6Le..." <?php $this->disabled_without_feature('advanced_antispam'); ?>>
                            </div>
                            <div>
                                <label class="ff-settings-label"><?php esc_html_e('Secret Key', 'formforge'); ?></label>
                                <?php $rc_set = !empty($s['recaptcha_secret_key']); ?>
                                <input type="password" name="recaptcha_secret_key" value="" class="ff-settings-input" placeholder="<?php echo $rc_set ? esc_attr__('•••• stored — leave blank to keep', 'formforge') : '6Le...'; ?>" <?php $this->disabled_without_feature('advanced_antispam'); ?>>
                            </div>
                            <div>
                                <label class="ff-settings-label"><?php esc_html_e('Threshold', 'formforge'); ?></label>
                                <input type="number" name="recaptcha_threshold" value="<?php echo esc_attr($s['recaptcha_threshold'] ?? '0.5'); ?>" step="0.1" min="0" max="1" class="ff-settings-input" <?php $this->disabled_without_feature('advanced_antispam'); ?>>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- Telegram Card -->
                <div class="<?php echo esc_attr($this->settings_card_class('telegram')); ?>">
                    <div class="ff-settings-card-head"><span class="dashicons dashicons-format-chat"></span><h2><?php esc_html_e('Telegram Notifications', 'formforge'); ?><?php Forge_Docs_Link::render('formforge', 'notifications-via-slack-discord-telegram-whatsapp', __('Telegram alerts', 'formforge'), __('Push new submissions to a Telegram bot/channel.', 'formforge')); ?></h2></div>
                    <div class="ff-settings-card-body">
                        <p style="margin:0 0 12px;font-size:14px;color:#64748b;"><?php esc_html_e('Receive form submissions directly in Telegram via', 'formforge'); ?> <a href="https://t.me/ForgeAPINotifyBot" target="_blank" style="color:#3B82F6;font-weight:600;">@ForgeAPINotifyBot</a></p>
                        <div style="background:#EFF6FF;border:1px solid #BFDBFE;border-radius:10px;padding:12px 16px;margin-bottom:12px;font-size:14px;color:#1e40af;">
                            <strong><?php esc_html_e('How to connect:', 'formforge'); ?></strong><br>
                            1. <?php esc_html_e('Open Telegram and search for', 'formforge'); ?> <code>@ForgeAPINotifyBot</code><br>
                            2. <?php esc_html_e('Send', 'formforge'); ?> <code>/start</code> — <?php esc_html_e('the bot will reply with your Chat ID', 'formforge'); ?><br>
                            3. <?php esc_html_e('Paste the Chat ID below', 'formforge'); ?>
                        </div>
                        <div>
                            <label class="ff-settings-label"><?php esc_html_e('Chat ID', 'formforge'); ?></label>
                            <input type="text" name="telegram_chat_id" value="<?php echo esc_attr(get_option('formforge_telegram_chat_id', '')); ?>" class="ff-settings-input" style="max-width:300px;" placeholder="123456789" <?php $this->disabled_without_feature('telegram'); ?>>
                        </div>
                        <p style="margin:8px 0 0;font-size:14px;color:#94a3b8;"><?php esc_html_e('Enable Telegram per form in the form editor settings.', 'formforge'); ?></p>
                    </div>
                </div>

                <!-- WhatsApp Card (FC-44) -->
	                <?php
	                $wa_global_enabled = !is_array($s) || !array_key_exists('whatsapp_global_enabled', $s) || !empty($s['whatsapp_global_enabled']);
	                $wa_recipient_phone = is_array($s) ? $this->sanitize_whatsapp_recipient_phone($s['whatsapp_phone_e164'] ?? '') : '';
	                ?>
                <div class="<?php echo esc_attr($this->settings_card_class('whatsapp')); ?>">
	                    <div class="ff-settings-card-head">
	                        <span class="dashicons dashicons-whatsapp"></span>
                        <h2><?php esc_html_e('WhatsApp Notifications', 'formforge'); ?><?php Forge_Docs_Link::render('formforge', 'notifications-via-slack-discord-telegram-whatsapp', __('WhatsApp alerts', 'formforge'), __('Push new submissions to your personal WhatsApp via the Avakode business number.', 'formforge')); ?></h2>
                    </div>
                    <div class="ff-settings-card-body">
                        <p style="margin:0 0 12px;font-size:14px;color:#64748b;"><?php esc_html_e('Receive form submissions on the WhatsApp number you enter here. Use the international format and save settings before enabling WhatsApp on individual forms.', 'formforge'); ?></p>

                        <label style="display:flex;align-items:flex-start;gap:8px;background:#F8FAFC;border:1px solid #E2E8F0;border-radius:10px;padding:10px 12px;margin-bottom:12px;cursor:pointer;">
                            <input type="checkbox" name="formforge_settings[whatsapp_global_enabled]" value="1" <?php checked($wa_global_enabled); ?> style="margin-top:2px;" <?php $this->disabled_without_feature('whatsapp'); ?>>
                            <span>
                                <strong style="display:block;font-size:14px;color:#1e293b;"><?php esc_html_e('Enable WhatsApp integration globally', 'formforge'); ?></strong>
                                <span style="display:block;font-size:13px;color:#64748b;margin-top:2px;"><?php esc_html_e('Turn this off to pause WhatsApp delivery for every form without changing per-form toggles.', 'formforge'); ?></span>
                            </span>
                        </label>

                        <div id="ff-whatsapp-state" style="background:#EFF6FF;border:1px solid #BFDBFE;border-radius:10px;padding:14px 16px;">
                            <label class="ff-settings-label" for="ff-wa-recipient-phone"><?php esc_html_e('Recipient WhatsApp number', 'formforge'); ?></label>
                            <input type="text" id="ff-wa-recipient-phone" name="formforge_settings[whatsapp_phone_e164]" value="<?php echo esc_attr($wa_recipient_phone); ?>" class="ff-settings-input" style="max-width:320px;" placeholder="+998901234567" inputmode="tel" autocomplete="tel" pattern="^\+?[0-9]*$" data-ff-wa-phone-input <?php $this->disabled_without_feature('whatsapp'); ?>>
                            <p style="margin:8px 0 0;font-size:13px;color:#1e40af;"><?php esc_html_e('This number receives WhatsApp alerts for forms where WhatsApp is enabled. Use digits with country code and one optional leading plus; letters, spaces, dashes, and extra plus signs are removed.', 'formforge'); ?></p>
                            <?php if ($wa_recipient_phone !== ''): ?>
                                <div style="display:flex;align-items:center;gap:8px;margin-top:10px;font-size:14px;color:#166534;">
                                    <span class="dashicons dashicons-yes-alt" style="color:#16a34a;"></span>
                                    <span><?php esc_html_e('Delivery target saved as', 'formforge'); ?> <strong style="font-family:monospace;"><?php echo esc_html($wa_recipient_phone); ?></strong></span>
                                </div>
                            <?php endif; ?>
                            </div>
                            <script>
                            (function(){
                                var input = document.getElementById('ff-wa-recipient-phone');
                                if (!input) return;

                                function clean(value) {
                                    value = String(value || '');
                                    var prefix = value.charAt(0) === '+' ? '+' : '';
                                    var digits = value.replace(/\D/g, '');
                                    return digits ? prefix + digits : prefix;
                                }

                                function sync() {
                                    var cleaned = clean(input.value);
                                    if (input.value !== cleaned) {
                                        input.value = cleaned;
                                    }
                                }

                                input.addEventListener('beforeinput', function(event) {
                                    if (!event.data || event.data.length !== 1 || typeof input.selectionStart !== 'number') return;
                                    var start = input.selectionStart;
                                    var end = input.selectionEnd;
                                    var next = input.value.slice(0, start) + event.data + input.value.slice(end);
                                    if (next !== clean(next)) {
                                        event.preventDefault();
                                    }
                                });
                                input.addEventListener('input', sync);
                                input.addEventListener('paste', function(){ window.setTimeout(sync, 0); });
                                input.addEventListener('blur', sync);
                                sync();
                            })();
                            </script>

                        <p style="margin:12px 0 0;font-size:14px;color:#94a3b8;"><?php _e('Enable WhatsApp per form in the form editor settings. The Avakode business number relays via the pre-approved <code>form_submission</code> template.', 'formforge'); // phpcs:ignore WordPress.Security.EscapeOutput.UnsafePrintingFunction -- translatable string contains trusted inline <code> markup ?></p>
                    </div>
                </div>

                <!-- Maps Card -->
	                <?php
	                $map_provider = $s['map_provider'] ?? 'google';
	                $map_api_key = $s['map_api_key'] ?? '';
	                $map_google_api_key = $s['map_google_api_key'] ?? ($map_provider === 'google' ? $map_api_key : '');
	                $map_yandex_js_api_key = $s['map_yandex_js_api_key'] ?? ($map_provider === 'yandex' ? $map_api_key : '');
	                $map_yandex_geocoder_key = $s['map_yandex_geocoder_key'] ?? '';
	                $map_yandex_suggest_key = $s['map_yandex_suggest_key'] ?? '';
	                $map_is_yandex = $map_provider === 'yandex';
	                ?>
                <div class="<?php echo esc_attr($this->settings_card_class('yandex_geocoder')); ?>">
                    <div class="ff-settings-card-head"><span class="dashicons dashicons-location-alt"></span><h2><?php esc_html_e('Maps (Address Picker)', 'formforge'); ?><?php Forge_Docs_Link::render('formforge', 'address-picker-with-maps-pro', __('Address/map picker', 'formforge'), __('Geocoded address input powered by your Maps API key.', 'formforge')); ?></h2></div>
                    <div class="ff-settings-card-body">
                        <p style="margin:0 0 12px;font-size:14px;color:#64748b;"><?php esc_html_e('Enable the "Address (Map)" field type in your forms. Visitors pick an address on an interactive map.', 'formforge'); ?></p>
	                        <div class="ff-settings-grid3" style="margin-bottom:12px;">
	                            <div>
	                                <label class="ff-settings-label"><?php esc_html_e('Map Provider', 'formforge'); ?></label>
	                                <select name="formforge_settings[map_provider]" class="ff-settings-select" data-ff-map-provider-select <?php $this->disabled_without_feature('yandex_geocoder'); ?>>
	                                    <option value="google" <?php selected($map_provider, 'google'); ?>>Google Maps</option>
	                                    <option value="yandex" <?php selected($map_provider, 'yandex'); ?>>Yandex Maps</option>
	                                </select>
	                            </div>
	                            <div data-ff-map-provider-field="google" <?php echo esc_attr($map_is_yandex ? 'hidden' : ''); ?>>
	                                <label class="ff-settings-label"><?php esc_html_e('Google Maps API Key', 'formforge'); ?></label>
	                                <input type="text" name="formforge_settings[map_google_api_key]" value="<?php echo esc_attr($map_google_api_key); ?>" class="ff-settings-input" placeholder="<?php esc_attr_e('Google Maps API key', 'formforge'); ?>" <?php $this->disabled_without_feature('yandex_geocoder'); ?>>
	                            </div>
	                            <div data-ff-map-provider-field="yandex" <?php echo esc_attr($map_is_yandex ? '' : 'hidden'); ?>>
	                                <label class="ff-settings-label"><?php esc_html_e('Yandex JS API Key', 'formforge'); ?></label>
	                                <input type="text" name="formforge_settings[map_yandex_js_api_key]" value="<?php echo esc_attr($map_yandex_js_api_key); ?>" class="ff-settings-input" placeholder="<?php esc_attr_e('Yandex JavaScript API key', 'formforge'); ?>" <?php $this->disabled_without_feature('yandex_geocoder'); ?>>
	                            </div>
	                            <div data-ff-map-provider-field="yandex" <?php echo esc_attr($map_is_yandex ? '' : 'hidden'); ?>>
	                                <label class="ff-settings-label"><?php esc_html_e('Yandex Geocoder API Key', 'formforge'); ?></label>
	                                <input type="text" name="formforge_settings[map_yandex_geocoder_key]" value="<?php echo esc_attr($map_yandex_geocoder_key); ?>" class="ff-settings-input" placeholder="<?php esc_attr_e('Optional for typed address lookup', 'formforge'); ?>" <?php $this->disabled_without_feature('yandex_geocoder'); ?>>
	                            </div>
	                            <div data-ff-map-provider-field="yandex" <?php echo esc_attr($map_is_yandex ? '' : 'hidden'); ?>>
	                                <label class="ff-settings-label"><?php esc_html_e('Yandex Geosuggest API Key', 'formforge'); ?></label>
	                                <input type="text" name="formforge_settings[map_yandex_suggest_key]" value="<?php echo esc_attr($map_yandex_suggest_key); ?>" class="ff-settings-input" placeholder="<?php esc_attr_e('Optional for Yandex suggestions', 'formforge'); ?>" <?php $this->disabled_without_feature('yandex_geocoder'); ?>>
	                            </div>
	                        </div>
	                        <p style="margin:0;font-size:14px;color:#94a3b8;"><?php esc_html_e('Google Maps: get a key at', 'formforge'); ?> <a href="https://console.cloud.google.com/apis" target="_blank">Google Cloud Console</a>. <?php esc_html_e('Yandex Maps uses a JavaScript API key for the map, a Geocoder API key for typed address lookup, and an optional Geosuggest API key for suggestions.', 'formforge'); ?> <a href="https://developer.tech.yandex.ru/" target="_blank">Yandex Developer</a>. <?php esc_html_e('Requires Pro license.', 'formforge'); ?></p>
	                        <script>
	                        (function(){
	                            var provider = document.querySelector('[data-ff-map-provider-select]');
	                            if (!provider) return;
	                            var providerFields = document.querySelectorAll('[data-ff-map-provider-field]');
	                            function syncMapProviderFields() {
	                                providerFields.forEach(function(field) {
	                                    field.hidden = field.getAttribute('data-ff-map-provider-field') !== provider.value;
	                                });
	                            }
	                            provider.addEventListener('change', syncMapProviderFields);
	                            syncMapProviderFields();
	                        })();
	                        </script>
                    </div>
                </div>

                <!-- Stripe Payments Card -->
                <?php $stripe = get_option('formforge_stripe_settings', []); ?>
                <div class="<?php echo esc_attr($this->settings_card_class('payment')); ?>">
                    <div class="ff-settings-card-head"><span class="dashicons dashicons-money-alt"></span><h2><?php esc_html_e('Stripe Payments', 'formforge'); ?><?php Forge_Docs_Link::render('formforge', 'accepting-payments-with-stripe-pro', __('Stripe payments (PRO)', 'formforge'), __('Collect payments on form submit — fixed or calculated amounts.', 'formforge')); ?></h2></div>
                    <div class="ff-settings-card-body">
                        <p style="margin:0 0 12px;font-size:14px;color:#64748b;"><?php esc_html_e('Accept payments in your forms via Stripe. Payments go directly to your Stripe account.', 'formforge'); ?></p>
                        <div style="margin-bottom:12px;">
                            <label class="ff-settings-label"><?php esc_html_e('Mode', 'formforge'); ?></label>
                            <select name="stripe_mode" class="ff-settings-select" style="max-width:200px;" <?php $this->disabled_without_feature('payment'); ?>>
                                <option value="test" <?php selected($stripe['mode'] ?? 'test', 'test'); ?>>Test</option>
                                <option value="live" <?php selected($stripe['mode'] ?? 'test', 'live'); ?>>Live</option>
                            </select>
                        </div>
                        <div class="ff-settings-grid3" style="grid-template-columns:1fr 1fr;margin-bottom:12px;">
                            <div>
                                <label class="ff-settings-label"><?php esc_html_e('Test Publishable Key', 'formforge'); ?></label>
                                <input type="text" name="stripe_test_publishable_key" value="<?php echo esc_attr($stripe['test_publishable_key'] ?? ''); ?>" class="ff-settings-input" placeholder="pk_test_..." <?php $this->disabled_without_feature('payment'); ?>>
                            </div>
                            <div>
                                <label class="ff-settings-label"><?php esc_html_e('Test Secret Key', 'formforge'); ?></label>
                                <?php $tk_set = !empty($stripe['test_secret_key']); ?>
                                <input type="password" name="stripe_test_secret_key" value="" class="ff-settings-input" placeholder="<?php echo $tk_set ? esc_attr__('•••• stored — leave blank to keep', 'formforge') : 'sk_test_...'; ?>" <?php $this->disabled_without_feature('payment'); ?>>
                            </div>
                        </div>
                        <div class="ff-settings-grid3" style="grid-template-columns:1fr 1fr;margin-bottom:12px;">
                            <div>
                                <label class="ff-settings-label"><?php esc_html_e('Live Publishable Key', 'formforge'); ?></label>
                                <input type="text" name="stripe_live_publishable_key" value="<?php echo esc_attr($stripe['live_publishable_key'] ?? ''); ?>" class="ff-settings-input" placeholder="pk_live_..." <?php $this->disabled_without_feature('payment'); ?>>
                            </div>
                            <div>
                                <label class="ff-settings-label"><?php esc_html_e('Live Secret Key', 'formforge'); ?></label>
                                <?php $lk_set = !empty($stripe['live_secret_key']); ?>
                                <input type="password" name="stripe_live_secret_key" value="" class="ff-settings-input" placeholder="<?php echo $lk_set ? esc_attr__('•••• stored — leave blank to keep', 'formforge') : 'sk_live_...'; ?>" <?php $this->disabled_without_feature('payment'); ?>>
                            </div>
                        </div>
                        <div style="margin-bottom:12px;">
                            <label class="ff-settings-label"><?php esc_html_e('Webhook Signing Secret', 'formforge'); ?></label>
                            <?php $wh_set = !empty($stripe['webhook_secret']); ?>
                            <input type="password" name="stripe_webhook_secret" value="" class="ff-settings-input" style="max-width:400px;" placeholder="<?php echo $wh_set ? esc_attr__('•••• stored — leave blank to keep', 'formforge') : 'whsec_...'; ?>" <?php $this->disabled_without_feature('payment'); ?>>
                            <?php /* translators: %s: Stripe webhook endpoint URL */ ?>
                            <p class="description" style="margin:4px 0 0;font-size:14px;color:#94a3b8;"><?php printf(esc_html__('Webhook URL: %s', 'formforge'), '<code>' . esc_html(rest_url('formforge/v1/stripe/webhook')) . '</code>'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- format string esc_html__'d; %s arg is trusted <code> with esc_html'd URL ?></p>
                        </div>
                        <p style="margin:0;font-size:14px;color:#94a3b8;"><?php esc_html_e('Get your keys at', 'formforge'); ?> <a href="https://dashboard.stripe.com/apikeys" target="_blank">Stripe Dashboard</a>. <?php esc_html_e('Requires Pro license.', 'formforge'); ?></p>
                    </div>
                </div>

                <!-- Slack Notifications Card -->
                <div class="<?php echo esc_attr($this->settings_card_class('slack')); ?>">
                    <div class="ff-settings-card-head"><span class="dashicons dashicons-format-chat"></span><h2><?php esc_html_e('Slack Notifications', 'formforge'); ?><?php Forge_Docs_Link::render('formforge', 'notifications-via-slack-discord-telegram-whatsapp', __('Slack alerts', 'formforge'), __('Post new submissions to a Slack channel via incoming webhook.', 'formforge')); ?></h2></div>
                    <div class="ff-settings-card-body">
                        <p style="margin:0 0 12px;font-size:14px;color:#64748b;"><?php esc_html_e('Send form submissions to a Slack channel via Incoming Webhook.', 'formforge'); ?></p>
                        <div style="background:#EFF6FF;border:1px solid #BFDBFE;border-radius:10px;padding:12px 16px;margin-bottom:12px;font-size:14px;color:#1e40af;">
                            <strong><?php esc_html_e('How to set up:', 'formforge'); ?></strong><br>
                            1. <?php esc_html_e('Go to', 'formforge'); ?> <a href="https://api.slack.com/apps" target="_blank">api.slack.com/apps</a> &rarr; <?php esc_html_e('Create New App', 'formforge'); ?><br>
                            2. <?php esc_html_e('Add "Incoming Webhooks" feature and activate it', 'formforge'); ?><br>
                            3. <?php esc_html_e('Click "Add New Webhook to Workspace" and select a channel', 'formforge'); ?><br>
                            4. <?php esc_html_e('Copy the Webhook URL and paste it below', 'formforge'); ?>
                        </div>
                        <div>
                            <label class="ff-settings-label"><?php esc_html_e('Default Webhook URL', 'formforge'); ?></label>
                            <input type="url" name="slack_webhook_url" value="<?php echo esc_attr(get_option('formforge_slack_webhook_url', '')); ?>" class="ff-settings-input" placeholder="https://hooks.slack.com/services/T.../B.../..." <?php $this->disabled_without_feature('slack'); ?>>
                        </div>
                        <p style="margin:8px 0 0;font-size:14px;color:#94a3b8;"><?php esc_html_e('You can override this per form in the form editor. Requires Pro license.', 'formforge'); ?></p>
                    </div>
                </div>

                <!-- Discord Notifications Card -->
                <div class="<?php echo esc_attr($this->settings_card_class('discord')); ?>">
                    <div class="ff-settings-card-head"><span class="dashicons dashicons-format-status"></span><h2><?php esc_html_e('Discord Notifications', 'formforge'); ?><?php Forge_Docs_Link::render('formforge', 'notifications-via-slack-discord-telegram-whatsapp', __('Discord alerts', 'formforge'), __('Post new submissions to a Discord channel via webhook.', 'formforge')); ?></h2></div>
                    <div class="ff-settings-card-body">
                        <p style="margin:0 0 12px;font-size:14px;color:#64748b;"><?php esc_html_e('Send form submissions to a Discord channel via Webhook.', 'formforge'); ?></p>
                        <div style="background:#EFF6FF;border:1px solid #BFDBFE;border-radius:10px;padding:12px 16px;margin-bottom:12px;font-size:14px;color:#1e40af;">
                            <strong><?php esc_html_e('How to set up:', 'formforge'); ?></strong><br>
                            1. <?php esc_html_e('Open your Discord server settings', 'formforge'); ?><br>
                            2. <?php esc_html_e('Go to Integrations &rarr; Webhooks &rarr; New Webhook', 'formforge'); ?><br>
                            3. <?php esc_html_e('Select a channel and copy the Webhook URL', 'formforge'); ?>
                        </div>
                        <div>
                            <label class="ff-settings-label"><?php esc_html_e('Default Webhook URL', 'formforge'); ?></label>
                            <input type="url" name="discord_webhook_url" value="<?php echo esc_attr(get_option('formforge_discord_webhook_url', '')); ?>" class="ff-settings-input" placeholder="https://discord.com/api/webhooks/..." <?php $this->disabled_without_feature('discord'); ?>>
                        </div>
                        <p style="margin:8px 0 0;font-size:14px;color:#94a3b8;"><?php esc_html_e('You can override this per form in the form editor. Requires Pro license.', 'formforge'); ?></p>
                    </div>
                </div>

                <!-- Mailchimp Card -->
                <div class="<?php echo esc_attr($this->settings_card_class('mailchimp')); ?>">
                    <div class="ff-settings-card-head"><span class="dashicons dashicons-email"></span><h2><?php esc_html_e('Mailchimp', 'formforge'); ?><?php Forge_Docs_Link::render('formforge', 'connecting-to-mailchimp-pro', __('Mailchimp sync', 'formforge'), __('Push submissions as subscribers into a Mailchimp audience.', 'formforge')); ?></h2></div>
                    <div class="ff-settings-card-body">
                        <p style="margin:0 0 12px;font-size:14px;color:#64748b;"><?php esc_html_e('Subscribe form submitters to your Mailchimp email list. API key is stored securely on the Forge API server.', 'formforge'); ?></p>
                        <?php
                        $integration_license_ready = $forge_api->has_license_key();
                        $mailchimp_can_connect = $integration_license_ready && $this->can_pro_feature('mailchimp');
                        $mc_status = $integration_license_ready ? $forge_api->mailchimp_status() : null;
                        $mc_connected = (!is_wp_error($mc_status) && !empty($mc_status['connected']));
                        ?>
                        <?php if ($mc_connected): ?>
                            <div style="display:flex;align-items:center;gap:8px;font-size:14px;color:#059669;margin-bottom:12px;">
                                <span class="dashicons dashicons-yes-alt" style="font-size:20px;width:20px;height:20px;color:#059669;"></span>
                                <?php /* translators: %s: connected Mailchimp account name */ ?>
                                <?php printf(esc_html__('Connected to Mailchimp (%s)', 'formforge'), esc_html($mc_status['account_name'] ?? '')); ?>
                            </div>
                            <button type="submit" name="formforge_mailchimp_disconnect" value="1" class="button ff-settings-destructive-submit" style="color:#EF4444;"><?php esc_html_e('Disconnect', 'formforge'); ?></button>
                        <?php else: ?>
                            <?php if (!$integration_license_ready): ?>
                                <p style="margin:0 0 12px;font-size:14px;color:#b45309;"><?php
                                if (FORMFORGE_License::instance()->is_pro()) {
                                    esc_html_e('Connect Form Forge to your Avakode license before connecting Mailchimp.', 'formforge');
                                } else {
                                    esc_html_e('Mailchimp integration is a Pro feature. Install Form Forge Pro to enable.', 'formforge');
                                }
                                ?></p>
                            <?php endif; ?>
                            <div style="margin-bottom:12px;">
                                <label class="ff-settings-label"><?php esc_html_e('API Key', 'formforge'); ?></label>
                                <div style="display:flex;gap:8px;">
                                    <input type="text" name="mailchimp_api_key" class="ff-settings-input" placeholder="xxxxxxxxxx-us1" style="flex:1;" <?php disabled(!$mailchimp_can_connect); ?>>
                                    <button type="submit" name="formforge_mailchimp_connect" value="1" class="button button-primary" <?php disabled(!$mailchimp_can_connect); ?>><?php esc_html_e('Connect', 'formforge'); ?></button>
                                </div>
                            </div>
                            <p style="margin:0;font-size:14px;color:#94a3b8;"><?php esc_html_e('Get your API key at', 'formforge'); ?> <a href="https://admin.mailchimp.com/account/api/" target="_blank">Mailchimp &rarr; Account &rarr; API keys</a>. <?php esc_html_e('Requires Pro license.', 'formforge'); ?></p>
                        <?php endif; ?>
                    </div>
                </div>

                <!-- Google (Sheets + Calendar) Card -->
                <div class="<?php echo esc_attr($this->settings_card_class('google_sheets')); ?>">
                    <div class="ff-settings-card-head"><span class="dashicons dashicons-google"></span><h2><?php esc_html_e('Google (Sheets + Calendar)', 'formforge'); ?><?php Forge_Docs_Link::render('formforge', 'sending-data-to-google-sheets-pro', __('Google Sheets & Calendar', 'formforge'), __('Append submissions to Sheets; create Calendar events on booking.', 'formforge')); ?></h2></div>
                    <div class="ff-settings-card-body">
                        <p style="margin:0 0 12px;font-size:14px;color:#64748b;"><?php esc_html_e('Connect your Google account to use Sheets (append rows) and Calendar (date picker with availability).', 'formforge'); ?></p>
                        <?php
                        $g_status = $integration_license_ready ? $forge_api->google_status() : null;
                        $g_connected = (!is_wp_error($g_status) && !empty($g_status['connected']));
                        $g_email = (!is_wp_error($g_status) && is_array($g_status) && !empty($g_status['email']))
                            ? (string) $g_status['email']
                            : '';
                        ?>
                        <?php if ($g_connected): ?>
                            <div style="display:flex;align-items:center;gap:8px;font-size:14px;color:#059669;margin-bottom:12px;">
                                <span class="dashicons dashicons-yes-alt" style="font-size:20px;width:20px;height:20px;color:#059669;"></span>
                                <?php if ($g_email !== ''): ?>
                                    <?php printf(
                                        /* translators: %s = Google account email used to connect */
                                        esc_html__('Connected as %s', 'formforge'),
                                        '<span style="color:#0f172a;font-weight:600;">' . esc_html($g_email) . '</span>'
                                    ); ?>
                                <?php else: ?>
                                    <?php esc_html_e('Google account connected', 'formforge'); ?>
                                <?php endif; ?>
                            </div>
                            <button type="submit" name="formforge_google_disconnect" value="1" class="button ff-settings-destructive-submit" style="color:#EF4444;"><?php esc_html_e('Disconnect', 'formforge'); ?></button>
                        <?php else: ?>
                            <?php
                            // google_auth_url obtains the site_token via the
                            // shared Forge_Connect::get_or_refresh_site_token helper —
                            // it returns '' when the license is missing OR the Worker
                            // can't mint a valid site capability token. Gate the <a> on
                            // the URL actually being non-empty: rendering <a href="">
                            // makes the browser resolve href to the current page, so the
                            // button silently pointed back at Form Forge Settings instead
                            // of starting Google OAuth (Sergei's retest 2026-05-14).
                            // (cross-plugin): gate the Connect button on PRO
                            // state, NOT on the constructor-cached license key
                            // ($integration_license_ready = has_license_key), which
                            // can be empty on a genuine PRO site (Freemius synced after
                            // boot, or linked only via an SSO site-token). google_auth_url
                            // resolves a fresh site-token via Forge_Connect (existing token,
                            // or minted from the live Freemius license) and returns '' only
                            // when no connection path exists — then we render the actionable
                            // "Connect to Avakode" card instead of a dead-end note.
                            $google_pro = $this->can_pro_feature('google_sheets') || $this->can_pro_feature('google_calendar');
                            $ff_google_auth_url = $google_pro
                                ? $forge_api->google_auth_url(admin_url('admin.php?page=formforge-settings'))
                                : '';
                            ?>
                            <?php if ($ff_google_auth_url !== ''): ?>
                                <a id="ff-google-connect" href="<?php echo esc_url($ff_google_auth_url); ?>" class="button button-primary"><?php esc_html_e('Connect Google Account', 'formforge'); ?></a>
                                <p style="margin:8px 0 0;font-size:14px;color:#94a3b8;"><?php esc_html_e('Opens Google consent in this tab. Requires Pro license.', 'formforge'); ?></p>
                            <?php elseif ($google_pro && class_exists('Forge_Connect') && method_exists('Forge_Connect', 'render_button')): ?>
                                <?php
                                // PRO but no resolvable Avakode connection yet — render the
                                // canonical "Connect to Avakode" card right here instead of
                                // the old dead-end note. After connecting, the site-token
                                // resolves and "Connect Google Account" appears on reload.
                                Forge_Connect::render_button('formforge', '#3B82F6');
                                ?>
                            <?php else: ?>
                                <button type="button" id="ff-google-connect" class="button button-primary" disabled><?php esc_html_e('Connect Google Account', 'formforge'); ?></button>
                                <p style="margin:8px 0 0;font-size:14px;color:#b45309;">
                                    <?php esc_html_e('Google Sheets / Calendar integration is a Pro feature. Install Form Forge Pro to enable.', 'formforge'); ?>
                                </p>
                            <?php endif; ?>
                        <?php endif; ?>
                    </div>
                </div>

                <!-- HubSpot CRM Card -->
                <div class="<?php echo esc_attr($this->settings_card_class('hubspot')); ?>">
                    <div class="ff-settings-card-head"><span class="dashicons dashicons-businessman"></span><h2><?php esc_html_e('HubSpot CRM', 'formforge'); ?><?php Forge_Docs_Link::render('formforge', 'creating-crm-contacts-with-hubspot-pro', __('HubSpot CRM', 'formforge'), __('Push submissions to HubSpot as contacts/deals via private app key.', 'formforge')); ?></h2></div>
                    <div class="ff-settings-card-body">
                        <p style="margin:0 0 12px;font-size:14px;color:#64748b;"><?php esc_html_e('Send form submissions as leads to your HubSpot CRM. API key is stored securely on the Forge API server.', 'formforge'); ?></p>
                        <?php
                        $hs_status = $integration_license_ready ? $forge_api->hubspot_status() : null;
                        $hs_connected = (!is_wp_error($hs_status) && !empty($hs_status['connected']));
                        // HubSpot connect needs a valid site capability token.
                        // Resolve it up front via the SHARED helper so the Connect
                        // button can be disabled (with an inline reason) when the
                        // token can't be obtained, instead of failing mid-flow with
                        // a generic page-top "Missing site capability token" notice.
                        $hs_site_token = ($integration_license_ready && $this->can_pro_feature('hubspot') && !$hs_connected && class_exists('Forge_Connect'))
                            ? \Forge_Connect::get_or_refresh_site_token('formforge')
                            : '';
                        $hs_can_connect = $integration_license_ready && $this->can_pro_feature('hubspot') && ($hs_connected || $hs_site_token !== '');
                        ?>
                        <?php if ($hs_connected): ?>
                            <div id="ff-hs-connected" style="display:flex;align-items:center;gap:8px;font-size:14px;color:#059669;margin-bottom:12px;">
                                <span class="dashicons dashicons-yes-alt" style="font-size:20px;width:20px;height:20px;color:#059669;"></span>
                                <?php esc_html_e('Connected to HubSpot', 'formforge'); ?>
                            </div>
                            <button type="submit" name="formforge_hubspot_disconnect" value="1" class="button ff-settings-destructive-submit" style="color:#EF4444;"><?php esc_html_e('Disconnect', 'formforge'); ?></button>
                        <?php else: ?>
                            <?php if (!$integration_license_ready): ?>
                                <p style="margin:0 0 12px;font-size:14px;color:#b45309;"><?php
                                if (FORMFORGE_License::instance()->is_pro()) {
                                    esc_html_e('Connect Form Forge to your Avakode license before connecting HubSpot.', 'formforge');
                                } else {
                                    esc_html_e('HubSpot CRM integration is a Pro feature. Install Form Forge Pro to enable.', 'formforge');
                                }
                                ?></p>
                            <?php elseif ($hs_site_token === ''): ?>
                                <p style="margin:0 0 12px;font-size:14px;color:#b45309;"><?php esc_html_e('Could not verify your site capability token. Open the Form Forge license page, reconnect to Avakode, then return here.', 'formforge'); ?></p>
                            <?php endif; ?>
                            <div style="margin-bottom:12px;">
                                <label class="ff-settings-label" for="ff-hs-api-key"><?php esc_html_e('Private App Access Token', 'formforge'); ?></label>
                                <div style="display:flex;gap:8px;">
                                    <input type="text" id="ff-hs-api-key" name="hubspot_api_key" class="ff-settings-input" placeholder="pat-na1-..." style="flex:1;" <?php disabled(!$hs_can_connect); ?>>
                                    <button type="button" id="ff-hs-connect" class="button button-primary" <?php disabled(!$hs_can_connect); ?>><?php esc_html_e('Connect', 'formforge'); ?></button>
                                </div>
                                <!-- connect result renders INLINE here, in the HubSpot card. -->
                                <div id="ff-hs-connect-msg" style="display:none;margin-top:10px;font-size:14px;"></div>
                            </div>
                            <p style="margin:0;font-size:14px;color:#94a3b8;"><?php esc_html_e('Create a Private App at', 'formforge'); ?> <a href="https://app.hubspot.com/private-apps/" target="_blank">HubSpot &rarr; Settings &rarr; Private Apps</a>. <?php esc_html_e('Requires scopes: crm.objects.contacts.write, crm.objects.deals.write. Requires Pro license.', 'formforge'); ?></p>
                            <?php if ($hs_can_connect): ?>
                            <script>
                            (function(){
                                var btn   = document.getElementById('ff-hs-connect');
                                var input = document.getElementById('ff-hs-api-key');
                                var msg   = document.getElementById('ff-hs-connect-msg');
                                if (!btn || !input || !msg) return;
                                var idleLabel = btn.textContent;
                                function showMsg(text, ok){
                                    msg.style.display = 'block';
                                    msg.style.color = ok ? '#059669' : '#ef4444';
                                    msg.textContent = text;
                                }
	                                btn.addEventListener('click', function(){
	                                    if (window.FormForgeSettingsGuard && !window.FormForgeSettingsGuard.confirmAction()) {
	                                        return;
	                                    }
	                                    var key = (input.value || '').trim();
	                                    if (key === '') {
	                                        showMsg(<?php echo wp_json_encode(__('Enter your HubSpot private app token.', 'formforge')); ?>, false);
                                        input.focus();
                                        return;
                                    }
                                    btn.disabled = true;
                                    btn.textContent = <?php echo wp_json_encode(__('Connecting…', 'formforge')); ?>;
                                    msg.style.display = 'none';
                                    var fd = new FormData();
                                    fd.append('action', 'formforge_hubspot_connect');
                                    fd.append('_wpnonce', <?php echo wp_json_encode(wp_create_nonce('formforge_hubspot_connect')); ?>);
                                    fd.append('hubspot_api_key', key);
                                    fetch(formforgeAdmin.ajaxUrl, { method: 'POST', body: fd, credentials: 'same-origin' })
                                        .then(function(r){ return r.json().then(function(j){ return { ok: r.ok, body: j }; }); })
                                        .then(function(res){
                                            var data = (res.body && res.body.data) || {};
                                            if (res.body && res.body.success) {
                                                showMsg(data.message || <?php echo wp_json_encode(__('Connected to HubSpot', 'formforge')); ?>, true);
	                                                btn.textContent = <?php echo wp_json_encode(__('Connected', 'formforge')); ?>;
	                                                input.disabled = true;
	                                                if (window.FormForgeSettingsGuard) {
	                                                    window.FormForgeSettingsGuard.markClean();
	                                                }
	                                                // Reload so the card flips to the connected state + Disconnect button.
	                                                setTimeout(function(){ window.location.reload(); }, 900);
                                            } else {
                                                showMsg(data.message || <?php echo wp_json_encode(__('Could not connect to HubSpot. Check the private app token and try again.', 'formforge')); ?>, false);
                                                btn.disabled = false;
                                                btn.textContent = idleLabel;
                                            }
                                        })
                                        .catch(function(){
                                            showMsg(<?php echo wp_json_encode(__('Network error — please try again.', 'formforge')); ?>, false);
                                            btn.disabled = false;
                                            btn.textContent = idleLabel;
                                        });
                                });
                            })();
                            </script>
                            <?php endif; ?>
                        <?php endif; ?>
                    </div>
                </div>

                <!-- Email Notifications Card -->
                <div class="ff-settings-card">
                    <div class="ff-settings-card-head"><span class="dashicons dashicons-email-alt"></span><h2><?php esc_html_e('Email Notifications', 'formforge'); ?><?php Forge_Docs_Link::render('formforge', 'email-notifications', __('Email notifications', 'formforge'), __('From/To/CC, autoresponders, placeholders — all options explained.', 'formforge')); ?></h2></div>
                    <div class="ff-settings-card-body">
                        <div class="ff-settings-row" style="margin-bottom:16px;">
                            <label class="ff-settings-label"><?php esc_html_e('Default notification email', 'formforge'); ?></label>
                            <input type="email" name="formforge_settings[default_notification_email]"
                                   value="<?php echo esc_attr($s['default_notification_email'] ?? get_option('admin_email')); ?>"
                                   class="ff-settings-input">
                            <p class="description" style="margin:4px 0 0;font-size:14px;color:#94a3b8;"><?php esc_html_e('Used when no notification email is set per form', 'formforge'); ?></p>
                        </div>
                        <div class="ff-settings-row">
                            <label class="ff-settings-label"><?php esc_html_e('Email sender name', 'formforge'); ?></label>
                            <input type="text" name="formforge_settings[email_sender_name]"
                                   value="<?php echo esc_attr($s['email_sender_name'] ?? get_bloginfo('name')); ?>"
                                   class="ff-settings-input">
                        </div>
                    </div>
                </div>

                </div>

                <div style="margin-top:16px;">
                    <button type="submit" name="formforge_save_settings" class="ff-settings-save"><?php esc_html_e('Save Settings', 'formforge'); ?></button>
                </div>
                <div class="ff-settings-dirty-bar" id="ff-settings-dirty-bar" role="status" aria-live="polite">
                    <span class="ff-settings-dirty-copy"><?php esc_html_e('You have unsaved settings changes.', 'formforge'); ?></span>
                    <button type="button" class="button" id="ff-settings-reset"><?php esc_html_e('Reset', 'formforge'); ?></button>
                    <button type="submit" name="formforge_save_settings" class="ff-settings-save ff-settings-save--sticky"><?php esc_html_e('Save Settings', 'formforge'); ?></button>
                </div>
            </form>
        </div>

        <script>
        (function(){
            var form = document.getElementById('formforge-global-settings-form');
            var bar = document.getElementById('ff-settings-dirty-bar');
            var reset = document.getElementById('ff-settings-reset');
            if (!form || !bar) return;
            var dirty = false;
            var allowGuardedSubmitOnce = false;
	            var initial = new FormData(form);
	            var leaveMessage = <?php echo wp_json_encode(__('You have unsaved settings changes. Leave this page?', 'formforge')); ?>;
	            function formSnapshot() {
	                var data = new FormData(form);
	                var pairs = [];
                data.forEach(function(value, key) {
                    if (key === 'formforge_nonce' || key === '_wp_http_referer') return;
                    pairs.push(key + '=' + String(value));
                });
                return pairs.sort().join('&');
            }
            var initialSnapshot = formSnapshot();
	            function setDirty(next) {
	                dirty = !!next;
	                bar.classList.toggle('is-visible', dirty);
	            }
	            function confirmIfDirty() {
	                return !dirty || window.confirm(leaveMessage);
	            }
	            function markClean() {
	                initial = new FormData(form);
	                initialSnapshot = formSnapshot();
	                setDirty(false);
	            }
	            form.addEventListener('input', function(){ setDirty(formSnapshot() !== initialSnapshot); });
	            form.addEventListener('change', function(){ setDirty(formSnapshot() !== initialSnapshot); });
	            form.addEventListener('submit', function(e){
	                var submitter = e.submitter || document.activeElement;
	                var submitterName = submitter && submitter.name ? submitter.name : '';
	                if (submitterName === 'formforge_save_settings') {
	                    setDirty(false);
	                    return;
	                }
	                if (allowGuardedSubmitOnce) {
	                    allowGuardedSubmitOnce = false;
	                    setDirty(false);
	                    return;
	                }
	                if (!confirmIfDirty()) {
	                    e.preventDefault();
	                    return;
	                }
	                setDirty(false);
	            });
	            form.addEventListener('click', function(e) {
	                var guarded = e.target && e.target.closest
	                    ? e.target.closest('.ff-settings-destructive-submit, button[type="submit"][name^="formforge_"][name$="_disconnect"]')
	                    : null;
	                if (!guarded || !form.contains(guarded)) return;
	                if (!confirmIfDirty()) {
	                    e.preventDefault();
	                    e.stopPropagation();
	                    if (typeof e.stopImmediatePropagation === 'function') {
	                        e.stopImmediatePropagation();
	                    }
	                    return;
	                }
	                allowGuardedSubmitOnce = true;
	            }, true);
            if (reset) {
                reset.addEventListener('click', function(){
                    Array.from(form.elements).forEach(function(el) {
                        if (!el.name || el.name === 'formforge_nonce' || el.name === '_wp_http_referer') return;
                        if (el.type === 'checkbox' || el.type === 'radio') {
                            var values = initial.getAll(el.name);
                            el.checked = values.indexOf(el.value) !== -1;
                        } else {
                            var v = initial.get(el.name);
                            el.value = v == null ? '' : v;
                        }
                    });
	                    markClean();
	                });
	            }
	            window.FormForgeSettingsGuard = {
	                isDirty: function(){ return dirty; },
	                confirmAction: confirmIfDirty,
	                markClean: markClean
	            };
	            window.addEventListener('beforeunload', function(e) {
	                if (!dirty) return;
	                e.preventDefault();
                e.returnValue = '';
            });
            document.addEventListener('click', function(e) {
                if (!dirty) return;
	                var link = e.target.closest ? e.target.closest('a[href]') : null;
	                if (!link || link.target === '_blank') return;
	                if (link.href.indexOf('#') === link.href.length - 1) return;
	                if (!confirmIfDirty()) {
	                    e.preventDefault();
	                    return;
	                }
	                setDirty(false);
	            }, true);
        })();
        </script>
        <?php
    }

    /**
 * Form Analytics page. Lists every form with view / submit / abandon
 * counts plus conversion rate, pulled from `wp_formforge_analytics`
 * via FORMFORGE_Analytics::get_stats. PRO-gated; non-PRO users see
 * the standard upgrade banner.
 */
    public function render_analytics() {
        global $wpdb;
        $forms = FORMFORGE_Form_Builder::instance()->get_all();
        $is_pro = FORMFORGE_License::instance()->can('analytics');

        // Range selector (7 / 30 / 90 days). Default 30.
	        $days = intval($_GET['days'] ?? 30); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin UI state, no data mutation.
	        if (!in_array($days, [7, 30, 90], true)) $days = 30;

	        if (!$is_pro) {
	            ?>
	            <div class="wrap" style="max-width:900px;">
	                <?php FORMFORGE_Admin_Tabs::render('formforge-analytics'); ?>
	                <?php FORMFORGE_License::instance()->render_upgrade_banner(); ?>
	                <div style="display:flex;align-items:center;gap:8px;margin-bottom:20px;padding-bottom:14px;border-bottom:2px solid #3B82F6;">
	                    <span class="dashicons dashicons-chart-bar" style="color:#3B82F6;font-size:24px;width:24px;height:24px;"></span>
	                    <div style="margin:0;font-size:22px;font-weight:700;"><?php esc_html_e('Form Analytics', 'formforge'); ?></div>
	                    <?php if (class_exists('Forge_Docs_Link')) Forge_Docs_Link::render('formforge', 'form-analytics-pro', __('Form Analytics', 'formforge'), __('Views, submissions, conversion and abandonment per form.', 'formforge')); ?>
	                </div>
	                <div style="background:#fff7ed;border:1px solid #fed7aa;color:#9a3412;border-radius:10px;padding:18px 20px;margin-bottom:20px;">
	                    <strong style="display:block;margin-bottom:6px;"><?php esc_html_e('Form Analytics is a PRO feature.', 'formforge'); ?></strong>
	                    <span><?php esc_html_e('Upgrade to see per-form views, submissions, conversion rate and abandonment counts.', 'formforge'); ?></span>
	                </div>
	            </div>
	            <?php
	            return;
	        }

	        // Per-form abandonment count (separate query, not in get_stats).
        $analytics_table = $wpdb->prefix . 'formforge_analytics';
        $since = gmdate('Y-m-d H:i:s', strtotime("-{$days} days"));
        // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- table name from $wpdb->prefix, value is %s-prepared; admin read
        $abandon_rows = $wpdb->get_results($wpdb->prepare(
            "SELECT form_id, COUNT(*) AS abandons FROM $analytics_table WHERE event_type = 'abandon' AND created_at >= %s GROUP BY form_id",
            $since
        ));
        // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
        $abandons_by_form = [];
        foreach ($abandon_rows as $row) {
            $abandons_by_form[(int) $row->form_id] = (int) $row->abandons;
        }

        // Aggregate totals across all forms.
        $total_views = 0;
        $total_submits = 0;
        $total_abandons = 0;
        $rows = [];
        foreach ($forms as $form) {
            $stats = FORMFORGE_Analytics::instance()->get_stats($form->id, $days);
            $abandons = $abandons_by_form[(int) $form->id] ?? 0;
            $total_views += $stats['views'];
            $total_submits += $stats['submissions'];
            $total_abandons += $abandons;
            $rows[] = [
                'form' => $form,
                'views' => $stats['views'],
                'submissions' => $stats['submissions'],
                'conversion_rate' => $stats['conversion_rate'],
                'abandons' => $abandons,
            ];
        }
        $overall_conversion = $total_views > 0 ? round(($total_submits / $total_views) * 100, 1) : 0;
        ?>
        <div class="wrap" style="max-width:1100px;">
            <?php FORMFORGE_Admin_Tabs::render('formforge-analytics'); ?>
            <?php FORMFORGE_License::instance()->render_upgrade_banner(); ?>

            <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:20px;padding-bottom:14px;border-bottom:2px solid #3B82F6;">
                <div style="margin:0;font-size:22px;font-weight:700;display:flex;align-items:center;gap:8px;">
                    <span class="dashicons dashicons-chart-bar" style="color:#3B82F6;font-size:24px;width:24px;height:24px;"></span>
                    <?php esc_html_e('Form Analytics', 'formforge'); ?>
                    <?php if (class_exists('Forge_Docs_Link')) Forge_Docs_Link::render('formforge', 'form-analytics-pro', __('Form Analytics', 'formforge'), __('Views, submissions, conversion and abandonment per form.', 'formforge')); ?>
                </div>
                <form method="get" style="display:flex;gap:8px;align-items:center;">
                    <input type="hidden" name="page" value="formforge-analytics">
                    <label for="ff-analytics-days" style="font-size:14px;color:#64748b;"><?php esc_html_e('Range', 'formforge'); ?>:</label>
                    <select id="ff-analytics-days" name="days" onchange="this.form.submit()" style="padding:6px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;">
                        <option value="7" <?php selected($days, 7); ?>><?php esc_html_e('Last 7 days', 'formforge'); ?></option>
                        <option value="30" <?php selected($days, 30); ?>><?php esc_html_e('Last 30 days', 'formforge'); ?></option>
                        <option value="90" <?php selected($days, 90); ?>><?php esc_html_e('Last 90 days', 'formforge'); ?></option>
                    </select>
                </form>
            </div>

	            <!-- Aggregate cards -->
            <div style="display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:24px;">
                <div class="ff-analytics-card">
                    <div class="ff-analytics-label"><?php esc_html_e('Total Views', 'formforge'); ?></div>
                    <div class="ff-analytics-value"><?php echo esc_html(number_format_i18n($total_views)); ?></div>
                </div>
                <div class="ff-analytics-card">
                    <div class="ff-analytics-label"><?php esc_html_e('Total Submissions', 'formforge'); ?></div>
                    <div class="ff-analytics-value"><?php echo esc_html(number_format_i18n($total_submits)); ?></div>
                </div>
                <div class="ff-analytics-card">
                    <div class="ff-analytics-label"><?php esc_html_e('Conversion Rate', 'formforge'); ?></div>
                    <div class="ff-analytics-value"><?php echo esc_html($overall_conversion); ?>%</div>
                </div>
                <div class="ff-analytics-card">
                    <div class="ff-analytics-label"><?php esc_html_e('Abandoned', 'formforge'); ?></div>
                    <div class="ff-analytics-value"><?php echo esc_html(number_format_i18n($total_abandons)); ?></div>
                </div>
            </div>

            <!-- Per-form table -->
            <div class="ff-forms-card">
                <table class="ff-forms-table">
                    <thead>
                        <tr>
                            <th><?php esc_html_e('Form', 'formforge'); ?></th>
                            <th style="width:100px;text-align:center;"><?php esc_html_e('Views', 'formforge'); ?></th>
                            <th style="width:120px;text-align:center;"><?php esc_html_e('Submissions', 'formforge'); ?></th>
                            <th style="width:110px;text-align:center;"><?php esc_html_e('Conversion', 'formforge'); ?></th>
                            <th style="width:100px;text-align:center;"><?php esc_html_e('Abandoned', 'formforge'); ?></th>
                            <th style="width:120px;"><?php esc_html_e('Actions', 'formforge'); ?></th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php if (empty($rows)): ?>
                            <tr><td colspan="6" style="text-align:center;padding:40px;color:#94a3b8;">
                                <?php esc_html_e('No forms yet.', 'formforge'); ?>
                            </td></tr>
                        <?php else: foreach ($rows as $r): $f = $r['form']; ?>
                            <tr>
                                <td>
                                    <a href="<?php echo esc_url(admin_url('admin.php?page=formforge-new&form_id=' . $f->id)); ?>" class="ff-form-title"><?php echo esc_html($f->title); ?></a>
                                    <div class="ff-form-meta">ID: <?php echo esc_html($f->id); ?></div>
                                </td>
                                <td style="text-align:center;"><span class="ff-stat"><?php echo esc_html(number_format_i18n($r['views'])); ?></span></td>
                                <td style="text-align:center;"><span class="ff-stat"><?php echo esc_html(number_format_i18n($r['submissions'])); ?></span></td>
                                <td style="text-align:center;"><span class="ff-stat"><?php echo esc_html($r['conversion_rate']); ?>%</span></td>
                                <td style="text-align:center;"><span class="ff-stat"><?php echo esc_html(number_format_i18n($r['abandons'])); ?></span></td>
                                <td>
                                    <div class="ff-row-actions">
                                        <a href="<?php echo esc_url(admin_url('admin.php?page=formforge-submissions&form_id=' . $f->id)); ?>" class="ff-row-btn" title="<?php esc_attr_e('Submissions', 'formforge'); ?>"><span class="dashicons dashicons-email"></span></a>
                                        <?php /* secondary entry point — navigates to the
 Submissions page where the full AI Submission
 Analysis widget (button + credit badge + polling
 panel) lives. This is a navigation link, not the
 credited trigger itself, so it carries no badge. */ ?>
                                        <?php if (FORMFORGE_License::instance()->can('submissions_ai')): ?>
                                        <a href="<?php echo esc_url(admin_url('admin.php?page=formforge-submissions&form_id=' . $f->id . '#ff-ai-analysis')); ?>" class="ff-row-btn" title="<?php esc_attr_e('AI Submission Analysis', 'formforge'); ?>"><span class="dashicons dashicons-chart-area"></span></a>
                                        <?php endif; ?>
                                        <a href="<?php echo esc_url(admin_url('admin.php?page=formforge-new&form_id=' . $f->id)); ?>" class="ff-row-btn" title="<?php esc_attr_e('Edit form', 'formforge'); ?>"><span class="dashicons dashicons-edit"></span></a>
                                    </div>
                                </td>
                            </tr>
                        <?php endforeach; endif; ?>
                    </tbody>
                </table>
            </div>
        </div>

        <style>
            .ff-analytics-card{background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:18px 20px;box-shadow:0 1px 3px rgba(0,0,0,0.06);}
            .ff-analytics-label{font-size:14px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:6px;}
            .ff-analytics-value{font-size:28px;font-weight:700;color:#1e293b;}
            @media(max-width:782px){.wrap > div[style*="grid-template-columns:repeat(4"]{grid-template-columns:1fr 1fr !important;}}
        </style>
        <?php
    }

    /**
 * Walk every form's settings JSON and reset Google-related keys to
 * their defaults. Called from the Google "Disconnect" handler so a
 * revoked OAuth token doesn't leave stale spreadsheet / calendar
 * selections sitting in the form-builder UI.
 *
 * Returns the number of forms whose settings actually changed (so
 * the admin notice can quote the count).
 */
    private function clear_all_forms_google_settings() {
        global $wpdb;
        $table = $wpdb->prefix . 'formforge_forms';
        $forms = $wpdb->get_results("SELECT id, settings FROM {$table}"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- own table from $wpdb->prefix; one-time/admin read
        if (empty($forms)) return 0;

        $defaults = [
            'gsheets_enabled'           => false,
            'gsheets_spreadsheet_id'    => '',
            'gsheets_sheet_name'        => 'Sheet1',
            'gsheets_spreadsheet_title' => '',
            'calendar_enabled'          => false,
            'calendar_id'               => '',
            'calendar_name'             => '',
            'calendar_event_title'      => 'Booking: {name}',
            'calendar_work_start'       => '',
            'calendar_work_end'         => '',
            'calendar_slot_duration'    => '',
        ];

        $updated = 0;
        foreach ($forms as $f) {
            $s = json_decode($f->settings, true);
            if (!is_array($s)) continue;
            $changed = false;
            foreach ($defaults as $k => $v) {
                if (!array_key_exists($k, $s)) continue;
                if ($s[$k] !== $v) {
                    $s[$k] = $v;
                    $changed = true;
                }
            }
            if ($changed) {
                $wpdb->update($table, ['settings' => wp_json_encode($s)], ['id' => (int) $f->id]); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- custom plugin table; direct query is intended.
                $updated++;
            }
        }
        return $updated;
    }
}
