<?php
/**
 * Plugin Name: Invalid Traffic
 * Plugin URI: https://invalid-traffic.com/
 * Description: Traffic quality bridge pages and first-party campaign signal tools for WordPress publishers.
 * Version: 0.18.251
 * Requires at least: 6.0
 * Requires PHP: 7.4
 * Author: Invalid Traffic
 * License: GPLv2 or later
 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain: invalid-traffic
 * Domain Path: /languages
 */

if (!defined('ABSPATH')) {
    exit;
}
require_once plugin_dir_path(__FILE__) . 'includes/license-client.php';
final class ITP_Invalid_Traffic_Presell {
    const VERSION = '0.18.251';
    const CPT = 'itp_presell';
    const EVENT_TABLE = 'itp_events';
    const LEAD_TABLE = 'itp_leads';
    const OPT = 'itp_settings';
    const PIXEL_OPT = 'itp_global_pixels';

    private static $instance = null;

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

    private function __construct() {
        add_action('plugins_loaded', [$this, 'load_textdomain']);
        add_filter('plugin_locale', [$this, 'filter_plugin_locale'], 10, 2);
        add_action('admin_init', [$this, 'maybe_start_language_buffer'], 1);
        add_action('init', [$this, 'register_post_type'], 1);
        add_filter('query_vars', [$this, 'add_query_vars']);
        add_filter('post_type_link', [$this, 'clean_bridge_permalink'], 10, 2);
        add_filter('request', [$this, 'route_clean_bridge_request']);
        add_action('template_redirect', [$this, 'redirect_legacy_bridge_url'], -5);
        add_action('template_redirect', [$this, 'force_no_ads_on_bridge_pages'], -20);
        add_filter('script_loader_tag', [$this, 'strip_ad_scripts_on_bridge_pages'], 1, 3);
        add_action('init', [$this, 'maybe_flush_rewrite_once'], 20);
        add_action('admin_menu', [$this, 'admin_menu']);
        add_action('admin_init', [$this, 'maybe_handle_admin_actions'], 0);
        add_action('admin_init', [$this, 'maybe_upgrade']);
        add_action('admin_enqueue_scripts', [$this, 'admin_assets']);
        add_action('wp_ajax_itp_track_event', [$this, 'track_event']);
        add_action('wp_ajax_nopriv_itp_track_event', [$this, 'track_event']);
        add_action('wp_ajax_itp_submit_lead', [$this, 'submit_lead']);
        add_action('wp_ajax_nopriv_itp_submit_lead', [$this, 'submit_lead']);
        add_action('wp_ajax_itp_admin_pixel_test', [$this, 'ajax_admin_pixel_test']);
        add_action('rest_api_init', [$this, 'register_conversion_rest_routes']);
        add_action('wp_head', [$this, 'render_global_pixel_head'], 1);
        add_action('wp_footer', [$this, 'render_global_pixel_footer'], 999);
        add_shortcode('invalid_traffic_vsl', [$this, 'shortcode_vsl_player']);
        add_action('wp_ajax_itp_validate_bridge', [$this, 'ajax_validate_bridge']);
        add_action('wp_ajax_itp_reorder_bridges', [$this, 'ajax_reorder_bridges']);
        add_action('itp_daily_cleanup', [$this, 'cleanup_old_events']);
        add_action('template_redirect', [$this, 'render_vsl_player_js_endpoint'], -2);
        add_action('template_redirect', [$this, 'render_vsl_embed_endpoint'], -1);
        add_action('template_redirect', [$this, 'render_bridge_template'], 0);
        add_filter('manage_' . self::CPT . '_posts_columns', [$this, 'cpt_columns']);
        add_action('manage_' . self::CPT . '_posts_custom_column', [$this, 'cpt_column_content'], 10, 2);
        add_filter('post_row_actions', [$this, 'cpt_row_actions'], 10, 2);
        add_action('admin_init', 'it_maybe_auto_check_license');
    }

    public function load_textdomain() {
        load_plugin_textdomain('invalid-traffic', false, dirname(plugin_basename(__FILE__)) . '/languages');
    }

    public function filter_plugin_locale($locale, $domain) {
        if ($domain !== 'invalid-traffic') {
            return $locale;
        }
        $settings = get_option(self::OPT, []);
        if (!is_array($settings)) {
            return $locale;
        }
        $selected = $this->sanitize_plugin_language($settings['plugin_language'] ?? 'auto');
        if ($selected === '' || $selected === 'auto') {
            return $locale;
        }
        return $selected;
    }

    public static function plugin_language_choices() {
        return [
            'auto' => 'Auto - Use WordPress language',
            'en_US' => 'English',
            'pt_BR' => 'Português do Brasil',
            'pt_PT' => 'Português (Portugal)',
            'es_ES' => 'Español',
            'fr_FR' => 'Français',
            'de_DE' => 'Deutsch',
            'it_IT' => 'Italiano',
            'nl_NL' => 'Nederlands',
            'pl_PL' => 'Polski',
        ];
    }

    private function supported_plugin_locale($locale) {
        $locale = (string) $locale;
        $normalized = strtolower(str_replace('-', '_', $locale));

        if ($normalized === '' || strpos($normalized, 'en') === 0) {
            return 'en_US';
        }
        if ($normalized === 'pt_br') {
            return 'pt_BR';
        }
        if ($normalized === 'pt_pt') {
            return 'pt_PT';
        }
        if (strpos($normalized, 'es') === 0) {
            return 'es_ES';
        }
        if (strpos($normalized, 'fr') === 0) {
            return 'fr_FR';
        }
        if (strpos($normalized, 'de') === 0) {
            return 'de_DE';
        }
        if (strpos($normalized, 'it') === 0) {
            return 'it_IT';
        }
        if (strpos($normalized, 'nl') === 0) {
            return 'nl_NL';
        }
        if (strpos($normalized, 'pl') === 0) {
            return 'pl_PL';
        }
        return 'en_US';
    }

    private function selected_plugin_locale() {
        $settings = get_option(self::OPT, []);
        if (!is_array($settings)) {
            $settings = [];
        }
        $selected = $this->sanitize_plugin_language($settings['plugin_language'] ?? 'auto');
        if ($selected === '' || $selected === 'auto') {
            $locale = function_exists('determine_locale') ? determine_locale() : get_locale();
            return $this->supported_plugin_locale($locale);
        }
        return $this->supported_plugin_locale($selected);
    }

    public function maybe_start_language_buffer() {
        if (!is_admin() || !current_user_can('manage_options')) {
            return;
        }
        $page = isset($_GET['page']) ? sanitize_key((string) wp_unslash($_GET['page'])) : '';
        if (strpos($page, 'itp-') !== 0) {
            return;
        }
        $locale = $this->selected_plugin_locale();
        if (strpos($locale, 'en_') === 0) {
            return;
        }
        ob_start([$this, 'translate_admin_output']);
    }

    public function translate_admin_output($html) {
        $locale = $this->selected_plugin_locale();
        $map = $this->admin_translation_map($locale);
        if (!$map) {
            return $html;
        }
        // Longest phrases first prevents partial replacements from breaking longer UI labels.
        uksort($map, static function($a, $b) { return strlen($b) <=> strlen($a); });
        return strtr($html, $map);
    }

    private function admin_translation_map($locale) {
        $base = [
            'pt' => [
                'Dashboard' => 'Painel',
                'Live View' => 'Ao vivo',
                'My Bridges' => 'Minhas Bridges',
                'Create Bridge' => 'Criar Bridge',
                'Analytics' => 'Análises',
                'Reports' => 'Relatórios',
                'Settings' => 'Configurações',
                'License' => 'Licença',
                'Traffic Command Console' => 'Console de Comando de Tráfego',
                'Conversions' => 'Conversões',
                'Revenue' => 'Receita',
                'Visits' => 'Visitas',
                'Clicks' => 'Cliques',
                'Risk' => 'Risco',
                'Quality' => 'Qualidade',
                'Save Settings' => 'Salvar configurações',
                'Save pixel setup' => 'Salvar configuração de pixel',
                'Language & localization' => 'Idioma e localização',
                'Plugin language' => 'Idioma do plugin',
                'Auto - Use WordPress language' => 'Automático - usar idioma do WordPress',
                'Risk rules' => 'Regras de risco',
                'Emergency mode' => 'Modo de emergência',
                'Enable Emergency Mode' => 'Ativar modo de emergência',
                'Global Pixel Setup' => 'Configuração global de pixels',
                'Global platform pixels' => 'Pixels globais de plataformas',
                'Connect confirmed sales / revenue' => 'Conectar vendas confirmadas / receita',
                'Your universal postback URL' => 'Sua URL universal de postback',
                'Copy universal postback URL' => 'Copiar URL universal de postback',
                'One-line thank-you pixel' => 'Pixel de obrigado em uma linha',
                'Copy one-line pixel' => 'Copiar pixel de uma linha',
                'Send demo purchase' => 'Enviar compra de demonstração',
                'Conversion delivery log' => 'Log de entrega de conversões',
                'Show log' => 'Mostrar log',
                'Open' => 'Abrir',
                'Close' => 'Fechar',
                'Copy' => 'Copiar',
                'Advanced' => 'Avançado',
                'Advanced mapping example' => 'Exemplo avançado de mapeamento',
                'Supported adapter presets' => 'Presets de adaptadores compatíveis',
                'Public bridge URL format' => 'Formato da URL pública da bridge',
                'Brand name inside admin panel' => 'Nome da marca no painel admin',
                'Default button delay in seconds' => 'Atraso padrão do botão em segundos',
                'Engaged after seconds' => 'Engajado após segundos',
                'Engaged after scroll %' => 'Engajado após rolagem %',
                'Fast click threshold seconds' => 'Limite de clique rápido em segundos',
                'Repeated visit limit' => 'Limite de visitas repetidas',
                'Repeated visit window minutes' => 'Janela de visitas repetidas em minutos',
                'Data retention days' => 'Dias de retenção de dados',
                'Clear old logs now' => 'Limpar logs antigos agora',
                'Value' => 'Valor',
                'Currency' => 'Moeda',
                'Source' => 'Origem',
                'Event' => 'Evento',
                'External ID' => 'ID externo',
                'Message' => 'Mensagem',
                'Counted?' => 'Contado?',
                'Matched adapter' => 'Adaptador encontrado',
                'Received but not mapped' => 'Recebido, mas não mapeado',
                'Conversion recorded' => 'Conversão registrada',
                'Duplicate skipped' => 'Duplicado ignorado',
                'Test received' => 'Teste recebido',
                'Missing value' => 'Valor ausente',
                'Missing attribution' => 'Atribuição ausente',
                'Open selected bridge analytics' => 'Abrir análises da bridge selecionada',
                'This changes fixed plugin interface text. Bridge headlines, buttons, VSL copy, card text and CTA messages stay controlled by the text you write inside each bridge.' => 'Isso muda os textos fixos da interface do plugin. Headline, botões, textos do VSL, cards e CTAs das bridges continuam controlados pelo texto que você escreve dentro de cada bridge.',
                'Default is Auto, which follows the WordPress site/admin language. Built-in core UI translations are active, and external language packs can still be added in /languages.' => 'O padrão é Automático, seguindo o idioma do site/admin do WordPress. As traduções internas principais estão ativas e pacotes externos ainda podem ser adicionados em /languages.',
                'Keep it simple. Change brand details, button delay and basic compliance pages. Bridge URLs use the clean root format: domain.com/slug/.' => 'Mantenha simples. Altere detalhes da marca, atraso do botão e páginas básicas de conformidade. As URLs das bridges usam o formato limpo: dominio.com/slug/.',
                'Traffic logs older than the retention window are cleaned automatically once per day. Keep this shorter for high-volume paid traffic sites.' => 'Logs de tráfego mais antigos que a janela de retenção são limpos automaticamente uma vez por dia. Use um período menor em sites com alto volume de tráfego pago.',
            ],
            'es' => [
                'Dashboard' => 'Panel',
                'Live View' => 'Vista en vivo',
                'My Bridges' => 'Mis bridges',
                'Create Bridge' => 'Crear bridge',
                'Analytics' => 'Analíticas',
                'Pixels' => 'Píxeles',
                'Reports' => 'Informes',
                'Settings' => 'Ajustes',
                'License' => 'Licencia',
                'Traffic Command Console' => 'Consola de comando de tráfico',
                'Conversions' => 'Conversiones',
                'Revenue' => 'Ingresos',
                'Visits' => 'Visitas',
                'Clicks' => 'Clics',
                'Risk' => 'Riesgo',
                'Quality' => 'Calidad',
                'Save Settings' => 'Guardar ajustes',
                'Save pixel setup' => 'Guardar configuración de píxeles',
                'Language & localization' => 'Idioma y localización',
                'Plugin language' => 'Idioma del plugin',
                'Auto - Use WordPress language' => 'Automático - usar idioma de WordPress',
                'Risk rules' => 'Reglas de riesgo',
                'Emergency mode' => 'Modo de emergencia',
                'Enable Emergency Mode' => 'Activar modo de emergencia',
                'Global Pixel Setup' => 'Configuración global de píxeles',
                'Global platform pixels' => 'Píxeles globales de plataformas',
                'Connect confirmed sales / revenue' => 'Conectar ventas confirmadas / ingresos',
                'Your universal postback URL' => 'Tu URL universal de postback',
                'Copy universal postback URL' => 'Copiar URL universal de postback',
                'One-line thank-you pixel' => 'Píxel de agradecimiento de una línea',
                'Copy one-line pixel' => 'Copiar píxel de una línea',
                'Send demo purchase' => 'Enviar compra de prueba',
                'Conversion delivery log' => 'Registro de entrega de conversiones',
                'Show log' => 'Mostrar registro',
                'Open' => 'Abrir',
                'Close' => 'Cerrar',
                'Copy' => 'Copiar',
                'Advanced' => 'Avanzado',
                'Public bridge URL format' => 'Formato de URL pública de bridge',
                'Brand name inside admin panel' => 'Nombre de marca en el panel admin',
                'Default button delay in seconds' => 'Retraso predeterminado del botón en segundos',
                'Engaged after seconds' => 'Enganchado después de segundos',
                'Engaged after scroll %' => 'Enganchado después de scroll %',
                'Fast click threshold seconds' => 'Umbral de clic rápido en segundos',
                'Repeated visit limit' => 'Límite de visitas repetidas',
                'Repeated visit window minutes' => 'Ventana de visitas repetidas en minutos',
                'Data retention days' => 'Días de retención de datos',
                'Clear old logs now' => 'Borrar registros antiguos ahora',
                'Value' => 'Valor',
                'Currency' => 'Moneda',
                'Source' => 'Fuente',
                'Status' => 'Estado',
                'Event' => 'Evento',
                'External ID' => 'ID externo',
                'Message' => 'Mensaje',
                'Counted?' => '¿Contado?',
                'Matched adapter' => 'Adaptador detectado',
                'Received but not mapped' => 'Recibido pero no mapeado',
                'Conversion recorded' => 'Conversión registrada',
                'Duplicate skipped' => 'Duplicado omitido',
                'Test received' => 'Prueba recibida',
                'Missing value' => 'Falta valor',
                'Missing attribution' => 'Falta atribución',
                'Open selected bridge analytics' => 'Abrir analíticas de la bridge seleccionada',
            ],
            'fr' => [
                'Dashboard' => 'Tableau de bord',
                'Live View' => 'Vue en direct',
                'My Bridges' => 'Mes bridges',
                'Create Bridge' => 'Créer une bridge',
                'Analytics' => 'Analyses',
                'Reports' => 'Rapports',
                'Settings' => 'Réglages',
                'License' => 'Licence',
                'Traffic Command Console' => 'Console de commande du trafic',
                'Revenue' => 'Revenus',
                'Visits' => 'Visites',
                'Clicks' => 'Clics',
                'Risk' => 'Risque',
                'Quality' => 'Qualité',
                'Save Settings' => 'Enregistrer les réglages',
                'Save pixel setup' => 'Enregistrer la configuration pixel',
                'Language & localization' => 'Langue et localisation',
                'Plugin language' => 'Langue du plugin',
                'Auto - Use WordPress language' => 'Auto - utiliser la langue de WordPress',
                'Risk rules' => 'Règles de risque',
                'Emergency mode' => 'Mode d’urgence',
                'Enable Emergency Mode' => 'Activer le mode d’urgence',
                'Global Pixel Setup' => 'Configuration globale des pixels',
                'Global platform pixels' => 'Pixels globaux des plateformes',
                'Connect confirmed sales / revenue' => 'Connecter les ventes confirmées / revenus',
                'Your universal postback URL' => 'Votre URL postback universelle',
                'Copy universal postback URL' => 'Copier l’URL postback universelle',
                'One-line thank-you pixel' => 'Pixel de remerciement en une ligne',
                'Copy one-line pixel' => 'Copier le pixel une ligne',
                'Send demo purchase' => 'Envoyer un achat de test',
                'Conversion delivery log' => 'Journal de livraison des conversions',
                'Show log' => 'Afficher le journal',
                'Open' => 'Ouvrir',
                'Close' => 'Fermer',
                'Copy' => 'Copier',
                'Advanced' => 'Avancé',
                'Public bridge URL format' => 'Format d’URL publique de la bridge',
                'Brand name inside admin panel' => 'Nom de marque dans le panneau admin',
                'Default button delay in seconds' => 'Délai par défaut du bouton en secondes',
                'Engaged after seconds' => 'Engagé après secondes',
                'Engaged after scroll %' => 'Engagé après défilement %',
                'Fast click threshold seconds' => 'Seuil de clic rapide en secondes',
                'Repeated visit limit' => 'Limite de visites répétées',
                'Repeated visit window minutes' => 'Fenêtre de visites répétées en minutes',
                'Data retention days' => 'Jours de conservation des données',
                'Clear old logs now' => 'Effacer les anciens journaux',
                'Value' => 'Valeur',
                'Currency' => 'Devise',
                'Status' => 'Statut',
                'Event' => 'Événement',
                'External ID' => 'ID externe',
                'Counted?' => 'Compté ?',
                'Matched adapter' => 'Adaptateur détecté',
                'Received but not mapped' => 'Reçu mais non mappé',
                'Conversion recorded' => 'Conversion enregistrée',
                'Duplicate skipped' => 'Doublon ignoré',
                'Test received' => 'Test reçu',
                'Missing value' => 'Valeur manquante',
                'Missing attribution' => 'Attribution manquante',
                'Open selected bridge analytics' => 'Ouvrir les analyses de la bridge sélectionnée',
            ],
            'de' => [
                'Live View' => 'Live-Ansicht',
                'My Bridges' => 'Meine Bridges',
                'Create Bridge' => 'Bridge erstellen',
                'Analytics' => 'Analysen',
                'Pixels' => 'Pixel',
                'Reports' => 'Berichte',
                'Settings' => 'Einstellungen',
                'License' => 'Lizenz',
                'Traffic Command Console' => 'Traffic-Kommandozentrale',
                'Revenue' => 'Umsatz',
                'Visits' => 'Besuche',
                'Clicks' => 'Klicks',
                'Risk' => 'Risiko',
                'Quality' => 'Qualität',
                'Save Settings' => 'Einstellungen speichern',
                'Save pixel setup' => 'Pixel-Einstellungen speichern',
                'Language & localization' => 'Sprache und Lokalisierung',
                'Plugin language' => 'Plugin-Sprache',
                'Auto - Use WordPress language' => 'Automatisch - WordPress-Sprache verwenden',
                'Risk rules' => 'Risikoregeln',
                'Emergency mode' => 'Notfallmodus',
                'Enable Emergency Mode' => 'Notfallmodus aktivieren',
                'Global Pixel Setup' => 'Globale Pixel-Einrichtung',
                'Global platform pixels' => 'Globale Plattform-Pixel',
                'Connect confirmed sales / revenue' => 'Bestätigte Verkäufe / Umsatz verbinden',
                'Your universal postback URL' => 'Ihre universelle Postback-URL',
                'Copy universal postback URL' => 'Universelle Postback-URL kopieren',
                'One-line thank-you pixel' => 'Einzeiliger Thank-you-Pixel',
                'Copy one-line pixel' => 'Einzeiligen Pixel kopieren',
                'Send demo purchase' => 'Testkauf senden',
                'Conversion delivery log' => 'Conversion-Zustellungsprotokoll',
                'Show log' => 'Log anzeigen',
                'Open' => 'Öffnen',
                'Close' => 'Schließen',
                'Copy' => 'Kopieren',
                'Advanced' => 'Erweitert',
                'Public bridge URL format' => 'Öffentliches Bridge-URL-Format',
                'Brand name inside admin panel' => 'Markenname im Admin-Bereich',
                'Default button delay in seconds' => 'Standard-Button-Verzögerung in Sekunden',
                'Engaged after seconds' => 'Engagiert nach Sekunden',
                'Engaged after scroll %' => 'Engagiert nach Scroll %',
                'Fast click threshold seconds' => 'Schwelle für schnellen Klick in Sekunden',
                'Repeated visit limit' => 'Limit für wiederholte Besuche',
                'Repeated visit window minutes' => 'Fenster für wiederholte Besuche in Minuten',
                'Data retention days' => 'Datenaufbewahrung in Tagen',
                'Clear old logs now' => 'Alte Logs jetzt löschen',
                'Value' => 'Wert',
                'Currency' => 'Währung',
                'Source' => 'Quelle',
                'Event' => 'Ereignis',
                'External ID' => 'Externe ID',
                'Message' => 'Nachricht',
                'Counted?' => 'Gezählt?',
                'Matched adapter' => 'Adapter erkannt',
                'Received but not mapped' => 'Empfangen, aber nicht zugeordnet',
                'Conversion recorded' => 'Conversion aufgezeichnet',
                'Duplicate skipped' => 'Duplikat übersprungen',
                'Test received' => 'Test empfangen',
                'Missing value' => 'Wert fehlt',
                'Missing attribution' => 'Attribution fehlt',
                'Open selected bridge analytics' => 'Analysen der ausgewählten Bridge öffnen',
            ],
            'it' => [
                'Dashboard' => 'Bacheca',
                'Live View' => 'Vista live',
                'My Bridges' => 'Le mie bridge',
                'Create Bridge' => 'Crea bridge',
                'Analytics' => 'Analisi',
                'Leads' => 'Lead',
                'Pixels' => 'Pixel',
                'Reports' => 'Report',
                'Settings' => 'Impostazioni',
                'License' => 'Licenza',
                'Traffic Command Console' => 'Console comando traffico',
                'Conversions' => 'Conversioni',
                'Revenue' => 'Ricavi',
                'Visits' => 'Visite',
                'Clicks' => 'Clic',
                'Risk' => 'Rischio',
                'Quality' => 'Qualità',
                'Save Settings' => 'Salva impostazioni',
                'Save pixel setup' => 'Salva configurazione pixel',
                'Language & localization' => 'Lingua e localizzazione',
                'Plugin language' => 'Lingua del plugin',
                'Auto - Use WordPress language' => 'Auto - usa la lingua di WordPress',
                'Risk rules' => 'Regole di rischio',
                'Emergency mode' => 'Modalità emergenza',
                'Enable Emergency Mode' => 'Attiva modalità emergenza',
                'Global Pixel Setup' => 'Configurazione pixel globale',
                'Global platform pixels' => 'Pixel globali delle piattaforme',
                'Connect confirmed sales / revenue' => 'Collega vendite confermate / ricavi',
                'Your universal postback URL' => 'Il tuo URL postback universale',
                'Copy universal postback URL' => 'Copia URL postback universale',
                'One-line thank-you pixel' => 'Pixel thank-you in una riga',
                'Copy one-line pixel' => 'Copia pixel a una riga',
                'Send demo purchase' => 'Invia acquisto demo',
                'Conversion delivery log' => 'Log consegna conversioni',
                'Show log' => 'Mostra log',
                'Open' => 'Apri',
                'Close' => 'Chiudi',
                'Copy' => 'Copia',
                'Advanced' => 'Avanzato',
                'Public bridge URL format' => 'Formato URL pubblico della bridge',
                'Brand name inside admin panel' => 'Nome brand nel pannello admin',
                'Default button delay in seconds' => 'Ritardo predefinito del pulsante in secondi',
                'Engaged after seconds' => 'Coinvolto dopo secondi',
                'Engaged after scroll %' => 'Coinvolto dopo scroll %',
                'Fast click threshold seconds' => 'Soglia clic rapido in secondi',
                'Repeated visit limit' => 'Limite visite ripetute',
                'Repeated visit window minutes' => 'Finestra visite ripetute in minuti',
                'Data retention days' => 'Giorni conservazione dati',
                'Clear old logs now' => 'Cancella vecchi log ora',
                'Value' => 'Valore',
                'Currency' => 'Valuta',
                'Source' => 'Fonte',
                'Status' => 'Stato',
                'Event' => 'Evento',
                'External ID' => 'ID esterno',
                'Message' => 'Messaggio',
                'Counted?' => 'Conteggiato?',
                'Matched adapter' => 'Adapter rilevato',
                'Received but not mapped' => 'Ricevuto ma non mappato',
                'Conversion recorded' => 'Conversione registrata',
                'Duplicate skipped' => 'Duplicato ignorato',
                'Test received' => 'Test ricevuto',
                'Missing value' => 'Valore mancante',
                'Missing attribution' => 'Attribuzione mancante',
                'Open selected bridge analytics' => 'Apri analisi della bridge selezionata',
            ],
            'nl' => [
                'Live View' => 'Liveweergave',
                'My Bridges' => 'Mijn bridges',
                'Create Bridge' => 'Bridge maken',
                'Analytics' => 'Analyses',
                'Reports' => 'Rapporten',
                'Settings' => 'Instellingen',
                'License' => 'Licentie',
                'Traffic Command Console' => 'Traffic-commandocentrum',
                'Conversions' => 'Conversies',
                'Revenue' => 'Omzet',
                'Visits' => 'Bezoeken',
                'Clicks' => 'Klikken',
                'Risk' => 'Risico',
                'Quality' => 'Kwaliteit',
                'Save Settings' => 'Instellingen opslaan',
                'Save pixel setup' => 'Pixelinstellingen opslaan',
                'Language & localization' => 'Taal en lokalisatie',
                'Plugin language' => 'Plugintaal',
                'Auto - Use WordPress language' => 'Auto - gebruik WordPress-taal',
                'Risk rules' => 'Risicoregels',
                'Emergency mode' => 'Noodmodus',
                'Enable Emergency Mode' => 'Noodmodus inschakelen',
                'Global Pixel Setup' => 'Globale pixelinstellingen',
                'Global platform pixels' => 'Globale platformpixels',
                'Connect confirmed sales / revenue' => 'Bevestigde verkopen / omzet koppelen',
                'Your universal postback URL' => 'Je universele postback-URL',
                'Copy universal postback URL' => 'Universele postback-URL kopiëren',
                'One-line thank-you pixel' => 'Thank-you pixel in één regel',
                'Copy one-line pixel' => 'Eenregelige pixel kopiëren',
                'Send demo purchase' => 'Demo-aankoop verzenden',
                'Conversion delivery log' => 'Conversieleveringslog',
                'Show log' => 'Log tonen',
                'Open' => 'Openen',
                'Close' => 'Sluiten',
                'Copy' => 'Kopiëren',
                'Advanced' => 'Geavanceerd',
                'Public bridge URL format' => 'Publiek bridge-URL-formaat',
                'Brand name inside admin panel' => 'Merknaam in adminpaneel',
                'Default button delay in seconds' => 'Standaard knopvertraging in seconden',
                'Engaged after seconds' => 'Betrokken na seconden',
                'Engaged after scroll %' => 'Betrokken na scroll %',
                'Fast click threshold seconds' => 'Drempel snelle klik in seconden',
                'Repeated visit limit' => 'Limiet herhaalde bezoeken',
                'Repeated visit window minutes' => 'Venster herhaalde bezoeken in minuten',
                'Data retention days' => 'Dagen gegevensbewaring',
                'Clear old logs now' => 'Oude logs nu wissen',
                'Value' => 'Waarde',
                'Currency' => 'Valuta',
                'Source' => 'Bron',
                'Event' => 'Gebeurtenis',
                'External ID' => 'Externe ID',
                'Message' => 'Bericht',
                'Counted?' => 'Geteld?',
                'Matched adapter' => 'Adapter herkend',
                'Received but not mapped' => 'Ontvangen maar niet toegewezen',
                'Conversion recorded' => 'Conversie geregistreerd',
                'Duplicate skipped' => 'Duplicaat overgeslagen',
                'Test received' => 'Test ontvangen',
                'Missing value' => 'Waarde ontbreekt',
                'Missing attribution' => 'Attributie ontbreekt',
                'Open selected bridge analytics' => 'Analyses van geselecteerde bridge openen',
            ],
            'pl' => [
                'Dashboard' => 'Panel',
                'Live View' => 'Widok na żywo',
                'My Bridges' => 'Moje bridge',
                'Create Bridge' => 'Utwórz bridge',
                'Analytics' => 'Analityka',
                'Leads' => 'Leady',
                'Pixels' => 'Piksele',
                'Reports' => 'Raporty',
                'Settings' => 'Ustawienia',
                'License' => 'Licencja',
                'Traffic Command Console' => 'Konsola zarządzania ruchem',
                'Conversions' => 'Konwersje',
                'Revenue' => 'Przychód',
                'Visits' => 'Wizyty',
                'Clicks' => 'Kliknięcia',
                'Risk' => 'Ryzyko',
                'Quality' => 'Jakość',
                'Save Settings' => 'Zapisz ustawienia',
                'Save pixel setup' => 'Zapisz konfigurację pikseli',
                'Language & localization' => 'Język i lokalizacja',
                'Plugin language' => 'Język wtyczki',
                'Auto - Use WordPress language' => 'Auto - użyj języka WordPressa',
                'Risk rules' => 'Reguły ryzyka',
                'Emergency mode' => 'Tryb awaryjny',
                'Enable Emergency Mode' => 'Włącz tryb awaryjny',
                'Global Pixel Setup' => 'Globalna konfiguracja pikseli',
                'Global platform pixels' => 'Globalne piksele platform',
                'Connect confirmed sales / revenue' => 'Połącz potwierdzone sprzedaże / przychód',
                'Your universal postback URL' => 'Twój uniwersalny URL postback',
                'Copy universal postback URL' => 'Kopiuj uniwersalny URL postback',
                'One-line thank-you pixel' => 'Jednoliniowy pixel thank-you',
                'Copy one-line pixel' => 'Kopiuj jednoliniowy pixel',
                'Send demo purchase' => 'Wyślij zakup demo',
                'Conversion delivery log' => 'Log dostarczenia konwersji',
                'Show log' => 'Pokaż log',
                'Open' => 'Otwórz',
                'Close' => 'Zamknij',
                'Copy' => 'Kopiuj',
                'Advanced' => 'Zaawansowane',
                'Public bridge URL format' => 'Format publicznego URL bridge',
                'Brand name inside admin panel' => 'Nazwa marki w panelu admin',
                'Default button delay in seconds' => 'Domyślne opóźnienie przycisku w sekundach',
                'Engaged after seconds' => 'Zaangażowany po sekundach',
                'Engaged after scroll %' => 'Zaangażowany po przewinięciu %',
                'Fast click threshold seconds' => 'Próg szybkiego kliknięcia w sekundach',
                'Repeated visit limit' => 'Limit powtarzanych wizyt',
                'Repeated visit window minutes' => 'Okno powtarzanych wizyt w minutach',
                'Data retention days' => 'Dni przechowywania danych',
                'Clear old logs now' => 'Wyczyść stare logi teraz',
                'Value' => 'Wartość',
                'Currency' => 'Waluta',
                'Source' => 'Źródło',
                'Event' => 'Zdarzenie',
                'External ID' => 'ID zewnętrzne',
                'Message' => 'Wiadomość',
                'Counted?' => 'Policzone?',
                'Matched adapter' => 'Wykryty adapter',
                'Received but not mapped' => 'Odebrano, ale nie zmapowano',
                'Conversion recorded' => 'Konwersja zapisana',
                'Duplicate skipped' => 'Duplikat pominięty',
                'Test received' => 'Test odebrany',
                'Missing value' => 'Brak wartości',
                'Missing attribution' => 'Brak atrybucji',
                'Open selected bridge analytics' => 'Otwórz analitykę wybranej bridge',
            ],
            'ro' => [
                'Dashboard' => 'Tablou de bord',
                'Live View' => 'Vizualizare live',
                'My Bridges' => 'Bridge-urile mele',
                'Create Bridge' => 'Creează bridge',
                'Analytics' => 'Analitice',
                'Leads' => 'Leaduri',
                'Pixels' => 'Pixeli',
                'Reports' => 'Rapoarte',
                'Settings' => 'Setări',
                'License' => 'Licență',
                'Traffic Command Console' => 'Consolă comandă trafic',
                'Conversions' => 'Conversii',
                'Revenue' => 'Venit',
                'Visits' => 'Vizite',
                'Clicks' => 'Clicuri',
                'Risk' => 'Risc',
                'Quality' => 'Calitate',
                'Save Settings' => 'Salvează setările',
                'Save pixel setup' => 'Salvează pixelii',
                'Language & localization' => 'Limbă și localizare',
                'Plugin language' => 'Limba pluginului',
                'Auto - Use WordPress language' => 'Automat - folosește limba WordPress',
                'Risk rules' => 'Reguli de risc',
                'Emergency mode' => 'Mod de urgență',
                'Enable Emergency Mode' => 'Activează modul de urgență',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'sv' => [
                'Dashboard' => 'Instrumentpanel',
                'Live View' => 'Livevy',
                'My Bridges' => 'Mina bridges',
                'Create Bridge' => 'Skapa bridge',
                'Analytics' => 'Analys',
                'Pixels' => 'Pixlar',
                'Reports' => 'Rapporter',
                'Settings' => 'Inställningar',
                'License' => 'Licens',
                'Traffic Command Console' => 'Trafikkommandocentral',
                'Conversions' => 'Konverteringar',
                'Revenue' => 'Intäkt',
                'Visits' => 'Besök',
                'Clicks' => 'Klick',
                'Quality' => 'Kvalitet',
                'Save Settings' => 'Spara inställningar',
                'Save pixel setup' => 'Spara pixelinställning',
                'Language & localization' => 'Språk och lokalisering',
                'Plugin language' => 'Plugin-språk',
                'Auto - Use WordPress language' => 'Auto - använd WordPress-språk',
                'Risk rules' => 'Riskregler',
                'Emergency mode' => 'Nödläge',
                'Enable Emergency Mode' => 'Aktivera nödläge',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'da' => [
                'Dashboard' => 'Kontrolpanel',
                'Live View' => 'Livevisning',
                'My Bridges' => 'Mine bridges',
                'Create Bridge' => 'Opret bridge',
                'Analytics' => 'Analyse',
                'Reports' => 'Rapporter',
                'Settings' => 'Indstillinger',
                'License' => 'Licens',
                'Traffic Command Console' => 'Trafikkommandocenter',
                'Conversions' => 'Konverteringer',
                'Revenue' => 'Omsætning',
                'Visits' => 'Besøg',
                'Clicks' => 'Klik',
                'Risk' => 'Risiko',
                'Quality' => 'Kvalitet',
                'Save Settings' => 'Gem indstillinger',
                'Save pixel setup' => 'Gem pixelopsætning',
                'Language & localization' => 'Sprog og lokalisering',
                'Plugin language' => 'Plugin-sprog',
                'Auto - Use WordPress language' => 'Auto - brug WordPress-sprog',
                'Risk rules' => 'Risikoregler',
                'Emergency mode' => 'Nødtilstand',
                'Enable Emergency Mode' => 'Aktivér nødtilstand',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'fi' => [
                'Dashboard' => 'Hallintapaneeli',
                'Live View' => 'Live-näkymä',
                'My Bridges' => 'Omat bridget',
                'Create Bridge' => 'Luo bridge',
                'Analytics' => 'Analytiikka',
                'Leads' => 'Liidit',
                'Pixels' => 'Pikselit',
                'Reports' => 'Raportit',
                'Settings' => 'Asetukset',
                'License' => 'Lisenssi',
                'Traffic Command Console' => 'Liikenteen komentokeskus',
                'Conversions' => 'Konversiot',
                'Revenue' => 'Tulot',
                'Visits' => 'Vierailut',
                'Clicks' => 'Klikit',
                'Risk' => 'Riski',
                'Quality' => 'Laatu',
                'Save Settings' => 'Tallenna asetukset',
                'Save pixel setup' => 'Tallenna pikseliasetus',
                'Language & localization' => 'Kieli ja lokalisointi',
                'Plugin language' => 'Lisäosan kieli',
                'Auto - Use WordPress language' => 'Automaattinen - käytä WordPressin kieltä',
                'Risk rules' => 'Riskisäännöt',
                'Emergency mode' => 'Hätätila',
                'Enable Emergency Mode' => 'Ota hätätila käyttöön',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'nb' => [
                'Dashboard' => 'Kontrollpanel',
                'Live View' => 'Livevisning',
                'My Bridges' => 'Mine bridges',
                'Create Bridge' => 'Opprett bridge',
                'Analytics' => 'Analyse',
                'Pixels' => 'Piksler',
                'Reports' => 'Rapporter',
                'Settings' => 'Innstillinger',
                'License' => 'Lisens',
                'Traffic Command Console' => 'Trafikk-kommandosenter',
                'Conversions' => 'Konverteringer',
                'Revenue' => 'Inntekt',
                'Visits' => 'Besøk',
                'Clicks' => 'Klikk',
                'Risk' => 'Risiko',
                'Quality' => 'Kvalitet',
                'Save Settings' => 'Lagre innstillinger',
                'Save pixel setup' => 'Lagre pikseloppsett',
                'Language & localization' => 'Språk og lokalisering',
                'Plugin language' => 'Plugin-språk',
                'Auto - Use WordPress language' => 'Auto - bruk WordPress-språk',
                'Risk rules' => 'Risikoregler',
                'Emergency mode' => 'Nødmodus',
                'Enable Emergency Mode' => 'Aktiver nødmodus',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'cs' => [
                'Dashboard' => 'Nástěnka',
                'Live View' => 'Živý náhled',
                'My Bridges' => 'Moje bridges',
                'Create Bridge' => 'Vytvořit bridge',
                'Analytics' => 'Analytika',
                'Leads' => 'Leady',
                'Pixels' => 'Pixely',
                'Reports' => 'Reporty',
                'Settings' => 'Nastavení',
                'License' => 'Licence',
                'Traffic Command Console' => 'Řídicí konzole provozu',
                'Conversions' => 'Konverze',
                'Revenue' => 'Tržby',
                'Visits' => 'Návštěvy',
                'Clicks' => 'Kliknutí',
                'Risk' => 'Riziko',
                'Quality' => 'Kvalita',
                'Save Settings' => 'Uložit nastavení',
                'Save pixel setup' => 'Uložit nastavení pixelů',
                'Language & localization' => 'Jazyk a lokalizace',
                'Plugin language' => 'Jazyk pluginu',
                'Auto - Use WordPress language' => 'Auto - použít jazyk WordPressu',
                'Risk rules' => 'Pravidla rizika',
                'Emergency mode' => 'Nouzový režim',
                'Enable Emergency Mode' => 'Zapnout nouzový režim',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'sk' => [
                'Dashboard' => 'Nástenka',
                'Live View' => 'Živé zobrazenie',
                'My Bridges' => 'Moje bridges',
                'Create Bridge' => 'Vytvoriť bridge',
                'Analytics' => 'Analytika',
                'Leads' => 'Leady',
                'Pixels' => 'Pixely',
                'Reports' => 'Reporty',
                'Settings' => 'Nastavenia',
                'License' => 'Licencia',
                'Traffic Command Console' => 'Riadiaca konzola návštevnosti',
                'Conversions' => 'Konverzie',
                'Revenue' => 'Tržby',
                'Visits' => 'Návštevy',
                'Clicks' => 'Kliknutia',
                'Risk' => 'Riziko',
                'Quality' => 'Kvalita',
                'Save Settings' => 'Uložiť nastavenia',
                'Save pixel setup' => 'Uložiť nastavenie pixelov',
                'Language & localization' => 'Jazyk a lokalizácia',
                'Plugin language' => 'Jazyk pluginu',
                'Auto - Use WordPress language' => 'Auto - použiť jazyk WordPressu',
                'Risk rules' => 'Pravidlá rizika',
                'Emergency mode' => 'Núdzový režim',
                'Enable Emergency Mode' => 'Zapnúť núdzový režim',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'sl' => [
                'Dashboard' => 'Nadzorna plošča',
                'Live View' => 'Pogled v živo',
                'My Bridges' => 'Moji bridgei',
                'Create Bridge' => 'Ustvari bridge',
                'Analytics' => 'Analitika',
                'Leads' => 'Leadi',
                'Pixels' => 'Piksli',
                'Reports' => 'Poročila',
                'Settings' => 'Nastavitve',
                'License' => 'Licenca',
                'Traffic Command Console' => 'Konzola za promet',
                'Conversions' => 'Konverzije',
                'Revenue' => 'Prihodek',
                'Visits' => 'Obiski',
                'Clicks' => 'Kliki',
                'Risk' => 'Tveganje',
                'Quality' => 'Kakovost',
                'Save Settings' => 'Shrani nastavitve',
                'Save pixel setup' => 'Shrani nastavitev pikslov',
                'Language & localization' => 'Jezik in lokalizacija',
                'Plugin language' => 'Jezik vtičnika',
                'Auto - Use WordPress language' => 'Samodejno - uporabi jezik WordPressa',
                'Risk rules' => 'Pravila tveganja',
                'Emergency mode' => 'Način v sili',
                'Enable Emergency Mode' => 'Omogoči način v sili',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'hr' => [
                'Dashboard' => 'Nadzorna ploča',
                'Live View' => 'Prikaz uživo',
                'My Bridges' => 'Moji bridgeovi',
                'Create Bridge' => 'Izradi bridge',
                'Analytics' => 'Analitika',
                'Leads' => 'Leadovi',
                'Pixels' => 'Pikseli',
                'Reports' => 'Izvješća',
                'Settings' => 'Postavke',
                'License' => 'Licenca',
                'Traffic Command Console' => 'Konzola za promet',
                'Conversions' => 'Konverzije',
                'Revenue' => 'Prihod',
                'Visits' => 'Posjeti',
                'Clicks' => 'Klikovi',
                'Risk' => 'Rizik',
                'Quality' => 'Kvaliteta',
                'Save Settings' => 'Spremi postavke',
                'Save pixel setup' => 'Spremi postavke piksela',
                'Language & localization' => 'Jezik i lokalizacija',
                'Plugin language' => 'Jezik dodatka',
                'Auto - Use WordPress language' => 'Automatski - koristi jezik WordPressa',
                'Risk rules' => 'Pravila rizika',
                'Emergency mode' => 'Hitni način',
                'Enable Emergency Mode' => 'Uključi hitni način',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'hu' => [
                'Dashboard' => 'Vezérlőpult',
                'Live View' => 'Élő nézet',
                'My Bridges' => 'Bridge-eim',
                'Create Bridge' => 'Bridge létrehozása',
                'Analytics' => 'Analitika',
                'Leads' => 'Leadek',
                'Pixels' => 'Pixelek',
                'Reports' => 'Jelentések',
                'Settings' => 'Beállítások',
                'License' => 'Licenc',
                'Traffic Command Console' => 'Forgalmi vezérlőpult',
                'Conversions' => 'Konverziók',
                'Revenue' => 'Bevétel',
                'Visits' => 'Látogatások',
                'Clicks' => 'Kattintások',
                'Risk' => 'Kockázat',
                'Quality' => 'Minőség',
                'Save Settings' => 'Beállítások mentése',
                'Save pixel setup' => 'Pixelbeállítás mentése',
                'Language & localization' => 'Nyelv és lokalizáció',
                'Plugin language' => 'Bővítmény nyelve',
                'Auto - Use WordPress language' => 'Automatikus - WordPress nyelv használata',
                'Risk rules' => 'Kockázati szabályok',
                'Emergency mode' => 'Vészhelyzeti mód',
                'Enable Emergency Mode' => 'Vészhelyzeti mód bekapcsolása',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'el' => [
                'Dashboard' => 'Πίνακας ελέγχου',
                'Live View' => 'Ζωντανή προβολή',
                'My Bridges' => 'Οι bridges μου',
                'Create Bridge' => 'Δημιουργία bridge',
                'Analytics' => 'Αναλυτικά',
                'Reports' => 'Αναφορές',
                'Settings' => 'Ρυθμίσεις',
                'License' => 'Άδεια',
                'Traffic Command Console' => 'Κονσόλα εντολών κίνησης',
                'Conversions' => 'Μετατροπές',
                'Revenue' => 'Έσοδα',
                'Visits' => 'Επισκέψεις',
                'Clicks' => 'Κλικ',
                'Risk' => 'Κίνδυνος',
                'Quality' => 'Ποιότητα',
                'Save Settings' => 'Αποθήκευση ρυθμίσεων',
                'Save pixel setup' => 'Αποθήκευση ρύθμισης pixel',
                'Language & localization' => 'Γλώσσα και τοπικοποίηση',
                'Plugin language' => 'Γλώσσα plugin',
                'Auto - Use WordPress language' => 'Αυτόματο - χρήση γλώσσας WordPress',
                'Risk rules' => 'Κανόνες κινδύνου',
                'Emergency mode' => 'Λειτουργία έκτακτης ανάγκης',
                'Enable Emergency Mode' => 'Ενεργοποίηση έκτακτης ανάγκης',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'bg' => [
                'Dashboard' => 'Табло',
                'Live View' => 'Изглед на живо',
                'My Bridges' => 'Моите bridges',
                'Create Bridge' => 'Създай bridge',
                'Analytics' => 'Анализи',
                'Leads' => 'Лийдове',
                'Pixels' => 'Пиксели',
                'Reports' => 'Отчети',
                'Settings' => 'Настройки',
                'License' => 'Лиценз',
                'Traffic Command Console' => 'Конзола за трафик',
                'Conversions' => 'Конверсии',
                'Revenue' => 'Приход',
                'Visits' => 'Посещения',
                'Clicks' => 'Кликове',
                'Risk' => 'Риск',
                'Quality' => 'Качество',
                'Save Settings' => 'Запази настройките',
                'Save pixel setup' => 'Запази pixel настройка',
                'Language & localization' => 'Език и локализация',
                'Plugin language' => 'Език на плъгина',
                'Auto - Use WordPress language' => 'Автоматично - използвай езика на WordPress',
                'Risk rules' => 'Правила за риск',
                'Emergency mode' => 'Авариен режим',
                'Enable Emergency Mode' => 'Активирай авариен режим',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'et' => [
                'Dashboard' => 'Töölaud',
                'Live View' => 'Otsevaade',
                'My Bridges' => 'Minu bridged',
                'Create Bridge' => 'Loo bridge',
                'Analytics' => 'Analüütika',
                'Leads' => 'Leadid',
                'Pixels' => 'Pikslid',
                'Reports' => 'Aruanded',
                'Settings' => 'Seaded',
                'License' => 'Litsents',
                'Traffic Command Console' => 'Liikluse juhtpaneel',
                'Conversions' => 'Konversioonid',
                'Revenue' => 'Tulu',
                'Visits' => 'Külastused',
                'Clicks' => 'Klikid',
                'Quality' => 'Kvaliteet',
                'Save Settings' => 'Salvesta seaded',
                'Save pixel setup' => 'Salvesta piksliseaded',
                'Language & localization' => 'Keel ja lokaliseerimine',
                'Plugin language' => 'Plugina keel',
                'Auto - Use WordPress language' => 'Auto - kasuta WordPressi keelt',
                'Risk rules' => 'Riskireeglid',
                'Emergency mode' => 'Hädaolukorra režiim',
                'Enable Emergency Mode' => 'Lülita hädaolukorra režiim sisse',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'lv' => [
                'Dashboard' => 'Panelis',
                'Live View' => 'Tiešskats',
                'My Bridges' => 'Mani bridge',
                'Create Bridge' => 'Izveidot bridge',
                'Analytics' => 'Analītika',
                'Leads' => 'Leadi',
                'Pixels' => 'Pikseļi',
                'Reports' => 'Atskaites',
                'Settings' => 'Iestatījumi',
                'License' => 'Licence',
                'Traffic Command Console' => 'Trafika vadības konsole',
                'Conversions' => 'Konversijas',
                'Revenue' => 'Ieņēmumi',
                'Visits' => 'Apmeklējumi',
                'Clicks' => 'Klikšķi',
                'Risk' => 'Risks',
                'Quality' => 'Kvalitāte',
                'Save Settings' => 'Saglabāt iestatījumus',
                'Save pixel setup' => 'Saglabāt pikseļa iestatījumus',
                'Language & localization' => 'Valoda un lokalizācija',
                'Plugin language' => 'Spraudņa valoda',
                'Auto - Use WordPress language' => 'Automātiski - izmantot WordPress valodu',
                'Risk rules' => 'Riska noteikumi',
                'Emergency mode' => 'Ārkārtas režīms',
                'Enable Emergency Mode' => 'Ieslēgt ārkārtas režīmu',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'lt' => [
                'Dashboard' => 'Prietaisų skydelis',
                'Live View' => 'Tiesioginė peržiūra',
                'My Bridges' => 'Mano bridge',
                'Create Bridge' => 'Sukurti bridge',
                'Analytics' => 'Analitika',
                'Leads' => 'Lead’ai',
                'Pixels' => 'Pikseliai',
                'Reports' => 'Ataskaitos',
                'Settings' => 'Nustatymai',
                'License' => 'Licencija',
                'Traffic Command Console' => 'Srauto valdymo konsolė',
                'Conversions' => 'Konversijos',
                'Revenue' => 'Pajamos',
                'Visits' => 'Apsilankymai',
                'Clicks' => 'Paspaudimai',
                'Risk' => 'Rizika',
                'Quality' => 'Kokybė',
                'Save Settings' => 'Išsaugoti nustatymus',
                'Save pixel setup' => 'Išsaugoti pikselio nustatymus',
                'Language & localization' => 'Kalba ir lokalizacija',
                'Plugin language' => 'Įskiepio kalba',
                'Auto - Use WordPress language' => 'Automatiškai - naudoti WordPress kalbą',
                'Risk rules' => 'Rizikos taisyklės',
                'Emergency mode' => 'Avarinis režimas',
                'Enable Emergency Mode' => 'Įjungti avarinį režimą',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'ga' => [
                'Dashboard' => 'Painéal',
                'Live View' => 'Amharc beo',
                'My Bridges' => 'Mo bridges',
                'Create Bridge' => 'Cruthaigh bridge',
                'Analytics' => 'Anailísíocht',
                'Pixels' => 'Picteilíní',
                'Reports' => 'Tuairiscí',
                'Settings' => 'Socruithe',
                'License' => 'Ceadúnas',
                'Traffic Command Console' => 'Consól ordaithe tráchta',
                'Conversions' => 'Tiontuithe',
                'Revenue' => 'Ioncam',
                'Visits' => 'Cuairteanna',
                'Clicks' => 'Cliceanna',
                'Risk' => 'Riosca',
                'Quality' => 'Cáilíocht',
                'Save Settings' => 'Sábháil socruithe',
                'Save pixel setup' => 'Sábháil socrú picteilín',
                'Language & localization' => 'Teanga agus logánú',
                'Plugin language' => 'Teanga an bhreiseáin',
                'Auto - Use WordPress language' => 'Uathoibríoch - úsáid teanga WordPress',
                'Risk rules' => 'Rialacha riosca',
                'Emergency mode' => 'Mód éigeandála',
                'Enable Emergency Mode' => 'Cumasaigh mód éigeandála',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'mt' => [
                'Live View' => 'Dehra diretta',
                'My Bridges' => 'Il-bridges tiegħi',
                'Create Bridge' => 'Oħloq bridge',
                'Analytics' => 'Analitika',
                'Reports' => 'Rapporti',
                'License' => 'Liċenzja',
                'Traffic Command Console' => 'Konsola tal-kmand tat-traffiku',
                'Conversions' => 'Konverżjonijiet',
                'Revenue' => 'Dħul',
                'Visits' => 'Żjarat',
                'Clicks' => 'Klikks',
                'Risk' => 'Riskju',
                'Quality' => 'Kwalità',
                'Save Settings' => 'Issejvja settings',
                'Save pixel setup' => 'Issejvja setup tal-pixel',
                'Language & localization' => 'Lingwa u lokalizzazzjoni',
                'Plugin language' => 'Lingwa tal-plugin',
                'Auto - Use WordPress language' => 'Auto - uża l-lingwa ta’ WordPress',
                'Risk rules' => 'Regoli tar-riskju',
                'Emergency mode' => 'Modalità ta’ emerġenza',
                'Enable Emergency Mode' => 'Attiva modalità ta’ emerġenza',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'is' => [
                'Dashboard' => 'Stjórnborð',
                'Live View' => 'Bein sýn',
                'My Bridges' => 'Mín bridge',
                'Create Bridge' => 'Búa til bridge',
                'Analytics' => 'Greining',
                'Pixels' => 'Pixlar',
                'Reports' => 'Skýrslur',
                'Settings' => 'Stillingar',
                'License' => 'Leyfi',
                'Traffic Command Console' => 'Umferðarstjórnborð',
                'Conversions' => 'Umbreytingar',
                'Revenue' => 'Tekjur',
                'Visits' => 'Heimsóknir',
                'Clicks' => 'Smellir',
                'Risk' => 'Áhætta',
                'Quality' => 'Gæði',
                'Save Settings' => 'Vista stillingar',
                'Save pixel setup' => 'Vista pixlastillingu',
                'Language & localization' => 'Tungumál og staðfærsla',
                'Plugin language' => 'Tungumál viðbótar',
                'Auto - Use WordPress language' => 'Sjálfvirkt - nota WordPress tungumál',
                'Risk rules' => 'Áhættureglur',
                'Emergency mode' => 'Neyðarhamur',
                'Enable Emergency Mode' => 'Virkja neyðarham',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'uk' => [
                'Dashboard' => 'Панель',
                'Live View' => 'Перегляд наживо',
                'My Bridges' => 'Мої bridge',
                'Create Bridge' => 'Створити bridge',
                'Analytics' => 'Аналітика',
                'Leads' => 'Ліди',
                'Pixels' => 'Пікселі',
                'Reports' => 'Звіти',
                'Settings' => 'Налаштування',
                'License' => 'Ліцензія',
                'Traffic Command Console' => 'Консоль керування трафіком',
                'Conversions' => 'Конверсії',
                'Revenue' => 'Дохід',
                'Visits' => 'Візити',
                'Clicks' => 'Кліки',
                'Risk' => 'Ризик',
                'Quality' => 'Якість',
                'Save Settings' => 'Зберегти налаштування',
                'Save pixel setup' => 'Зберегти налаштування пікселя',
                'Language & localization' => 'Мова та локалізація',
                'Plugin language' => 'Мова плагіна',
                'Auto - Use WordPress language' => 'Авто - використовувати мову WordPress',
                'Risk rules' => 'Правила ризику',
                'Emergency mode' => 'Аварійний режим',
                'Enable Emergency Mode' => 'Увімкнути аварійний режим',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'ru' => [
                'Dashboard' => 'Панель',
                'Live View' => 'Просмотр в реальном времени',
                'My Bridges' => 'Мои bridge',
                'Create Bridge' => 'Создать bridge',
                'Analytics' => 'Аналитика',
                'Leads' => 'Лиды',
                'Pixels' => 'Пиксели',
                'Reports' => 'Отчеты',
                'Settings' => 'Настройки',
                'License' => 'Лицензия',
                'Traffic Command Console' => 'Консоль управления трафиком',
                'Conversions' => 'Конверсии',
                'Revenue' => 'Доход',
                'Visits' => 'Визиты',
                'Clicks' => 'Клики',
                'Risk' => 'Риск',
                'Quality' => 'Качество',
                'Save Settings' => 'Сохранить настройки',
                'Save pixel setup' => 'Сохранить настройки пикселя',
                'Language & localization' => 'Язык и локализация',
                'Plugin language' => 'Язык плагина',
                'Auto - Use WordPress language' => 'Авто - использовать язык WordPress',
                'Risk rules' => 'Правила риска',
                'Emergency mode' => 'Аварийный режим',
                'Enable Emergency Mode' => 'Включить аварийный режим',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'tr' => [
                'Dashboard' => 'Pano',
                'Live View' => 'Canlı görünüm',
                'My Bridges' => 'Bridge’lerim',
                'Create Bridge' => 'Bridge oluştur',
                'Analytics' => 'Analitik',
                'Leads' => 'Leadler',
                'Pixels' => 'Pikseller',
                'Reports' => 'Raporlar',
                'Settings' => 'Ayarlar',
                'License' => 'Lisans',
                'Traffic Command Console' => 'Trafik komuta konsolu',
                'Conversions' => 'Dönüşümler',
                'Revenue' => 'Gelir',
                'Visits' => 'Ziyaretler',
                'Clicks' => 'Tıklamalar',
                'Quality' => 'Kalite',
                'Save Settings' => 'Ayarları kaydet',
                'Save pixel setup' => 'Piksel ayarını kaydet',
                'Language & localization' => 'Dil ve yerelleştirme',
                'Plugin language' => 'Eklenti dili',
                'Auto - Use WordPress language' => 'Otomatik - WordPress dilini kullan',
                'Risk rules' => 'Risk kuralları',
                'Emergency mode' => 'Acil durum modu',
                'Enable Emergency Mode' => 'Acil durum modunu etkinleştir',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'sr' => [
                'Dashboard' => 'Контролна табла',
                'Live View' => 'Приказ уживо',
                'My Bridges' => 'Моји bridge-ови',
                'Create Bridge' => 'Креирај bridge',
                'Analytics' => 'Аналитика',
                'Leads' => 'Лидови',
                'Pixels' => 'Пиксели',
                'Reports' => 'Извештаји',
                'Settings' => 'Подешавања',
                'License' => 'Лиценца',
                'Traffic Command Console' => 'Конзола за саобраћај',
                'Conversions' => 'Конверзије',
                'Revenue' => 'Приход',
                'Visits' => 'Посете',
                'Clicks' => 'Кликови',
                'Risk' => 'Ризик',
                'Quality' => 'Квалитет',
                'Save Settings' => 'Сачувај подешавања',
                'Save pixel setup' => 'Сачувај pixel подешавање',
                'Language & localization' => 'Језик и локализација',
                'Plugin language' => 'Језик додатка',
                'Auto - Use WordPress language' => 'Аутоматски - користи WordPress језик',
                'Risk rules' => 'Правила ризика',
                'Emergency mode' => 'Хитни режим',
                'Enable Emergency Mode' => 'Укључи хитни режим',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'bs' => [
                'Dashboard' => 'Kontrolna ploča',
                'Live View' => 'Prikaz uživo',
                'My Bridges' => 'Moji bridgeovi',
                'Create Bridge' => 'Kreiraj bridge',
                'Analytics' => 'Analitika',
                'Leads' => 'Leadovi',
                'Pixels' => 'Pikseli',
                'Reports' => 'Izvještaji',
                'Settings' => 'Postavke',
                'License' => 'Licenca',
                'Traffic Command Console' => 'Konzola za promet',
                'Conversions' => 'Konverzije',
                'Revenue' => 'Prihod',
                'Visits' => 'Posjete',
                'Clicks' => 'Klikovi',
                'Risk' => 'Rizik',
                'Quality' => 'Kvalitet',
                'Save Settings' => 'Sačuvaj postavke',
                'Save pixel setup' => 'Sačuvaj pixel postavke',
                'Language & localization' => 'Jezik i lokalizacija',
                'Plugin language' => 'Jezik plugina',
                'Auto - Use WordPress language' => 'Automatski - koristi WordPress jezik',
                'Risk rules' => 'Pravila rizika',
                'Emergency mode' => 'Hitni način',
                'Enable Emergency Mode' => 'Uključi hitni način',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'sq' => [
                'Dashboard' => 'Paneli',
                'Live View' => 'Pamje live',
                'My Bridges' => 'Bridge-et e mia',
                'Create Bridge' => 'Krijo bridge',
                'Analytics' => 'Analitika',
                'Pixels' => 'Pikselë',
                'Reports' => 'Raporte',
                'Settings' => 'Cilësimet',
                'License' => 'Licenca',
                'Traffic Command Console' => 'Konsola e komandës së trafikut',
                'Conversions' => 'Konvertime',
                'Revenue' => 'Të ardhura',
                'Visits' => 'Vizita',
                'Clicks' => 'Klikime',
                'Risk' => 'Rrezik',
                'Quality' => 'Cilësi',
                'Save Settings' => 'Ruaj cilësimet',
                'Save pixel setup' => 'Ruaj konfigurimin e pikselit',
                'Language & localization' => 'Gjuha dhe lokalizimi',
                'Plugin language' => 'Gjuha e plugin-it',
                'Auto - Use WordPress language' => 'Auto - përdor gjuhën e WordPress',
                'Risk rules' => 'Rregullat e rrezikut',
                'Emergency mode' => 'Modalitet emergjence',
                'Enable Emergency Mode' => 'Aktivizo modalitetin emergjent',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
            'mk' => [
                'Dashboard' => 'Контролна табла',
                'Live View' => 'Преглед во живо',
                'My Bridges' => 'Мои bridge',
                'Create Bridge' => 'Креирај bridge',
                'Analytics' => 'Аналитика',
                'Leads' => 'Лидови',
                'Pixels' => 'Пиксели',
                'Reports' => 'Извештаи',
                'Settings' => 'Поставки',
                'License' => 'Лиценца',
                'Traffic Command Console' => 'Конзола за сообраќај',
                'Conversions' => 'Конверзии',
                'Revenue' => 'Приход',
                'Visits' => 'Посети',
                'Clicks' => 'Кликови',
                'Risk' => 'Ризик',
                'Quality' => 'Квалитет',
                'Save Settings' => 'Зачувај поставки',
                'Save pixel setup' => 'Зачувај pixel поставки',
                'Language & localization' => 'Јазик и локализација',
                'Plugin language' => 'Јазик на додатокот',
                'Auto - Use WordPress language' => 'Автоматски - користи WordPress јазик',
                'Risk rules' => 'Правила за ризик',
                'Emergency mode' => 'Итен режим',
                'Enable Emergency Mode' => 'Овозможи итен режим',
                'Your universal postback URL' => 'Universal postback URL',
                'Copy universal postback URL' => 'Copy postback URL',
            ],
        ];
        $key = strtolower(substr((string) $locale, 0, 2));
        return $base[$key] ?? [];
    }

    private function sanitize_plugin_language($value) {
        $value = preg_replace('/[^A-Za-z0-9_\-]/', '', (string) $value);
        if ($value === '' || strtolower($value) === 'auto') {
            return 'auto';
        }
        $choices = self::plugin_language_choices();
        if (isset($choices[$value])) {
            return $value;
        }
        $needle = strtolower(str_replace('-', '_', $value));
        foreach ($choices as $code => $label) {
            if (strtolower(str_replace('-', '_', (string) $code)) === $needle) {
                return (string) $code;
            }
        }
        return 'auto';
    }

    public static function activate() {
        self::instance()->register_post_type();
        self::create_tables();
        self::default_options();
        update_option('itp_flush_rewrite', '1', false);
        if (!wp_next_scheduled('itp_daily_cleanup')) {
            wp_schedule_event(time() + HOUR_IN_SECONDS, 'daily', 'itp_daily_cleanup');
        }
        flush_rewrite_rules();
    }

    public static function deactivate() {
        wp_clear_scheduled_hook('itp_daily_cleanup');
        flush_rewrite_rules();
    }

    public static function create_tables() {
        global $wpdb;
        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        $table = $wpdb->prefix . self::EVENT_TABLE;
        $charset = $wpdb->get_charset_collate();
        $sql = "CREATE TABLE {$table} (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
            presell_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
            session_id VARCHAR(64) NOT NULL DEFAULT '',
            visitor_id VARCHAR(64) NOT NULL DEFAULT '',
            event_type VARCHAR(24) NOT NULL DEFAULT 'visit',
            quality VARCHAR(32) NOT NULL DEFAULT 'valid',
            reason VARCHAR(255) NOT NULL DEFAULT '',
            source VARCHAR(120) NOT NULL DEFAULT '',
            medium VARCHAR(120) NOT NULL DEFAULT '',
            campaign VARCHAR(180) NOT NULL DEFAULT '',
            content VARCHAR(180) NOT NULL DEFAULT '',
            term_value VARCHAR(180) NOT NULL DEFAULT '',
            referrer TEXT NULL,
            landing_url TEXT NULL,
            destination TEXT NULL,
            device VARCHAR(32) NOT NULL DEFAULT '',
            browser VARCHAR(80) NOT NULL DEFAULT '',
            os VARCHAR(80) NOT NULL DEFAULT '',
            screen VARCHAR(32) NOT NULL DEFAULT '',
            language VARCHAR(32) NOT NULL DEFAULT '',
            timezone VARCHAR(80) NOT NULL DEFAULT '',
            ip_hash VARCHAR(128) NOT NULL DEFAULT '',
            ip_mask VARCHAR(64) NOT NULL DEFAULT '',
            country VARCHAR(80) NOT NULL DEFAULT '',
            country_code VARCHAR(8) NOT NULL DEFAULT '',
            scroll_depth SMALLINT UNSIGNED NOT NULL DEFAULT 0,
            time_on_page INT UNSIGNED NOT NULL DEFAULT 0,
            created_at DATETIME NOT NULL,
            PRIMARY KEY (id),
            KEY presell_id (presell_id),
            KEY event_type (event_type),
            KEY quality (quality),
            KEY created_at (created_at),
            KEY session_id (session_id),
            KEY visitor_id (visitor_id),
            KEY ip_hash (ip_hash)
        ) {$charset};";
        dbDelta($sql);

        $lead_table = $wpdb->prefix . self::LEAD_TABLE;
        $lead_sql = "CREATE TABLE {$lead_table} (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
            presell_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
            session_id VARCHAR(64) NOT NULL DEFAULT '',
            visitor_id VARCHAR(64) NOT NULL DEFAULT '',
            email VARCHAR(190) NOT NULL DEFAULT '',
            name VARCHAR(190) NOT NULL DEFAULT '',
            phone VARCHAR(80) NOT NULL DEFAULT '',
            source VARCHAR(120) NOT NULL DEFAULT '',
            medium VARCHAR(120) NOT NULL DEFAULT '',
            campaign VARCHAR(180) NOT NULL DEFAULT '',
            referrer TEXT NULL,
            landing_url TEXT NULL,
            destination TEXT NULL,
            created_at DATETIME NOT NULL,
            PRIMARY KEY (id),
            KEY presell_id (presell_id),
            KEY email (email),
            KEY created_at (created_at)
        ) {$charset};";
        dbDelta($lead_sql);
    }

    public static function default_options() {
        $existing = get_option(self::OPT, []);
        if (!is_array($existing)) {
            $existing = [];
        }
        $defaults = [
            'base_slug' => '',
            'brand_name' => 'Invalid Traffic',
            'plugin_language' => 'auto',
            'powered_by' => '0',
            'button_delay' => '0',
            'engaged_seconds' => '10',
            'engaged_scroll' => '30',
            'fast_click_seconds' => '2',
            'repeat_visit_limit' => '8',
            'repeat_window_minutes' => '60',
            'retention_days' => '30',
            'emergency_mode' => '0',
            'contact_page_id' => 0,
            'privacy_page_id' => 0,
            'terms_page_id' => 0,
        ];
        update_option(self::OPT, array_merge($defaults, $existing), false);
    }

    public function maybe_upgrade() {
        $version = get_option("itp_version");
        if ($version !== self::VERSION) {
            self::create_tables();
            self::default_options();
            // v0.14 uses clean root bridge URLs by default: domain.com/bridge-slug/.
            // This removes old test/go prefixes that could create /test/test/ style URLs.
            $settings = $this->settings();
            $settings['base_slug'] = '';
            update_option(self::OPT, $settings, false);
            update_option('itp_version', self::VERSION, false);
            update_option('itp_flush_rewrite', '1', false);
        }
    }

    public function maybe_flush_rewrite_once() {
        if (get_option('itp_flush_rewrite') === '1') {
            delete_option('itp_flush_rewrite');
            flush_rewrite_rules(false);
        }
    }

    public function add_query_vars($vars) {
        $vars[] = 'itp_vsl_embed';
                $vars[] = 'itp_vsl_player_js';
return $vars;
    }

    public function register_post_type() {
        $settings = $this->settings();
        $base = trim((string)($settings['base_slug'] ?? ''), '/');
        $rewrite_slug = $base !== '' ? $base : '';

        register_post_type(self::CPT, [
            'labels' => [
                'name' => 'Bridge Pages',
                'singular_name' => 'Bridge Page',
                'add_new_item' => 'Create Bridge Page',
                'edit_item' => 'Edit Bridge Page',
            ],
            'public' => true,
            'show_ui' => false,
            'show_in_menu' => false,
            'exclude_from_search' => true,
            'publicly_queryable' => true,
            'query_var' => true,
            'has_archive' => false,
            'supports' => ['title', 'thumbnail'],
            // Public links are handled manually so bridge URLs can live at domain.com/slug/.
            // A blank CPT rewrite slug falls back to /itp_presell/ in WordPress, so it is disabled here.
            'rewrite' => false,
            'capability_type' => 'post',
            'show_in_rest' => false,
        ]);

        // Player-only iframe/JS endpoint. This prevents Elementor/HTML embeds from rendering the full site header/footer.
        add_rewrite_rule('^it-vsl-embed/([^/]+)/?$', 'index.php?itp_vsl_embed=$matches[1]', 'top');
        add_rewrite_rule('^it-vsl-player/([^/]+)/player\.js$', 'index.php?itp_vsl_player_js=$matches[1]', 'top');
    }

    public function clean_bridge_permalink($permalink, $post) {
        if (!$post || $post->post_type !== self::CPT) return $permalink;
        return home_url(user_trailingslashit($post->post_name));
    }

    public function route_clean_bridge_request($query_vars) {
        if (is_admin() || empty($query_vars) || !is_array($query_vars)) return $query_vars;

        $raw_pagename = isset($query_vars['pagename']) ? trim((string)$query_vars['pagename'], '/') : '';
        if ($raw_pagename && preg_match('#^it-vsl-embed/([^/]+)/?$#', $raw_pagename, $m)) {
            return ['itp_vsl_embed' => sanitize_title($m[1])];
        }
        if ($raw_pagename && preg_match('#^it-vsl-player/([^/]+)/player\.js$#', $raw_pagename, $m)) {
            return ['itp_vsl_player_js' => sanitize_title($m[1])];
        }

        $slug = '';
        if (!empty($query_vars['pagename']) && strpos((string)$query_vars['pagename'], '/') === false) {
            $slug = sanitize_title($query_vars['pagename']);
        } elseif (!empty($query_vars['name']) && empty($query_vars['post_type'])) {
            $slug = sanitize_title($query_vars['name']);
        }

        if (!$slug) return $query_vars;

        $reserved = [
            'wp-admin', 'wp-content', 'wp-includes', 'wp-json', 'xmlrpc.php', 'feed', 'comments',
            'robots.txt', 'sitemap.xml', 'favicon.ico', 'apple-touch-icon.png'
        ];
        if (in_array($slug, $reserved, true)) return $query_vars;

        // Never steal an existing public page/post URL. Real site pages keep priority.
        if (get_page_by_path($slug, OBJECT, ['page', 'post'])) {
            return $query_vars;
        }

        $bridge = get_page_by_path($slug, OBJECT, self::CPT);
        if (!$bridge || $bridge->post_status !== 'publish') return $query_vars;

        return [
            'post_type' => self::CPT,
            'name' => $slug,
            self::CPT => $slug,
        ];
    }

    public function redirect_legacy_bridge_url() {
        if (!is_singular(self::CPT)) return;
        global $post;
        if (!$post || $post->post_type !== self::CPT) return;

        $clean = $this->clean_bridge_permalink('', $post);
        $requested_path = trailingslashit((string)wp_parse_url(home_url(add_query_arg([])), PHP_URL_PATH));
        $clean_path = trailingslashit((string)wp_parse_url($clean, PHP_URL_PATH));

        if ($requested_path && $clean_path && $requested_path !== $clean_path) {
            wp_safe_redirect($clean, 301);
            exit;
        }
    }

    public function cpt_columns($columns) {
        $columns['itp_stats'] = 'Bridge stats';
        return $columns;
    }

    public function cpt_column_content($column, $post_id) {
        if ($column === 'itp_stats') {
            $stats = $this->stats_for_presell($post_id);
            echo esc_html($stats['visits'] . ' visits / ' . $stats['clicks'] . ' clicks');
        }
    }

    public function cpt_row_actions($actions, $post) {
        if ($post->post_type !== self::CPT) {
            return $actions;
        }
        $actions['itp_analytics'] = '<a href="' . esc_url(admin_url('admin.php?page=itp-analytics&id=' . (int)$post->ID)) . '">Analytics</a>';
        return $actions;
    }

    public function settings() {
        $settings = get_option(self::OPT, []);
        if (!is_array($settings)) {
            $settings = [];
        }
        return array_merge([
            'base_slug' => '',
            'brand_name' => 'Invalid Traffic',
            'plugin_language' => 'auto',
            'powered_by' => '0',
            'button_delay' => '0',
            'engaged_seconds' => '10',
            'engaged_scroll' => '30',
            'fast_click_seconds' => '2',
            'repeat_visit_limit' => '8',
            'repeat_window_minutes' => '60',
            'retention_days' => '30',
            'emergency_mode' => '0',
            'contact_page_id' => 0,
            'privacy_page_id' => 0,
            'terms_page_id' => 0,
        ], $settings);
    }

public function admin_menu() {
    add_menu_page(
        'Invalid Traffic',
        'Invalid Traffic',
        'manage_options',
        'itp-dashboard',
        [$this, 'page_dashboard'],
        'dashicons-shield-alt',
        56
    );

    add_submenu_page('itp-dashboard', 'Dashboard', 'Dashboard', 'manage_options', 'itp-dashboard', [$this, 'page_dashboard']);
    add_submenu_page('itp-dashboard', 'Live View', 'Live View', 'manage_options', 'itp-live', [$this, 'page_live_view']);
    add_submenu_page('itp-dashboard', 'My Bridges', 'My Bridges', 'manage_options', 'itp-presells', [$this, 'page_presells']);
    add_submenu_page('itp-dashboard', 'Create Bridge', 'Create Bridge', 'manage_options', 'itp-create', [$this, 'page_create']);
    add_submenu_page('itp-dashboard', 'Analytics', 'Analytics', 'manage_options', 'itp-analytics', [$this, 'page_analytics']);
    add_submenu_page('itp-dashboard', 'Leads', 'Leads', 'manage_options', 'itp-leads', [$this, 'page_leads']);
    add_submenu_page('itp-dashboard', 'Pixels', 'Pixels', 'manage_options', 'itp-pixels', [$this, 'page_pixels']);
    add_submenu_page('itp-dashboard', 'Reports', 'Reports', 'manage_options', 'itp-reports', [$this, 'page_evidence_report']);
    add_submenu_page('itp-dashboard', 'Settings', 'Settings', 'manage_options', 'itp-settings', [$this, 'page_settings']);
    add_submenu_page('itp-dashboard', 'License', 'License', 'manage_options', 'itp-license', [$this, 'page_license']);
}

    public function admin_assets($hook) {
        if (strpos($hook, 'itp-') === false && strpos($hook, 'invalid-traffic') === false) {
            return;
        }
        wp_enqueue_media();
        wp_enqueue_style('itp-jsvectormap', plugin_dir_url(__FILE__) . 'assets/vendor/jsvectormap/jsvectormap.min.css', [], '1.7.0');
        wp_enqueue_style('itp-admin', plugin_dir_url(__FILE__) . 'assets/admin.css', ['itp-jsvectormap'], self::VERSION);
        wp_enqueue_script('itp-jsvectormap', plugin_dir_url(__FILE__) . 'assets/vendor/jsvectormap/jsvectormap.min.js', [], '1.7.0', true);
        wp_enqueue_script('itp-jsvectormap-world', plugin_dir_url(__FILE__) . 'assets/vendor/jsvectormap/world.js', ['itp-jsvectormap'], '1.7.0', true);
        wp_enqueue_script('itp-admin-charts', plugin_dir_url(__FILE__) . 'assets/admin-charts.js', ['itp-jsvectormap', 'itp-jsvectormap-world'], self::VERSION, true);
        wp_enqueue_script('itp-admin', plugin_dir_url(__FILE__) . 'assets/admin.js', ['itp-admin-charts'], self::VERSION, true);
        wp_localize_script('itp-admin', 'ITP_ADMIN', [
            'ajax' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('itp_admin_nonce'),
            'assetsUrl' => plugin_dir_url(__FILE__) . 'assets/',
        ]);
    }
private function asset_url($path = '') {
    return plugin_dir_url(__FILE__) . ltrim($path, '/');
}

private function brand_logo_url() {
    return $this->asset_url('assets/logo-admin.png');
}
private function admin_shell_start($title = 'Dashboard', $subtitle = '') {
    $settings = $this->settings();
    $brand_raw = $settings['brand_name'] ?: 'Invalid Traffic';
    $current_page = sanitize_key($_GET['page'] ?? 'itp-dashboard');
    $active = function($page) use ($current_page) {
        return $current_page === $page ? ' is-active' : '';
    };

    $svg_dashboard = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1.5"></rect><rect x="14" y="3" width="7" height="7" rx="1.5"></rect><rect x="3" y="14" width="7" height="7" rx="1.5"></rect><rect x="14" y="14" width="7" height="7" rx="1.5"></rect></svg>';
    $svg_live = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 12s3.5-6 9-6 9 6 9 6-3.5 6-9 6-9-6-9-6z"></path><circle cx="12" cy="12" r="3"></circle></svg>';
    $svg_bridges = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 7h16v5H4z"></path><path d="M4 16h16v1.5A2.5 2.5 0 0 1 17.5 20h-11A2.5 2.5 0 0 1 4 17.5z"></path><path d="M8 10h4"></path></svg>';
    $svg_reports = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 19V5"></path><path d="M4 19h16"></path><path d="M7 15l4-4 3 3 5-7"></path></svg>';
    $svg_leads = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 6h16v12H4z"></path><path d="M4 7l8 6 8-6"></path></svg>';
    $svg_pixels = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 7h10"></path><path d="M4 12h16"></path><path d="M4 17h7"></path><circle cx="17" cy="7" r="2"></circle><circle cx="14" cy="17" r="2"></circle></svg>';
    $svg_settings = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 15.5A3.5 3.5 0 1 0 12 8a3.5 3.5 0 0 0 0 7.5z"></path><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06A1.65 1.65 0 0 0 15 19.4a1.65 1.65 0 0 0-1 .6 1.65 1.65 0 0 0-.4 1.1V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 8.6 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.6 15a1.65 1.65 0 0 0-.6-1 1.65 1.65 0 0 0-1.1-.4H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 8.6a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6a1.65 1.65 0 0 0 1-.6 1.65 1.65 0 0 0 .4-1.1V3a2 2 0 1 1 4 0v.09A1.65 1.65 0 0 0 15.4 4.6a1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9c.4.2.8.3 1.2.3H21a2 2 0 1 1 0 4h-.09A1.65 1.65 0 0 0 19.4 15z"></path></svg>';

    echo '<div class="itp-admin-wrap itp-page-' . esc_attr($current_page) . '">';
    echo '<div class="itp-admin-top">';
    echo '<div class="itp-plugin-header">';

    echo '<a class="itp-plugin-brand" href="' . esc_url(admin_url('admin.php?page=itp-dashboard')) . '" aria-label="Invalid Traffic dashboard">';
    echo '<span class="itp-plugin-logo-mark"><img class="itp-logo-image" src="' . esc_url($this->brand_logo_url()) . '" alt="' . esc_attr($brand_raw) . '"></span>';
    echo '<span class="itp-plugin-brand-divider" aria-hidden="true"></span>';
    echo '<span class="itp-plugin-wordmark"><strong><span class="itp-name-invalid">Invalid</span><span class="itp-name-traffic">Traffic</span></strong><em>Build • Play • Track • Filter</em></span>';
    echo '</a>';

    echo '<nav class="itp-plugin-nav" aria-label="Invalid Traffic admin navigation">';
    echo '<a class="itp-plugin-nav-link' . esc_attr($active('itp-dashboard')) . '" href="' . esc_url(admin_url('admin.php?page=itp-dashboard')) . '">' . $svg_dashboard . '<span>Dashboard</span></a>';
    echo '<a class="itp-plugin-nav-link' . esc_attr($active('itp-live')) . '" href="' . esc_url(admin_url('admin.php?page=itp-live')) . '">' . $svg_live . '<span>Live View</span></a>';
    echo '<a class="itp-plugin-nav-link' . esc_attr($active('itp-presells')) . '" href="' . esc_url(admin_url('admin.php?page=itp-presells')) . '">' . $svg_bridges . '<span>My Bridges</span></a>';
    echo '<a class="itp-plugin-nav-link' . esc_attr($active('itp-leads')) . '" href="' . esc_url(admin_url('admin.php?page=itp-leads')) . '">' . $svg_leads . '<span>Leads</span></a>';
    echo '<a class="itp-plugin-nav-link' . esc_attr($active('itp-pixels')) . '" href="' . esc_url(admin_url('admin.php?page=itp-pixels')) . '">' . $svg_pixels . '<span>Pixels</span></a>';
    echo '<a class="itp-plugin-nav-link' . esc_attr($active('itp-reports')) . '" href="' . esc_url(admin_url('admin.php?page=itp-reports')) . '">' . $svg_reports . '<span>Reports</span></a>';
    echo '</nav>';

    echo '<div class="itp-plugin-actions">';
    echo '<span class="itp-plugin-divider" aria-hidden="true"></span>';
    echo '<a class="itp-plugin-icon-link' . esc_attr($active('itp-settings')) . '" href="' . esc_url(admin_url('admin.php?page=itp-settings')) . '" aria-label="Settings">' . $svg_settings . '</a>';
    echo '<a class="itp-plugin-cta' . esc_attr($active('itp-create')) . '" href="' . esc_url(admin_url('admin.php?page=itp-create')) . '">+ Create Bridge</a>';
    echo '</div>';

    echo '</div>';
    echo '</div>';
    echo '<main class="itp-admin-main">';
    echo '<header class="itp-page-head"><h1>' . esc_html($title) . '</h1>';

    if ($subtitle !== '') {
        echo '<p>' . esc_html($subtitle) . '</p>';
    }

    echo '</header>';

    if (($settings['emergency_mode'] ?? '0') === '1') {
        echo '<section class="itp-emergency-banner"><div><strong>Emergency Mode is active</strong><p>Risk labeling is stricter for fast clicks, weak engagement and repeated visits. Bridge pages remain no-ad; destination pages are not modified.</p></div><a class="itp-btn itp-btn-white" href="' . esc_url(admin_url('admin.php?page=itp-reports')) . '">Generate Evidence Report</a></section>';
    }
}

    private function admin_shell_end() {
        echo '</main></div>';
    }

    public function page_dashboard() {
        if (!current_user_can('manage_options')) return;
        $this->handle_actions();
        $stats = $this->global_stats();
        $vsl_global = $this->global_vsl_stats();
        $conversion_global = $this->conversion_performance_stats(0);
        $quality = $this->quality_breakdown(0);
        $forward_rate = $stats['visits'] > 0 ? round(($stats['clicks'] / $stats['visits']) * 100, 1) : 0;
        $clean_signal = (int)$quality['valid'] + (int)$quality['engaged'];
        $risk_signal = (int)$quality['suspicious'] + (int)$quality['bot_like'] + (int)$quality['repeated'];
        $quality_base = max(1, $clean_signal + (int)$quality['low_intent'] + $risk_signal);
        $quality_pct = round(($clean_signal / $quality_base) * 100, 1);
        $quality_risk_pct = round(($risk_signal / $quality_base) * 100, 1);
        $source_rows = $this->breakdown_rows('source', 0, 5, "event_type IN ('visit','click')");
        $country_rows = $this->breakdown_rows('country_code', 0, 6, "event_type IN ('visit','click')");
        $device_rows = $this->breakdown_rows('device', 0, 4, "event_type IN ('visit','click')");
        $risk_rows = $this->risk_rows(0, 5);
        $channel_rows = $this->channel_mix_rows(0, 6);
        $daily = $this->daily_series(0);
        $this->admin_shell_start('Dashboard', 'Protect and measure paid clicks before the money page.');
        echo $this->analytics_period_bar(admin_url('admin.php?page=itp-dashboard'));
        echo $this->reset_metrics_panel(0, 'dashboard');
        ?>
        <section class="itp-hero-card itp-command-hero">
            <div>
                <span class="itp-kicker">Traffic Quality Command Center</span>
                <h2>Build, play, track and filter<br>before the money page.</h2>
                <p>One control room for bridge pages, VSL players and first-party traffic signals. Track visits, plays, Smart Autoplay starts, CTA clicks, geo/device mix and risk patterns without connecting ad accounts.</p>
                <div class="itp-row-actions"><a class="itp-btn" href="<?php echo esc_url(admin_url('admin.php?page=itp-create')); ?>">Create Bridge</a><a class="itp-btn itp-btn-white" href="<?php echo esc_url(admin_url('admin.php?page=itp-analytics')); ?>">Open Analytics</a><a class="itp-btn itp-btn-white" href="<?php echo esc_url(admin_url('admin.php?page=itp-live')); ?>">Live View</a><a class="itp-btn itp-btn-white" href="<?php echo esc_url(admin_url('admin.php?page=itp-reports')); ?>">Evidence Report</a></div>
            </div>
            <div class="itp-command-art itp-command-art-v2 itp-command-art-v3 itp-command-art-orbit" data-itp-command-orbit aria-hidden="true">
                <div class="itp-command-aurora"></div>
                <div class="itp-command-scan"></div>
                <div class="itp-command-rail"></div>
                <div class="itp-command-orbit"></div>
                <div class="itp-orbit-ring itp-orbit-ring-one"></div>
                <div class="itp-orbit-ring itp-orbit-ring-two"></div>
                <span class="itp-orbit-node node-a"></span>
                <span class="itp-orbit-node node-b"></span>
                <span class="itp-orbit-node node-c"></span>
                <span class="itp-orbit-node node-d"></span>
                <div class="itp-command-card-v2 itp-card-clean itp-card-quality-motion">
                    <small>Traffic Quality</small>
                    <strong<?php echo $this->countup_attrs($quality_pct, '%', 1); ?>><?php echo esc_html($quality_pct); ?>%</strong>
                    <span>valid traffic signal</span>
                    <i class="itp-mini-meter itp-mini-meter-stack" style="--p:<?php echo esc_attr(min(100, max(8, $quality_pct))); ?>%;--risk:<?php echo esc_attr(min(100, max(4, $quality_risk_pct))); ?>%"></i>
                    <em class="itp-orbit-card-note">valid <?php echo esc_html($clean_signal); ?> · risk <?php echo esc_html($risk_signal); ?></em>
                </div>
                <div class="itp-command-card-v2 itp-card-curve itp-card-traffic-motion">
                    <small>Invalid Traffic over time</small>
                    <b class="itp-mini-line itp-mini-line-live"><i></i><i></i><i></i><i></i><i></i></b>
                    <span>risk line vs clean sessions</span>
                    <em class="itp-orbit-card-note">live trend</em>
                </div>
                <div class="itp-command-card-v2 itp-card-blocked itp-card-blocked-motion">
                    <small>Blocked / Reviewed Traffic</small>
                    <strong<?php echo $this->countup_attrs($risk_signal); ?>><?php echo esc_html($risk_signal); ?></strong>
                    <span>risk signals detected</span>
                    <em class="itp-orbit-card-note">review queue</em>
                </div>
                <div class="itp-command-card-v2 itp-card-vsl itp-card-vsl-motion">
                    <small>VSL Play Rate</small>
                    <strong<?php echo $this->countup_attrs($vsl_global['play_rate'], '%', 1); ?>><?php echo esc_html($vsl_global['play_rate']); ?>%</strong>
                    <span><?php echo esc_html($vsl_global['plays']); ?> plays · <?php echo esc_html($vsl_global['unique_plays']); ?> unique</span>
                    <em class="itp-orbit-card-note">player signal</em>
                </div>
                <div class="itp-command-core">
                    <div class="itp-command-shield-v2"><i></i><b>IT</b></div>
                    <span class="itp-platform-dot g">G</span>
                    <span class="itp-platform-dot m">M</span>
                    <span class="itp-platform-dot b">B</span>
                    <span class="itp-platform-dot n">N</span>
                </div>
            </div>
        </section>

        <section class="itp-saas-engine" data-itp-saas-engine aria-label="Traffic command console">
            <div class="itp-saas-glow"></div>
            <div class="itp-saas-header">
                <div>
                    <span class="itp-kicker">Traffic Command Console</span>
                    <h2>Live traffic console powered by your own first-party data.</h2>
                    <p>Premium traffic analytics for visits, VSL plays, clean sessions, risk pressure and source evidence — using your own local Invalid Traffic metrics.</p>
                </div>
                <div class="itp-saas-status"><b>Current period</b><span><?php echo esc_html($this->analytics_period()['label']); ?></span></div>
            </div>
            <div class="itp-saas-layout">
                <div class="itp-saas-main">
                    <div class="itp-saas-kpi-row">
                        <article class="itp-saas-kpi cyan">
                            <span>Visits</span>
                            <strong<?php echo $this->countup_attrs($stats['visits']); ?>><?php echo esc_html($stats['visits']); ?></strong>
                            <em><?php echo esc_html($stats['clicks']); ?> clicks forward</em>
                            <div class="itp-saas-spark"><i></i><i></i><i></i><i></i><i></i><i></i></div>
                        </article>
                        <article class="itp-saas-kpi violet">
                            <span>VSL Plays</span>
                            <strong<?php echo $this->countup_attrs($vsl_global['plays']); ?>><?php echo esc_html($vsl_global['plays']); ?></strong>
                            <em><?php echo esc_html($vsl_global['unique_plays']); ?> unique starters</em>
                            <div class="itp-saas-spark"><i></i><i></i><i></i><i></i><i></i><i></i></div>
                        </article>
                        <article class="itp-saas-kpi gold">
                            <span>Clean Signal</span>
                            <strong<?php echo $this->countup_attrs($clean_signal); ?>><?php echo esc_html($clean_signal); ?></strong>
                            <em>valid + engaged sessions</em>
                            <div class="itp-saas-spark"><i></i><i></i><i></i><i></i><i></i><i></i></div>
                        </article>
                        <article class="itp-saas-kpi green">
                            <span>Conversions</span>
                            <strong<?php echo $this->countup_attrs($conversion_global['conversions']); ?>><?php echo esc_html($conversion_global['conversions']); ?></strong>
                            <em>confirmed purchases/actions</em>
                            <div class="itp-saas-spark"><i></i><i></i><i></i><i></i><i></i><i></i></div>
                        </article>
                        <article class="itp-saas-kpi rose">
                            <span>Revenue</span>
                            <strong class="itp-saas-revenue-value"><?php echo esc_html($conversion_global['revenue']); ?></strong>
                            <em>from confirmed conversions</em>
                            <div class="itp-saas-spark"><i></i><i></i><i></i><i></i><i></i><i></i></div>
                        </article>
                    </div>
                    <article class="itp-saas-chart-card">
                        <div class="itp-saas-card-head">
                            <div><span>Global Traffic Analytics</span><h3>Visits, forward clicks and risk pressure</h3></div>
                            <div class="itp-saas-tabs"><b>Visits</b><b>Clicks</b><b>Risk</b></div>
                        </div>
                        <canvas class="itp-saas-line" data-itp-saas-line='<?php echo esc_attr(wp_json_encode($daily)); ?>' data-itp-saas-risk="<?php echo esc_attr($risk_signal); ?>"></canvas>
                        <div class="itp-saas-chart-meta">
                            <span><i class="cyan"></i><?php echo esc_html($stats['visits']); ?> visits</span>
                            <span><i class="violet"></i><?php echo esc_html($stats['clicks']); ?> forward clicks</span>
                            <span><i class="ok"></i><?php echo esc_html($conversion_global['conversions']); ?> conversions</span>
                            <span><i class="danger"></i><?php echo esc_html($risk_signal); ?> risk signals</span>
                        </div>
                    </article>
                    <article class="itp-saas-table-card">
                        <div class="itp-saas-card-head"><div><span>Bridge operations</span><h3>What needs attention before scaling</h3></div><a href="<?php echo esc_url(admin_url('admin.php?page=itp-presells')); ?>">Open My Bridges</a></div>
                        <div class="itp-saas-table">
                            <div><span>Layer</span><span>Signal</span><span>Value</span><span>Status</span></div>
                            <div><b>Bridge</b><em>Forward rate</em><strong><?php echo esc_html($forward_rate); ?>%</strong><i class="ok">Tracked</i></div>
                            <div><b>VSL</b><em>Play rate</em><strong><?php echo esc_html($vsl_global['play_rate']); ?>%</strong><i class="ok">Separated</i></div>
                            <div><b>Conversions</b><em>Global confirmed</em><strong><?php echo esc_html($conversion_global['conversions']); ?></strong><i class="ok">Attributed</i></div>
                            <div><b>Revenue</b><em>Global confirmed</em><strong><?php echo esc_html($conversion_global['revenue']); ?></strong><i class="ok">Tracked</i></div>
                            <div><b>Quality</b><em>Risk review</em><strong><?php echo esc_html($risk_signal); ?></strong><i class="warn">Review</i></div>
                        </div>
                    </article>
                </div>
                <aside class="itp-saas-side">
                    <article class="itp-saas-radial-card">
                        <div class="itp-saas-card-head"><div><span>Traffic Quality</span><h3>Clean vs risk mix</h3></div></div>
                        <div class="itp-saas-radial" style="--p:<?php echo esc_attr(min(100, max(0, $quality_pct))); ?>"><b><?php echo esc_html($quality_pct); ?>%</b><small>clean signal</small></div>
                        <div class="itp-saas-mini-list">
                            <div><span>Valid + engaged</span><strong><?php echo esc_html($clean_signal); ?></strong></div>
                            <div><span>Risk sessions</span><strong><?php echo esc_html($risk_signal); ?></strong></div>
                            <div><span>Forward clicks</span><strong><?php echo esc_html($stats['clicks']); ?></strong></div>
                            <div><span>Conversions</span><strong><?php echo esc_html($conversion_global['conversions']); ?></strong></div>
                            <div><span>Revenue</span><strong><?php echo esc_html($conversion_global['revenue']); ?></strong></div>
                        </div>
                    </article>
                    <article class="itp-saas-source-card">
                        <div class="itp-saas-card-head"><div><span>Top source</span><h3>First-party source signal</h3></div></div>
                        <?php echo wp_kses_post($this->mini_breakdown_html(array_slice($source_rows, 0, 4), 'No source data yet.')); ?>
                    </article>
                </aside>
            </div>
        </section>

        <section class="itp-stat-grid five itp-command-kpi-grid">
            <div class="itp-stat"><span>Bridges</span><strong<?php echo $this->countup_attrs($stats['presells']); ?>><?php echo esc_html($stats['presells']); ?></strong></div>
            <div class="itp-stat"><span>Visits</span><strong<?php echo $this->countup_attrs($stats['visits']); ?>><?php echo esc_html($stats['visits']); ?></strong></div>
            <div class="itp-stat"><span>Leads</span><strong<?php echo $this->countup_attrs($stats['leads'] ?? 0); ?>><?php echo esc_html($stats['leads'] ?? 0); ?></strong><em>captured emails</em></div>
            <div class="itp-stat"><span>Clicks forward</span><strong<?php echo $this->countup_attrs($stats['clicks']); ?>><?php echo esc_html($stats['clicks']); ?></strong></div>
            <div class="itp-stat"><span>VSL plays</span><strong<?php echo $this->countup_attrs($vsl_global['plays']); ?>><?php echo esc_html($vsl_global['plays']); ?></strong><em><?php echo esc_html($vsl_global['unique_plays']); ?> unique</em></div>
        </section>

        <section class="itp-command-strip" aria-label="Visual performance preview">
            <div class="itp-command-strip-card itp-strip-retention">
                <span>Retention pulse</span>
                <strong<?php echo $this->countup_attrs($vsl_global['play_rate'], '%', 1); ?>><?php echo esc_html($vsl_global['play_rate']); ?>%</strong>
                <div class="itp-strip-graph"><i></i><i></i><i></i><i></i><i></i></div>
            </div>
            <div class="itp-command-strip-card itp-strip-quality">
                <span>Traffic quality</span>
                <strong<?php echo $this->countup_attrs($quality['valid'] + $quality['engaged']); ?>><?php echo esc_html($quality['valid'] + $quality['engaged']); ?></strong>
                <div class="itp-strip-stack" data-itp-quality='<?php echo esc_attr(wp_json_encode($quality)); ?>'></div>
            </div>
            <div class="itp-command-strip-card itp-strip-vsl">
                <span>Smart Autoplay starts</span>
                <strong><?php echo esc_html($vsl_global['plays']); ?></strong>
                <em><?php echo esc_html($vsl_global['play_rate']); ?>% play rate</em>
            </div>
            <div class="itp-command-strip-card itp-strip-risk">
                <span>Risk review</span>
                <strong<?php echo $this->countup_attrs($quality['suspicious'] + $quality['bot_like'] + $quality['repeated']); ?>><?php echo esc_html($quality['suspicious'] + $quality['bot_like'] + $quality['repeated']); ?></strong>
                <em>first-party evidence</em>
            </div>
        </section>

        <?php $action_items = $this->dashboard_action_items(); ?>
        <section class="itp-card itp-action-needed">
            <div class="itp-card-head"><div><h2>Action needed</h2><p>Quick checks that make the plugin feel like a traffic quality control room, without connecting ad accounts.</p></div><a class="itp-btn itp-btn-ghost" href="<?php echo esc_url(admin_url('admin.php?page=itp-presells')); ?>">Review bridges</a></div>
            <div class="itp-action-grid">
                <?php foreach ($action_items as $item) : ?>
                    <div class="<?php echo esc_attr($item['class']); ?>"><strong><?php echo esc_html($item['label']); ?></strong><span><?php echo esc_html($item['text']); ?></span></div>
                <?php endforeach; ?>
            </div>
        </section>

        <section class="itp-dashboard-grid">
            <div class="itp-card itp-flow-card">
                <div>
                    <h2>Traffic quality snapshot</h2>
                    <p>First-party signals collected from your bridge pages. No ad account connection required.</p>
                    <div class="itp-quality-stack" data-itp-quality='<?php echo esc_attr(wp_json_encode($quality)); ?>'></div>
                    <div class="itp-legend"><span class="valid"></span> Valid: <?php echo esc_html($quality['valid']); ?><span class="engaged"></span> Engaged: <?php echo esc_html($quality['engaged']); ?><span class="low"></span> Low intent: <?php echo esc_html($quality['low_intent']); ?><span class="sus"></span> Risk: <?php echo esc_html($quality['suspicious'] + $quality['bot_like'] + $quality['repeated']); ?></div>
                </div>
                <div class="itp-donut" style="--p:<?php echo esc_attr(min(100, max(0, $forward_rate))); ?>"><strong><?php echo esc_html($forward_rate); ?>%</strong><span>Forward rate</span></div>
            </div>

            <div class="itp-card itp-mini-panel">
                <h2>Intent signals</h2>
                <div class="itp-metric-list">
                    <div><span>Valid sessions</span><strong><?php echo esc_html($quality['valid']); ?></strong></div>
                    <div><span>Engaged sessions</span><strong><?php echo esc_html($quality['engaged']); ?></strong></div>
                    <div><span>Risk sessions</span><strong<?php echo $this->countup_attrs($quality['suspicious'] + $quality['bot_like'] + $quality['repeated']); ?>><?php echo esc_html($quality['suspicious'] + $quality['bot_like'] + $quality['repeated']); ?></strong></div>
                </div>
            </div>
        </section>

        <section class="itp-card">
            <div class="itp-card-head"><div><h2>Traffic trend</h2><p>Visits and forward clicks by day. Hover the chart to inspect each day.</p></div><a class="itp-btn itp-btn-ghost" href="<?php echo esc_url(admin_url('admin.php?page=itp-analytics')); ?>">Open bridge analytics</a></div>
            <canvas class="itp-chart" data-itp-line='<?php echo esc_attr(wp_json_encode($daily)); ?>'></canvas>
        </section>

        <section class="itp-card itp-country-map-card">
            <div class="itp-card-head"><div><h2>Country traffic map</h2><p>Visual country signal from bridge visits and forward clicks. No GA4 integration required.</p></div><a class="itp-btn itp-btn-ghost" href="<?php echo esc_url(admin_url('admin.php?page=itp-live')); ?>">Open Live View</a></div>
            <?php echo $this->country_map_html($country_rows, 'No country signal yet.'); ?>
        </section>

        <section class="itp-insight-grid">
            <div class="itp-card">
                <h2>Top sources</h2>
                <p>Detected from UTMs, click IDs and referrers.</p>
                <?php echo wp_kses_post($this->mini_breakdown_html($source_rows, 'No source data yet.')); ?>
            </div>
            <div class="itp-card">
                <h2>Traffic type mix</h2>
                <p>Organic, paid, native, social, direct and referral signals inferred from UTMs/referrers.</p>
                <?php echo wp_kses_post($this->mini_breakdown_html($channel_rows, 'No channel data yet.')); ?>
            </div>
            <div class="itp-card">
                <h2>Countries / IP signal</h2>
                <p>Server/CDN country when available; otherwise inferred from timezone/language.</p>
                <?php echo wp_kses_post($this->mini_breakdown_html($country_rows, 'Country unknown until the server provides geo headers or visitor timezone/language is captured.')); ?>
            </div>
            <div class="itp-card">
                <h2>Devices</h2>
                <p>Basic device mix from the local tracking script.</p>
                <?php echo wp_kses_post($this->mini_breakdown_html($device_rows, 'No device data yet.')); ?>
            </div>
            <div class="itp-card">
                <h2>Risk signals</h2>
                <p>Reasons detected by the bridge quality layer.</p>
                <?php echo wp_kses_post($this->mini_breakdown_html($risk_rows, 'No obvious risk signals yet.')); ?>
            </div>
        </section>

        <section class="itp-card">
            <h2>Who this plugin is for</h2>
            <div class="itp-use-grid">
                <div><strong>Traffic arbitrage</strong><p>Keep ads off the first stop and measure intent before sending visitors to monetized content.</p></div>
                <div><strong>Affiliate campaigns</strong><p>Route paid visitors through simple bridge pages with real product or offer choices.</p></div>
                <div><strong>Native/social ads</strong><p>Use lightweight campaign links for Taboola, Outbrain, Meta, TikTok or similar traffic sources.</p></div>
                <div><strong>Traffic quality layer</strong><p>Reduce blind redirects, catch weak intent, preserve UTMs and keep the money page cleaner.</p></div>
            </div>
        </section>
        <?php
        $this->admin_shell_end();
    }


    private function dashboard_action_items() {
        $posts = get_posts(['post_type' => self::CPT, 'post_status' => ['publish', 'draft'], 'numberposts' => 200]);
        $no_clicks = 0;
        $review = 0;
        $missing_preview = 0;
        foreach ($posts as $p) {
            $stats = $this->stats_for_presell($p->ID);
            $validate = $this->validate_bridge($p->ID);
            if ($stats['visits'] > 0 && $stats['clicks'] === 0) $no_clicks++;
            if ($validate['score'] < 90) $review++;
            if (!$validate['asset_ok']) $missing_preview++;
        }
        $q = $this->quality_breakdown(0);
        $risk = (int)$q['suspicious'] + (int)$q['bot_like'] + (int)$q['repeated'];
        $items = [];
        $items[] = $review > 0
            ? ['class' => 'warn', 'label' => $review . ' bridge(s) need setup review', 'text' => 'Open My Bridges and validate the pages before sending paid traffic.']
            : ['class' => 'ok', 'label' => 'All bridges pass setup checks', 'text' => 'No obvious missing setup items detected.'];
        $items[] = $no_clicks > 0
            ? ['class' => 'warn', 'label' => $no_clicks . ' bridge(s) have visits but no clicks', 'text' => 'Check CTA clarity, delay settings or destination links.']
            : ['class' => 'ok', 'label' => 'Forward click path is working', 'text' => 'No visited bridge is stuck without clicks.'];
        $items[] = $missing_preview > 0
            ? ['class' => 'warn', 'label' => $missing_preview . ' bridge(s) missing visual preview', 'text' => 'Add a logo, card image or landing preview for stronger trust.']
            : ['class' => 'ok', 'label' => 'Visual previews ready', 'text' => 'Bridges have a visual asset or destination preview.'];
        $items[] = $risk > 0
            ? ['class' => 'danger', 'label' => $risk . ' risk signal(s) detected', 'text' => 'Review repeated visits, bot-like agents or very fast clicks in analytics.']
            : ['class' => 'ok', 'label' => 'No risk signals detected', 'text' => 'The current traffic quality layer looks clean.'];
        return $items;
    }

    private function bridge_quality_summary($id) {
        $q = $this->quality_breakdown($id);
        // My Bridges is a management overview, so keep the badge friendly.
        // Detailed risk reasons are still available inside Analytics / Reports.
        if ((int)$q['engaged'] > 0) return ['label' => 'Engaged', 'class' => 'engaged'];
        if ((int)$q['valid'] > 0) return ['label' => 'Valid', 'class' => 'valid'];
        if ((int)$q['low_intent'] > 0) return ['label' => 'Low intent', 'class' => 'low'];
        return ['label' => 'No data', 'class' => 'none'];
    }


    public function page_live_view() {
        if (!current_user_can('manage_options')) return;
        $this->handle_actions();
        $stats30 = $this->live_stats(30);
        $stats5 = $this->live_stats(5);
        $recent = $this->recent_live_events(50);
        $source_rows = $this->breakdown_rows('source', 0, 8, "created_at >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 MINUTE)");
        $country_rows = $this->breakdown_rows('country_code', 0, 8, "created_at >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 MINUTE)");
        $device_rows = $this->breakdown_rows('device', 0, 6, "created_at >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 MINUTE)");
        $risk_rows = $this->risk_rows_recent(0, 6, 30);
        $this->admin_shell_start('Live View', 'Real-time bridge activity from the last 30 minutes. No GA4 or ad-account integration required.');
        ?>
        <section class="itp-stat-grid four">
            <div class="itp-stat"><span>Active sessions / 30 min</span><strong><?php echo esc_html($stats30['active_sessions']); ?></strong></div>
            <div class="itp-stat"><span>Active sessions / 5 min</span><strong><?php echo esc_html($stats5['active_sessions']); ?></strong></div>
            <div class="itp-stat"><span>Events / 30 min</span><strong><?php echo esc_html($stats30['events']); ?></strong></div>
            <div class="itp-stat"><span>Forward clicks / 30 min</span><strong><?php echo esc_html($stats30['clicks']); ?></strong></div>
        </section>
        <section class="itp-card itp-country-map-card">
            <div class="itp-card-head"><div><h2>Live country map</h2><p>Country signal from bridge events in the last 30 minutes. Larger dots mean more live events.</p></div><a class="itp-btn itp-btn-ghost" href="<?php echo esc_url(admin_url('admin.php?page=itp-live')); ?>">Refresh</a></div>
            <?php echo $this->country_map_html($country_rows, 'No live country signal in the last 30 minutes.'); ?>
        </section>
        <section class="itp-dashboard-grid">
            <div class="itp-card">
                <div class="itp-card-head"><div><h2>Live campaign pulse</h2><p>Shows what is happening on bridge pages right now: visits, heartbeats and forward clicks.</p></div><a class="itp-btn itp-btn-ghost" href="<?php echo esc_url(admin_url('admin.php?page=itp-live')); ?>">Refresh</a></div>
                <div class="itp-live-feed">
                    <?php if (empty($recent)) : ?><div class="itp-empty-mini">No live events in the last 30 minutes yet.</div><?php endif; ?>
                    <?php foreach ($recent as $ev) : ?>
                        <div class="itp-live-row">
                            <strong><?php echo esc_html(strtoupper($ev['event_type'])); ?></strong>
                            <span><?php echo esc_html($ev['created_at']); ?></span>
                            <em><?php echo esc_html($this->clean_source_label($ev['source'] ?: 'direct/internal')); ?> · <?php echo esc_html($ev['country_code'] ?: 'Unknown'); ?> · <?php echo esc_html($ev['device'] ?: 'unknown'); ?></em>
                            <small><?php echo esc_html($this->quality_label($ev['quality'])); ?> — <?php echo esc_html($ev['reason'] ?: 'Session signal'); ?></small>
                        </div>
                    <?php endforeach; ?>
                </div>
            </div>
            <div class="itp-card itp-mini-panel">
                <h2>Live risk signal</h2>
                <?php echo wp_kses_post($this->mini_breakdown_html($risk_rows, 'No suspicious live signal detected.')); ?>
            </div>
        </section>
        <section class="itp-insight-grid three">
            <div class="itp-card"><h2>Top live sources</h2><?php echo wp_kses_post($this->mini_breakdown_html($source_rows, 'No source data in the last 30 minutes.')); ?></div>
            <div class="itp-card"><h2>Live countries</h2><?php echo wp_kses_post($this->mini_breakdown_html($country_rows, 'No country signal in the last 30 minutes.')); ?></div>
            <div class="itp-card"><h2>Live devices</h2><?php echo wp_kses_post($this->mini_breakdown_html($device_rows, 'No device data in the last 30 minutes.')); ?></div>
        </section>
        <?php
        $this->admin_shell_end();
    }

    public function page_presells() {
        if (!current_user_can('manage_options')) return;
        $this->handle_actions();
        $query = new WP_Query([
            'post_type' => self::CPT,
            'post_status' => ['publish', 'draft'],
            'posts_per_page' => 200,
            'orderby' => 'menu_order date',
            'order' => 'ASC',
        ]);
        $validate_id = isset($_GET['validate']) ? absint($_GET['validate']) : 0;
        $this->admin_shell_start('My Bridges', 'Manage, reorder, pause, duplicate, validate and access your bridge pages in one place.');
        echo $this->analytics_period_bar(admin_url('admin.php?page=itp-presells'));
        ?>
        <div class="itp-list-tools"><input type="search" class="itp-search" placeholder="Search bridge..."><a class="itp-btn" href="<?php echo esc_url(admin_url('admin.php?page=itp-create')); ?>">+ Create New Bridge</a></div>
        <section class="itp-presell-list" data-itp-presell-list>
        <?php if ($query->have_posts()) : while ($query->have_posts()) : $query->the_post();
            $id = get_the_ID();
            $meta = $this->bridge_meta($id);
            $stats = $this->stats_for_presell($id);
            $status = get_post_status($id) === 'publish' ? 'Active' : 'Paused';
            $status_class = get_post_status($id) === 'publish' ? 'active' : 'paused';
            $thumb = $meta['logo'] ?: get_the_post_thumbnail_url($id, 'thumbnail');
            $validate = $this->validate_bridge($id);
            $quality_summary = $this->bridge_quality_summary($id);
            $is_vsl_row = in_array(($meta['template'] ?? ''), ['vsl_video','vsl_landing'], true);
            $vsl_list = $is_vsl_row ? $this->vsl_performance_stats($id) : null;
            $landing_pages = (($meta['template'] ?? '') === 'vsl_landing') ? $this->landing_pages_for_bridge($id) : [];
            $current_landing = null;
            foreach ($landing_pages as $lp_tmp) {
                if (!empty($lp_tmp['current'])) { $current_landing = $lp_tmp; break; }
            }
            if (!$current_landing && !empty($landing_pages)) {
                $current_landing = end($landing_pages);
            }
            $landing_url = $current_landing['url'] ?? '';
            $landing_search = trim(implode(' ', array_map(function($lp){ return ($lp['path'] ?? '') . ' ' . ($lp['title'] ?? ''); }, $landing_pages)));
            $campaign_name = trim(get_the_title());
            if ($campaign_name === '' || $campaign_name === 'New Bridge') $campaign_name = 'Untitled campaign #' . (int)$id;
            $open_url = (($meta['template'] ?? '') === 'vsl_video') ? add_query_arg('itp_preview', '1', $this->vsl_embed_url($id)) : ((($meta['template'] ?? '') === 'vsl_landing' && $landing_url) ? $landing_url : get_permalink($id));
            $copy_url = ((($meta['template'] ?? '') === 'vsl_landing' && $landing_url) ? $landing_url : get_permalink($id));
            ?>
            <article class="itp-presell-row <?php echo $is_vsl_row ? 'itp-presell-vsl' : ''; ?>" data-id="<?php echo esc_attr($id); ?>" data-search="<?php echo esc_attr(strtolower($campaign_name . ' ' . $meta['slug'] . ' ' . $meta['category'] . ' ' . $landing_url . ' ' . $landing_search)); ?>">
                <div class="itp-drag">⋮⋮</div>
                <div class="itp-thumb"><?php if ($thumb) echo '<img src="' . esc_url($thumb) . '" alt="">'; else echo '<span>IT</span>'; ?></div>
                <div class="itp-title-wrap">
                    <strong><?php echo esc_html($campaign_name); ?></strong>
                    <?php if (($meta['template'] ?? '') === 'vsl_landing') : ?>
                        <em class="itp-internal-player-note">Internal player URL: <?php echo esc_html(wp_parse_url(get_permalink($id), PHP_URL_PATH) ?: '/' . trim($meta['slug'], '/') . '/'); ?> <small>embed/player only</small></em>
                        <?php if (!empty($landing_pages)) : ?>
                            <div class="itp-row-landing-list"><span>Public landing pages</span><?php foreach ($landing_pages as $lp) : ?><a class="<?php echo !empty($lp['current']) ? 'is-current' : ''; ?>" href="<?php echo esc_url($lp['url']); ?>" target="_blank" rel="noopener"><?php echo esc_html($lp['path']); ?><?php if (!empty($lp['current'])) : ?><small>Current</small><?php endif; ?></a><?php endforeach; ?></div>
                        <?php else : ?>
                            <em class="itp-landing-url-note">No landing page connected yet.</em>
                        <?php endif; ?>
                    <?php else : ?>
                        <em>/<?php echo esc_html($meta['slug']); ?>/</em>
                    <?php endif; ?>
                </div>
                <div class="itp-badges">
                    <span><small>Template</small><?php echo esc_html($this->template_label($meta['template'])); ?></span>
                    <span class="<?php echo esc_attr($status_class); ?>"><small>Status</small><?php echo esc_html($status); ?></span>
                    <span class="health-<?php echo esc_attr($validate['class']); ?>"><small>Health</small><?php echo esc_html($validate['score']); ?>/100 · <?php echo esc_html($validate['title']); ?></span>
                    <span class="quality-<?php echo esc_attr($quality_summary['class']); ?>"><small>Quality</small><?php echo esc_html($quality_summary['label']); ?></span>
                    <span><small>Category</small><?php echo esc_html($meta['category'] ?: 'None'); ?></span>
                    <?php if ($is_vsl_row && $vsl_list) : ?>
                        <span class="itp-badge-vsl itp-badge-vsl-premium"><small>VSL signal</small><b><i><?php echo esc_html($vsl_list['vsl_view']); ?><em>views</em></i><i><?php echo esc_html($vsl_list['vsl_play']); ?><em>plays</em></i><i><?php echo esc_html($vsl_list['play_rate']); ?>%<em>rate</em></i><i><?php echo esc_html($vsl_list['vsl_cta_click']); ?><em>CTA</em></i></b></span>
                    <?php else : ?>
                        <span><small>Stats</small><?php echo esc_html($stats['visits']); ?> visits / <?php echo esc_html($stats['clicks']); ?> clicks</span>
                    <?php endif; ?>
                </div>
                <div class="itp-row-icons">
                    <a title="Copy campaign URL" href="#" data-itp-copy-url="<?php echo esc_attr($copy_url); ?>">⧉</a>
                    <a title="Open campaign page" href="<?php echo esc_url($open_url); ?>" target="_blank" rel="noopener">↗</a>
                    <a title="Edit" href="<?php echo esc_url(admin_url('admin.php?page=itp-create&id=' . $id)); ?>">✎</a>
                    <a title="Analytics" href="<?php echo esc_url(admin_url('admin.php?page=itp-analytics&id=' . $id . $this->analytics_query_suffix())); ?>">↗</a>
                    <a title="Evidence report" href="<?php echo esc_url(admin_url('admin.php?page=itp-reports&id=' . $id)); ?>">▣</a>
                    <a title="Duplicate" href="<?php echo esc_url(wp_nonce_url(admin_url('admin.php?page=itp-presells&itp_action=duplicate&id=' . $id), 'itp_action_' . $id)); ?>">⧉</a>
                    <a class="itp-status-toggle <?php echo esc_attr($status_class); ?>" title="Toggle status" href="<?php echo esc_url(wp_nonce_url(admin_url('admin.php?page=itp-presells&itp_action=toggle&id=' . $id), 'itp_action_' . $id)); ?>"><?php echo get_post_status($id) === 'publish' ? '✓' : '⏸'; ?></a>
                    <a title="Validate" href="<?php echo esc_url(admin_url('admin.php?page=itp-presells&validate=' . $id)); ?>">✓</a>
                    <a class="danger" title="Delete" onclick="return confirm('Delete this bridge?');" href="<?php echo esc_url(wp_nonce_url(admin_url('admin.php?page=itp-presells&itp_action=delete&id=' . $id), 'itp_action_' . $id)); ?>">🗑</a>
                </div>
                <?php if ($validate_id === $id) : $this->validate_modal($id, $validate); endif; ?>
            </article>
        <?php endwhile; wp_reset_postdata(); else : ?>
            <div class="itp-empty"><h2>No bridge pages yet.</h2><p>Create your first clean first step for paid traffic.</p><a class="itp-btn" href="<?php echo esc_url(admin_url('admin.php?page=itp-create')); ?>">Create Bridge</a></div>
        <?php endif; ?>
        </section>
        <?php
        $this->admin_shell_end();
    }

    public function page_create() {
        if (!current_user_can('manage_options')) return;
        // Handle landing-page actions (delete/set current/etc.) before rendering or saving.
        // v0.18.219 fixes delete links inside the Create/Edit screen.
        $this->handle_actions();
        $this->handle_save_bridge();
        $id = isset($_GET['id']) ? absint($_GET['id']) : 0;
        $is_edit = $id && get_post_type($id) === self::CPT;
        $meta = $is_edit ? $this->bridge_meta($id) : $this->empty_bridge_meta();
        if (isset($_GET['demo'])) {
            $demo = sanitize_key(wp_unslash($_GET['demo']));
            $meta = array_merge($meta, $demo === 'cookie' ? $this->demo_cookie_meta() : $this->demo_meta());
        }
        $tpl = $meta['template'] ?: 'blue_cards';
        $is_vsl = in_array($tpl, ['vsl_video','vsl_landing'], true);
        $is_landing = in_array($tpl, ['vsl_landing'], true);

        $active_vsl_tab = sanitize_key($_GET['itp_tab'] ?? 'video');
        $allowed_vsl_tabs = ['video','style','autoplay','turbo','cta','pause','hook','resume','embed'];
        if ($active_vsl_tab === 'pixels') $active_vsl_tab = 'embed';
        if (!in_array($active_vsl_tab, $allowed_vsl_tabs, true)) $active_vsl_tab = 'video';
        
        $this->admin_shell_start($is_edit ? 'Edit Campaign' : 'Create Bridge', 'Choose a campaign type, build the first layer, and track traffic quality.');
        ?>
        <form method="post" class="itp-create-form itp-create-form-studio">
            <?php wp_nonce_field('itp_save_bridge', 'itp_nonce'); ?>
            <input type="hidden" name="bridge_id" value="<?php echo esc_attr($id); ?>">
            <input type="hidden" name="itp_active_vsl_tab" value="<?php echo esc_attr(sanitize_key($_GET['itp_tab'] ?? 'video')); ?>" data-itp-active-vsl-tab-field>
            <section class="itp-create-hero itp-create-hero-studio">
                <div><span class="itp-kicker">Campaign builder</span><h2>Build a bridge, VSL or landing page.</h2><p>Use a clean first layer, preserve UTMs, track clicks and measure video engagement without turning this into a complex page builder.</p></div>
                <div class="itp-hero-actions"><a class="itp-btn itp-btn-white" href="<?php echo esc_url(admin_url('admin.php?page=itp-presells')); ?>">My Bridges</a><button type="submit" class="itp-btn">Save Campaign</button></div>
            </section>

            <section class="itp-card itp-template-picker-card">
    <?php
    $templates = $this->templates();

    $card_style_keys = ['blue_cards','news_cards','link_hub','offer_grid','review_clean','segment_choice'];
    $mode_keys = ['cookie_modal','vsl_video','vsl_landing'];

    $is_card_style = in_array($tpl, $card_style_keys, true);
    $current_card_style = $is_card_style ? $tpl : 'blue_cards';
    $current_card_template = $templates[$current_card_style] ?? $templates['blue_cards'];
    ?>

    <div class="itp-card-head">
        <div>
            <h2>1. Choose campaign type</h2>
            <p>Choose one campaign mode for this bridge. Switching mode converts this campaign; it does not create a second URL. Use My Bridges → duplicate if you want separate versions.</p>
        </div>
        <button type="button" class="itp-btn itp-btn-ghost" data-itp-fill-demo>Fill sample content</button>
    </div>

    <div class="itp-template-grid itp-template-grid-campaigns">
        <label class="itp-template <?php echo $is_card_style ? 'selected' : ''; ?>" data-itp-cards-bridge>
            <input type="radio" name="template" value="cards_bridge" <?php checked($is_card_style); ?>>
            <span class="itp-template-preview cards_bridge"><i></i><i></i><i></i></span>
<strong>Cards Bridge</strong>
<em>Customizable</em>
<p>Build a clean bridge page with editable design, text, buttons, cards and tracking.</p>
        </label>

        <?php foreach ($mode_keys as $key) : $template = $templates[$key]; ?>
            <label class="itp-template <?php echo $tpl === $key ? 'selected' : ''; ?>">
                <input type="radio" name="template" value="<?php echo esc_attr($key); ?>" <?php checked($tpl, $key); ?>>
                <span class="itp-template-preview <?php echo esc_attr($key); ?>"><i></i><i></i><i></i></span>
                <strong><?php echo esc_html($template['name']); ?></strong>
                <em><?php echo esc_html($template['tag']); ?></em>
                <p><?php echo esc_html($template['desc']); ?></p>
            </label>
        <?php endforeach; ?>
    </div>
</section>

            <?php $this->render_vsl_studio_fields($id, $is_edit, $meta); ?>
            <?php $this->render_landing_setup_fields($meta); ?>

            <section class="itp-card itp-card-bridge-studio-card <?php echo in_array($tpl, ['cookie_modal','vsl_video','vsl_landing','landing_page'], true) ? 'itp-template-options-hidden' : ''; ?>" data-itp-hide-on-template="cookie_modal,vsl_video,vsl_landing,landing_page" data-itp-card-studio>
                <div class="itp-card-head">
                    <div>
                        <h2>2. Cards Bridge Studio</h2>
                        <p>Edit the public bridge in one place. Use the left tabs, then watch the preview on the right.</p>
                    </div>
                    <a class="itp-btn itp-btn-ghost" href="<?php echo esc_url($is_edit ? get_permalink($id) : '#'); ?>" target="_blank">Open public page</a>
                </div>

                <div class="itp-card-studio-grid">
                    <div class="itp-card-studio-controls">
                        <nav class="itp-card-studio-tabs" aria-label="Cards Bridge sections">
                            <button type="button" class="active" data-itp-card-tab="design">Design</button>
                            <button type="button" data-itp-card-tab="text">Text / URL</button>
                            <button type="button" data-itp-card-tab="buttons">Buttons</button>
                            <button type="button" data-itp-card-tab="cards">Cards</button>
                        </nav>

                        <div class="itp-card-studio-panel active" data-itp-card-panel="design">
                            <h3>Design / Appearance</h3>
                            <p>Keep it simple: background, accent, card and text colors. The public page updates after save.</p>

                            <label class="itp-toggle itp-compact-toggle">
                                <input type="checkbox" name="bridge_show_badge" value="1" <?php checked($meta['bridge_show_badge'], '1'); ?>>
                                <span></span>
                                <div>
                                    <strong>Show logo on top</strong>
                                    <em>Optional. Upload a brand/avatar image below. No “IT” fallback.</em>
                                </div>
                            </label>

                            <div class="itp-form-grid logo-row itp-logo-image-row" data-itp-logo-image-row>
                                <label>Logo / avatar image URL
                                    <input name="logo" value="<?php echo esc_url($meta['logo']); ?>" placeholder="https://.../logo.png">
                                </label>
                                <button type="button" class="itp-btn itp-btn-ghost" data-itp-media="logo" data-itp-media-type="image">Media</button><button type="button" class="itp-btn itp-btn-light" data-itp-clear-field="logo">Remove</button>
                            </div>
                            <p class="itp-field-help">Logo recommended: square image, 512×512 PNG/WebP/SVG. It only appears when “Show logo on top” is enabled.</p>

                            <div class="itp-form-grid two">
                                <label>Background mode
                                    <select name="bridge_bg_mode" data-itp-bg-mode>
                                        <option value="gradient" <?php selected($meta['bridge_bg_mode'], 'gradient'); ?>>Gradient</option>
                                        <option value="solid" <?php selected($meta['bridge_bg_mode'], 'solid'); ?>>Solid color</option>
                                        <option value="image" <?php selected($meta['bridge_bg_mode'], 'image'); ?>>Background image</option>
                                    </select>
                                </label>
                                <label>Card layout
                                    <select name="bridge_card_layout" data-itp-card-layout>
                                        <option value="stacked" <?php selected($meta['bridge_card_layout'] ?? 'stacked', 'stacked'); ?>>Centered stack</option>
                                        <option value="grid" <?php selected($meta['bridge_card_layout'] ?? 'stacked', 'grid'); ?>>Grid cards</option>
                                        <option value="links" <?php selected($meta['bridge_card_layout'] ?? 'stacked', 'links'); ?>>Link buttons</option>
                                    </select>
                                </label>
                            </div>
                            <p class="itp-field-help"><strong>Card layout:</strong> Centered stack = simple bridge, Grid cards = news/products, Link buttons = link hub without images.</p>

                            <div class="itp-form-grid logo-row itp-bg-image-row" data-itp-bg-image-row>
                                <label>Background image URL
                                    <input name="bridge_bg_image" value="<?php echo esc_url($meta['bridge_bg_image']); ?>" placeholder="https://.../background.jpg">
                                </label>
                                <button type="button" class="itp-btn itp-btn-ghost" data-itp-media="bridge_bg" data-itp-media-type="image">Media</button><button type="button" class="itp-btn itp-btn-light" data-itp-clear-field="bridge_bg_image">Remove</button>
                            </div>
                            <p class="itp-field-help" data-itp-bg-image-help>Background image recommended: 1920×1080 or larger, JPG/WebP. It is fixed and cropped to cover the page while visitors scroll.</p>

                            <div class="itp-color-rows">
                                <div class="itp-color-row"><label>Background</label><input type="color" name="bridge_bg_color" value="<?php echo esc_attr($meta['bridge_bg_color']); ?>"></div>
                                <div class="itp-color-row" data-itp-bg-color-2-row><label>Background 2</label><input type="color" name="bridge_bg_color_2" value="<?php echo esc_attr($meta['bridge_bg_color_2']); ?>"></div>
                                <div class="itp-color-row"><label>Accent / Button</label><input type="color" name="bridge_button_bg" value="<?php echo esc_attr($meta['bridge_button_bg']); ?>"></div>
                                <div class="itp-color-row"><label>Card</label><input type="color" name="bridge_card_bg" value="<?php echo esc_attr($meta['bridge_card_bg']); ?>"></div>
                                <div class="itp-color-row"><label>Primary text</label><input type="color" name="bridge_title_color" value="<?php echo esc_attr($meta['bridge_title_color']); ?>"></div>
                                <div class="itp-color-row"><label>Secondary text</label><input type="color" name="bridge_text_color" value="<?php echo esc_attr($meta['bridge_text_color']); ?>"></div>
                            </div>

                            <input type="hidden" name="bridge_button_text" value="<?php echo esc_attr($meta['bridge_button_text']); ?>">
                            <input type="hidden" name="bridge_card_border" value="<?php echo esc_attr($meta['bridge_card_border']); ?>">
                            <input type="hidden" name="bridge_card_title" value="<?php echo esc_attr($meta['bridge_card_title']); ?>">
                            <input type="hidden" name="bridge_card_text" value="<?php echo esc_attr($meta['bridge_card_text']); ?>">
                        </div>

                        <div class="itp-card-studio-panel" data-itp-card-panel="text">
                            <h3>Text / URL</h3>
                            <p>Set the internal name, public URL, headline and short visitor copy. Logo is controlled in the Design tab.</p>
                            <div class="itp-form-grid two">
                                <label>Campaign name<input name="title" value="<?php echo esc_attr($is_edit ? get_the_title($id) : $meta['title']); ?>" placeholder="Ex: Finance offer - VSL test 01"></label>
                                <label>URL slug<input name="slug" value="<?php echo esc_attr($meta['slug']); ?>" placeholder="Ex: story-options"></label>
                            </div>
                            <div class="itp-final-url">Final URL: <strong data-itp-final-url><?php echo esc_html($this->preview_url($meta['slug'] ?: 'your-slug')); ?></strong></div>
                            <div class="itp-form-grid two">
                                <label>Category optional<input name="category" value="<?php echo esc_attr($meta['category']); ?>" placeholder="Ex: Taboola / Affiliate / Lead Gen"></label>
                                <label>Visitor headline<input name="headline" value="<?php echo esc_attr($meta['headline']); ?>" placeholder="Ex: Choose where to continue"></label>
                            </div>
                            <label>Visitor short copy<textarea name="short_copy" placeholder="Short paragraph for the visitor. No technical plugin text."><?php echo esc_textarea($meta['short_copy']); ?></textarea></label>
                        </div>

                        <div class="itp-card-studio-panel" data-itp-card-panel="buttons">
                            <h3>Top Buttons</h3>
                            <p>Optional buttons shown above the cards. Turn this off when the card itself is the main click path.</p>
                            <label class="itp-toggle itp-compact-toggle">
                                <input type="checkbox" name="bridge_show_top_buttons" value="1" <?php checked($meta['bridge_show_top_buttons'], '1'); ?>>
                                <span></span>
                                <div>
                                    <strong>Show top buttons above cards</strong>
                                    <em>Card buttons are edited in the Cards tab. These are extra hero buttons.</em>
                                </div>
                            </label>
                            <div data-itp-top-buttons-fields>
                                <label>Primary button URL<input name="top_cta_url" value="<?php echo esc_url($meta['top_cta_url']); ?>" placeholder="https://example.com/final-page"></label>
                                <label>Primary button label
                                    <select name="top_cta_label_preset" data-itp-label-select="top">
                                        <?php foreach ($this->cta_presets() as $label) echo '<option value="' . esc_attr($label) . '" ' . selected($meta['top_cta_label'], $label, false) . '>' . esc_html($label) . '</option>'; ?>
                                        <option value="custom" <?php selected(!in_array($meta['top_cta_label'], $this->cta_presets(), true)); ?>>Custom</option>
                                    </select>
                                    <input class="itp-custom-label-input" name="top_cta_label" value="<?php echo esc_attr($meta['top_cta_label']); ?>" placeholder="Write custom label">
                                </label>
                                <label>Secondary button URL optional<input name="secondary_cta_url" value="<?php echo esc_url($meta['secondary_cta_url']); ?>" placeholder="https://example.com/alternate-page"></label>
                                <label>Secondary button label optional
                                    <select name="secondary_cta_label_preset" data-itp-label-select="secondary">
                                        <?php foreach ($this->cta_presets() as $label) echo '<option value="' . esc_attr($label) . '" ' . selected($meta['secondary_cta_label'], $label, false) . '>' . esc_html($label) . '</option>'; ?>
                                        <option value="custom" <?php selected(!in_array($meta['secondary_cta_label'], $this->cta_presets(), true)); ?>>Custom</option>
                                    </select>
                                    <input class="itp-custom-label-input" name="secondary_cta_label" value="<?php echo esc_attr($meta['secondary_cta_label']); ?>" placeholder="Write custom label">
                                </label>
                                <p class="itp-field-help">Use the primary button for one main CTA. Use the secondary button only when there is a real second path.</p>
                            </div>
                        </div>

                        <div class="itp-card-studio-panel" data-itp-card-panel="cards">
                            <div class="itp-card-head itp-card-panel-head"><div><h3>Cards / destinations</h3><p>Each card can be an article, product, offer, guide or destination. Recommended card image: 1200×675 (16:9). Images are cropped to fit.</p></div><button type="button" class="itp-btn itp-btn-ghost" data-itp-add-card>+ Add card</button></div>
                            <div class="itp-cards-editor" data-itp-cards-editor>
                                <?php
                                $cards = is_array($meta['cards']) && !empty($meta['cards']) ? $meta['cards'] : $this->sample_cards();
                                foreach ($cards as $idx => $card) {
                                    $this->card_editor_row($idx, $card);
                                }
                                ?>
                            </div>
                        </div>
                    </div>

                    <aside class="itp-card-studio-preview">
                        <div class="itp-preview-toolbar itp-preview-toolbar-devices"><strong>Live preview</strong><span>Visual guide only. Save to update the public page.</span><div class="itp-preview-device-toggle"><button type="button" class="active" data-itp-preview-device="desktop" data-itp-preview-target="bridge">Desktop</button><button type="button" data-itp-preview-device="mobile" data-itp-preview-target="bridge">Mobile</button></div></div>
                        <div class="itp-bridge-mini-preview is-desktop" data-itp-bridge-preview>
                            <div class="itp-preview-badge" data-itp-preview-badge><img data-itp-preview-badge-img alt=""></div>
                            <h3 data-itp-preview-headline><?php echo esc_html($meta['headline']); ?></h3>
                            <p data-itp-preview-copy><?php echo esc_html($meta['short_copy']); ?></p>
                            <div class="itp-preview-actions" data-itp-preview-actions></div>
                            <div class="itp-preview-card-list" data-itp-preview-card-list></div>
                        </div>
                    </aside>
                </div>
            </section>
<section class="itp-card itp-cookie-options itp-cookie-studio-card <?php echo $tpl === 'cookie_modal' ? '' : 'itp-template-options-hidden'; ?>" data-itp-template-options="cookie_modal" data-itp-cookie-studio>
                <div class="itp-card-head">
                    <div>
                        <h2>2. Cookie Modal Studio</h2>
                        <p>Edit the cookie-style bridge with a live preview. Normal page URLs use WordPress screenshot preview after save; direct screenshots show instantly.</p>
                    </div>
                    <a class="itp-btn itp-btn-ghost" href="<?php echo esc_url($is_edit ? get_permalink($id) : '#'); ?>" target="_blank">Open public page</a>
                </div>

                <div class="itp-card-studio-grid itp-cookie-studio-grid">
                    <div class="itp-card-studio-controls itp-cookie-studio-controls">
                        <nav class="itp-card-studio-tabs itp-cookie-studio-tabs" aria-label="Cookie Modal sections">
                            <button type="button" class="active" data-itp-cookie-tab="page">Preview / Page</button>
                            <button type="button" data-itp-cookie-tab="text">Modal / Text</button>
                            <button type="button" data-itp-cookie-tab="buttons">Actions / Links</button>
                            <button type="button" data-itp-cookie-tab="design">Overlay / Background</button>
                        </nav>

                        <div class="itp-card-studio-panel active" data-itp-cookie-panel="page">
                            <h3>Preview / Page</h3>
                            <p>Set the public URL, final destination, and background preview behind the cookie modal.</p>
                            <div class="itp-form-grid two">
                                <label>Campaign name<input name="title" value="<?php echo esc_attr($is_edit ? get_the_title($id) : $meta['title']); ?>" placeholder="Ex: Cookie bridge - offer test"></label>
                                <label>URL slug<input name="slug" value="<?php echo esc_attr($meta['slug']); ?>" placeholder="Ex: offer-preview"></label>
                            </div>
                            <div class="itp-final-url">Final URL: <strong data-itp-final-url><?php echo esc_html($this->preview_url($meta['slug'] ?: 'your-slug')); ?></strong></div>
                            <div class="itp-form-grid two">
                                <label>Category optional<input name="category" value="<?php echo esc_attr($meta['category']); ?>" placeholder="Ex: Taboola / Affiliate / Lead Gen"></label>
                                <label>Destination URL<input name="top_cta_url" value="<?php echo esc_url($meta['top_cta_url']); ?>" placeholder="https://example.com/final-page"></label>
                            </div>
                            <div class="itp-form-grid logo-row itp-cookie-bg-row">
                                <label>Landing page URL or screenshot image URL<input name="background_image" value="<?php echo esc_url($meta['background_image']); ?>" placeholder="Paste final page URL or upload screenshot image"></label>
                                <button type="button" class="itp-btn itp-btn-ghost" data-itp-media="background" data-itp-media-type="image">Media</button>
                                <button type="button" class="itp-btn itp-btn-light" data-itp-clear-field="background_image">Remove</button>
                            </div>
                            <p class="itp-field-help">For fastest preview, upload a screenshot image. If you paste a normal page URL, the public bridge uses WordPress mShots; the live preview shows the same screenshot service when available.</p>
                        </div>

                        <div class="itp-card-studio-panel" data-itp-cookie-panel="text">
                            <h3>Modal / Text</h3>
                            <p>Edit the exact text inside the cookie popup and the most important colors in one place.</p>
                            <label>Modal title<input name="cookie_title" value="<?php echo esc_attr($meta['cookie_title']); ?>" placeholder="Cookie Policy"></label>
                            <label>Modal message<textarea name="cookie_copy" placeholder="This page uses cookies to personalize content, analyze visits and improve your experience."><?php echo esc_textarea($meta['cookie_copy']); ?></textarea></label>
                            <div class="itp-form-grid two">
                                <label>Accept button text<input name="cookie_accept_label" value="<?php echo esc_attr($meta['cookie_accept_label']); ?>" placeholder="Accept"></label>
                                <label>Privacy link text<input name="cookie_policy_label" value="<?php echo esc_attr($meta['cookie_policy_label']); ?>" placeholder="Privacy Policy"></label>
                            </div>
                            <div class="itp-cookie-inline-style-grid">
                                <div class="itp-color-row"><label>Cookie box background</label><input type="color" name="cookie_modal_bg" value="<?php echo esc_attr($meta['cookie_modal_bg']); ?>"></div>
                                <div class="itp-color-row"><label>Cookie text</label><input type="color" name="cookie_modal_text" value="<?php echo esc_attr($meta['cookie_modal_text']); ?>"></div>
                                <div class="itp-color-row"><label>Accept button</label><input type="color" name="cookie_accept_bg" value="<?php echo esc_attr($meta['cookie_accept_bg']); ?>"></div>
                                <div class="itp-color-row"><label>Accept text</label><input type="color" name="cookie_accept_text" value="<?php echo esc_attr($meta['cookie_accept_text']); ?>"></div>
                                <div class="itp-color-row"><label>Privacy link</label><input type="color" name="cookie_link_color" value="<?php echo esc_attr($meta['cookie_link_color']); ?>"></div>
                            </div>
                            <p class="itp-field-help">These controls update the live preview on the right. The redirect URLs are in Actions / Links.</p>
                        </div>

                        <div class="itp-card-studio-panel" data-itp-cookie-panel="buttons">
                            <h3>Actions / Links</h3>
                            <p>Choose where each action sends the visitor.</p>
                            <label>Accept redirect URL<input name="cookie_accept_url" value="<?php echo esc_url($meta['cookie_accept_url'] ?? ''); ?>" placeholder="Leave blank to use Destination URL"></label>
                            <div class="itp-form-grid two">
                                <label>Close link text<input name="cookie_close_label" value="<?php echo esc_attr($meta['cookie_close_label']); ?>" placeholder="Close"></label>
                                <label>Close redirect URL<input name="cookie_close_url" value="<?php echo esc_url($meta['cookie_close_url'] ?? ''); ?>" placeholder="https://example.com/back-or-home"></label>
                            </div>
                            <label>Privacy / policy URL<input name="cookie_policy_url" value="<?php echo esc_url($meta['cookie_policy_url'] ?? ''); ?>" placeholder="Leave blank to use plugin privacy page"></label>
                            <p class="itp-field-help"><strong>Tip:</strong> Accept can use the Destination URL from Preview / Page, or a specific URL here. Close and privacy can also redirect to URLs you define.</p>
                        </div>

                        <div class="itp-card-studio-panel" data-itp-cookie-panel="design">
                            <h3>Overlay / Background</h3>
                            <p>Control the background screenshot effect behind the cookie popup.</p>
                            <label class="itp-range-label">Overlay darkness <strong><span data-itp-range-output="cookie_overlay_opacity"><?php echo esc_html($meta['cookie_overlay_opacity']); ?></span>%</strong><input type="range" min="0" max="90" name="cookie_overlay_opacity" value="<?php echo esc_attr($meta['cookie_overlay_opacity']); ?>"></label>
                            <label class="itp-range-label">Background blur <strong><span data-itp-range-output="cookie_blur"><?php echo esc_html($meta['cookie_blur']); ?></span>px</strong><input type="range" min="0" max="12" name="cookie_blur" value="<?php echo esc_attr($meta['cookie_blur']); ?>"></label>
                            <p class="itp-field-help">The popup colors are in Modal / Text so the user edits the cookie box and message together.</p>
                        </div>
                    </div>

                    <aside class="itp-card-studio-preview itp-cookie-studio-preview">
                        <div class="itp-preview-toolbar itp-preview-toolbar-devices"><strong>Live preview</strong><span>Screenshot preview is visual only.</span><div class="itp-preview-device-toggle"><button type="button" class="active" data-itp-preview-device="desktop" data-itp-preview-target="cookie">Desktop</button><button type="button" data-itp-preview-device="mobile" data-itp-preview-target="cookie">Mobile</button></div></div>
                        <div class="itp-cookie-mini-preview is-desktop" data-itp-cookie-preview>
                            <div class="itp-cookie-mini-bg" data-itp-cookie-preview-bg><div class="itp-cookie-mini-fake"><i></i><i></i><i></i><b></b><b></b><b></b></div></div>
                            <div class="itp-cookie-mini-overlay" data-itp-cookie-preview-overlay></div>
                            <section class="itp-cookie-mini-modal" data-itp-cookie-preview-modal>
                                <button type="button" class="itp-cookie-mini-close" data-itp-cookie-preview-close><?php echo esc_html($meta['cookie_close_label']); ?></button>
                                <h3 data-itp-cookie-preview-title><?php echo esc_html($meta['cookie_title']); ?></h3>
                                <p><span data-itp-cookie-preview-copy><?php echo esc_html($meta['cookie_copy']); ?></span> <a href="#" data-itp-cookie-preview-policy><?php echo esc_html($meta['cookie_policy_label']); ?></a></p>
                                <span class="itp-cookie-mini-accept" data-itp-cookie-preview-accept><?php echo esc_html($meta['cookie_accept_label']); ?></span>
                            </section>
                        </div>
                    </aside>
                </div>
            </section>
<section class="itp-card">
                <h2>Footer / campaign notes</h2>
                <div class="itp-form-grid two">
                    <label>Small footer note optional<input name="footer_note" value="<?php echo esc_attr($meta['footer_note']); ?>" placeholder="Ex: Choose one option to continue."></label>
                    <label>Button delay override optional<input type="number" min="0" max="60" name="button_delay" value="<?php echo esc_attr($meta['button_delay']); ?>" placeholder="0"></label>
                </div>
                <p class="itp-note">Contact, Privacy Policy and Terms links are automatically added when connected in Settings.</p>
            </section>

            <?php $this->render_global_pixels_notice(); ?>

            <div class="itp-sticky-save"><button type="submit" class="itp-btn">Save Campaign</button><a class="itp-btn itp-btn-ghost" href="<?php echo esc_url(admin_url('admin.php?page=itp-presells')); ?>">Cancel</a></div>
        </form>
        <?php
        $this->admin_shell_end();
    }


    private function render_global_pixels_notice() {
        $px = $this->global_pixel_settings();
        $active_pixels = 0;
        if (trim((string)($px['global_head'] ?? '')) !== '') $active_pixels++;
        if (trim((string)($px['global_footer'] ?? '')) !== '') $active_pixels++;
        foreach ($this->normalized_custom_pixels($px) as $row) {
            if (($row['enabled'] ?? '0') === '1' && trim((string)($row['code'] ?? '')) !== '') $active_pixels++;
        }
        $rules = $this->normalized_conversion_events($px);
        ?>
        <section class="itp-card itp-global-pixel-notice">
            <div class="itp-card-head">
                <div>
                    <span class="itp-section-kicker">Pixels / Conversions</span>
                    <h2>Global pixel center</h2>
                    <p>Pixels are managed once for the whole site. Every Bridge, Card, Cookie, VSL and VSL + Landing Page loads the same saved pixels; purchases are credited back to the correct bridge when the checkout returns order data.</p>
                </div>
                <div class="itp-row-actions">
                    <span class="itp-count-pill"><?php echo esc_html($active_pixels); ?> active pixel<?php echo $active_pixels === 1 ? '' : 's'; ?></span>
                    <span class="itp-count-pill"><?php echo esc_html(count($rules)); ?> rule<?php echo count($rules) === 1 ? '' : 's'; ?></span>
                    <a class="itp-btn" href="<?php echo esc_url(admin_url('admin.php?page=itp-pixels')); ?>">Open Pixels</a>
                </div>
            </div>
            <p class="itp-note">Use the Pixels menu for Taboola, Meta, Microsoft UET, Google tags, affiliate networks and custom scripts. Paste each platform once only. For revenue inside Invalid Traffic Analytics, connect a thank-you helper or postback when the checkout can send order data back.</p>
        </section>
        <?php
    }

    private function render_pixels_conversions_section($meta) {
        $custom_pixels = $this->normalized_custom_pixels($meta);
        $conversion_events = $this->normalized_conversion_events($meta);
        $pixel_rows = $custom_pixels ? $custom_pixels : [$this->empty_custom_pixel()];
        $conversion_rows = $conversion_events ? $conversion_events : [$this->empty_conversion_event()];
        ?>
        <section class="itp-card itp-pixels-conversions-card">
            <div class="itp-card-head itp-pixels-head">
                <div>
                    <span class="itp-section-kicker">Pixels / Conversions</span>
                    <h2>Pixels / Conversions</h2>
                    <p>Install base pixels, add custom scripts, and define conversion events for this campaign. Each bridge can have several custom pixels and several conversion rules.</p>
                </div>
                <div class="itp-pixel-status-stack">
                    <span><?php echo esc_html(count($custom_pixels)); ?> custom pixel<?php echo count($custom_pixels) === 1 ? '' : 's'; ?></span>
                    <span><?php echo esc_html(count($conversion_events)); ?> conversion rule<?php echo count($conversion_events) === 1 ? '' : 's'; ?></span>
                </div>
            </div>

            <label>Meta description optional<input name="meta_description" value="<?php echo esc_attr($meta['meta_description']); ?>" placeholder="Short internal description for previews or reports."></label>

            <div class="itp-pixel-console-grid">
                <div class="itp-pixel-console-box">
                    <h3>Base head pixel</h3>
                    <p>Use for platform base pixels that should load early: Taboola, Meta, Microsoft UET, Google tag or other trusted code.</p>
                    <textarea class="itp-code-area" name="custom_head" placeholder="Paste base pixel code for the page head."><?php echo esc_textarea($meta['custom_head']); ?></textarea>
                </div>
                <div class="itp-pixel-console-box">
                    <h3>Base footer / body script</h3>
                    <p>Use for scripts that should load after the bridge/VSL markup. Leave empty for a clean bridge.</p>
                    <textarea class="itp-code-area" name="custom_footer" placeholder="Paste trusted footer/body scripts here."><?php echo esc_textarea($meta['custom_footer']); ?></textarea>
                </div>
            </div>

            <div class="itp-pixel-manager" data-itp-custom-pixels>
                <div class="itp-manager-title">
                    <div><h3>Custom pixels</h3><p>Add as many extra pixel blocks as the campaign needs. Disable a row instead of deleting it when testing.</p></div>
                    <button type="button" class="itp-btn itp-btn-ghost" data-itp-add-custom-pixel>+ Add custom pixel</button>
                </div>
                <div class="itp-repeater-list" data-itp-custom-pixel-list>
                    <?php foreach ($pixel_rows as $idx => $pixel) : $this->render_custom_pixel_row((int)$idx, $pixel); endforeach; if (empty($pixel_rows)) : ?><div class="itp-empty-note">No separated pixel blocks yet. Use <strong>+ Add pixel</strong> only if you want a named block for a platform.</div><?php endif; ?>
                </div>
            </div>

            <div class="itp-pixel-manager" data-itp-conversion-events>
                <div class="itp-manager-title">
                    <div><h3>Conversion events</h3><p>Create platform events or custom JS triggers. Use URL match for thank-you/success pages, or events like CTA click and lead submit.</p></div>
                    <button type="button" class="itp-btn" data-itp-add-conversion-event>+ Add conversion event</button>
                </div>
                <div class="itp-repeater-list" data-itp-conversion-event-list>
                    <?php foreach ($conversion_rows as $idx => $event) : $this->render_conversion_event_row((int)$idx, $event); endforeach; ?>
                </div>
                <div class="itp-conversion-test-note">
                    <strong>Developer test:</strong> open a campaign page and run <code>window.ITPTrackConversion('manual-test')</code> in the console to save an internal conversion and fire matching conversion rules.
                </div>
            </div>
        </section>
        <?php
    }

    private function empty_custom_pixel() {
        return ['enabled' => '0', 'name' => '', 'placement' => 'head', 'code' => ''];
    }

    private function empty_conversion_event() {
        return ['enabled' => '0', 'name' => '', 'trigger' => 'lead_submit', 'url_contains' => '', 'value' => '', 'currency' => 'USD', 'internal' => '1', 'code' => ''];
    }

    private function normalized_custom_pixels($meta) {
        $rows = isset($meta['custom_pixels']) && is_array($meta['custom_pixels']) ? $meta['custom_pixels'] : [];
        $out = [];
        foreach ($rows as $row) {
            if (!is_array($row)) continue;
            $code = (string)($row['code'] ?? '');
            $name = (string)($row['name'] ?? '');
            if (trim($code) === '' && trim($name) === '') continue;
            $placement = (string)($row['placement'] ?? 'head');
            if (!in_array($placement, ['head','footer'], true)) $placement = 'head';
            $out[] = [
                'enabled' => !empty($row['enabled']) && $row['enabled'] !== '0' ? '1' : '0',
                'name' => $name,
                'placement' => $placement,
                'code' => $code,
            ];
        }
        return $out;
    }

    private function normalized_conversion_events($meta) {
        $rows = isset($meta['conversion_events']) && is_array($meta['conversion_events']) ? $meta['conversion_events'] : [];
        $out = [];
        $allowed = ['page_view','click','vsl_view','vsl_play','vsl_unmute','vsl_25','vsl_50','vsl_75','vsl_100','vsl_cta_view','vsl_cta_click','lead_view','lead_submit','lead_redirect','conversion'];
        foreach ($rows as $row) {
            if (!is_array($row)) continue;
            $name = (string)($row['name'] ?? '');
            $code = (string)($row['code'] ?? '');
            $url_contains = (string)($row['url_contains'] ?? '');
            if (trim($name) === '' && trim($code) === '' && trim($url_contains) === '') continue;
            $trigger = sanitize_key($row['trigger'] ?? 'lead_submit');
            if (!in_array($trigger, $allowed, true)) $trigger = 'lead_submit';
            $currency = strtoupper(preg_replace('/[^A-Z]/', '', (string)($row['currency'] ?? 'USD')));
            if ($currency === '') $currency = 'USD';
            $out[] = [
                'enabled' => !empty($row['enabled']) && $row['enabled'] !== '0' ? '1' : '0',
                'name' => $name,
                'trigger' => $trigger,
                'url_contains' => $url_contains,
                'value' => (string)($row['value'] ?? ''),
                'currency' => substr($currency, 0, 8),
                'internal' => !empty($row['internal']) && $row['internal'] !== '0' ? '1' : '0',
                'code' => $code,
            ];
        }
        return $out;
    }

    private function render_custom_pixel_row($idx, $pixel) {
        $pixel = array_merge($this->empty_custom_pixel(), is_array($pixel) ? $pixel : []);
        ?>
        <div class="itp-pixel-row" data-itp-custom-pixel-row>
            <div class="itp-repeater-row-head">
                <strong>Custom pixel</strong>
                <label class="itp-mini-switch"><input type="checkbox" name="custom_pixels[<?php echo esc_attr($idx); ?>][enabled]" value="1" <?php checked($pixel['enabled'], '1'); ?>><span></span> Enabled</label>
                <button type="button" class="itp-btn itp-btn-light" data-itp-remove-repeater-row>Remove</button>
            </div>
            <div class="itp-form-grid two">
                <label>Pixel name<input name="custom_pixels[<?php echo esc_attr($idx); ?>][name]" value="<?php echo esc_attr($pixel['name']); ?>" placeholder="Ex: Taboola base, Microsoft UET, Meta"></label>
                <label>Placement<select name="custom_pixels[<?php echo esc_attr($idx); ?>][placement]"><option value="head" <?php selected($pixel['placement'], 'head'); ?>>Head</option><option value="footer" <?php selected($pixel['placement'], 'footer'); ?>>Footer / body</option></select></label>
            </div>
            <label>Pixel code<textarea class="itp-code-area" name="custom_pixels[<?php echo esc_attr($idx); ?>][code]" placeholder="Paste the pixel script here."><?php echo esc_textarea($pixel['code']); ?></textarea></label>
        </div>
        <?php
    }

    private function render_conversion_event_row($idx, $event) {
        $event = array_merge($this->empty_conversion_event(), is_array($event) ? $event : []);
        $trigger_options = [
            'page_view' => 'Thank-you / URL match',
            'lead_submit' => 'Lead submitted',
            'vsl_cta_click' => 'VSL CTA clicked',
            'click' => 'Any bridge click',
            'vsl_play' => 'VSL play',
            'vsl_50' => 'VSL watched 50%',
            'vsl_100' => 'VSL watched 100%',
            'conversion' => 'Manual JS conversion',
            'lead_view' => 'Lead popup opened',
            'lead_redirect' => 'Lead redirect',
            'vsl_cta_view' => 'VSL CTA appeared',
        ];
        ?>
        <div class="itp-conversion-row" data-itp-conversion-event-row>
            <div class="itp-repeater-row-head">
                <strong>Conversion rule</strong>
                <label class="itp-mini-switch"><input type="checkbox" name="conversion_events[<?php echo esc_attr($idx); ?>][enabled]" value="1" <?php checked($event['enabled'], '1'); ?>><span></span> Enabled</label>
                <label class="itp-mini-switch"><input type="checkbox" name="conversion_events[<?php echo esc_attr($idx); ?>][internal]" value="1" <?php checked($event['internal'], '1'); ?>><span></span> Save as Invalid Traffic conversion</label>
                <button type="button" class="itp-btn itp-btn-light" data-itp-remove-repeater-row>Remove</button>
            </div>
            <div class="itp-form-grid four">
                <label>Event name<input name="conversion_events[<?php echo esc_attr($idx); ?>][name]" value="<?php echo esc_attr($event['name']); ?>" placeholder="Ex: Purchase, Subscribe, Checkout"></label>
                <label>Trigger<select name="conversion_events[<?php echo esc_attr($idx); ?>][trigger]">
                    <?php foreach ($trigger_options as $key => $label) : ?><option value="<?php echo esc_attr($key); ?>" <?php selected($event['trigger'], $key); ?>><?php echo esc_html($label); ?></option><?php endforeach; ?>
                </select></label>
                <label>Value optional<input name="conversion_events[<?php echo esc_attr($idx); ?>][value]" value="<?php echo esc_attr($event['value']); ?>" placeholder="Ex: 29.90"></label>
                <label>Currency<input name="conversion_events[<?php echo esc_attr($idx); ?>][currency]" value="<?php echo esc_attr($event['currency']); ?>" placeholder="USD"></label>
            </div>
            <label>URL contains optional<input name="conversion_events[<?php echo esc_attr($idx); ?>][url_contains]" value="<?php echo esc_attr($event['url_contains']); ?>" placeholder="Ex: /thank-you/ or /success.php. Used mainly with URL match."></label>
            <label>Custom conversion JavaScript<textarea class="itp-code-area" name="conversion_events[<?php echo esc_attr($idx); ?>][code]" placeholder="Example: _tfa.push({notify:'event', name:'make_purchase', id:123}); or window.uetq.push('event','purchase',{});"><?php echo esc_textarea($event['code']); ?></textarea></label>
        </div>
        <?php
    }

    private function sanitize_custom_pixels_from_post() {
        $rows = isset($_POST['custom_pixels']) && is_array($_POST['custom_pixels']) ? wp_unslash($_POST['custom_pixels']) : [];
        $out = [];
        foreach ($rows as $row) {
            if (!is_array($row)) continue;
            $code = current_user_can('unfiltered_html') ? (string)($row['code'] ?? '') : sanitize_textarea_field((string)($row['code'] ?? ''));
            $name = sanitize_text_field((string)($row['name'] ?? ''));
            if (trim($code) === '' && trim($name) === '') continue;
            $placement = sanitize_key($row['placement'] ?? 'head');
            if (!in_array($placement, ['head','footer'], true)) $placement = 'head';
            $out[] = [
                'enabled' => !empty($row['enabled']) ? '1' : '0',
                'name' => $name,
                'placement' => $placement,
                'code' => $code,
            ];
        }
        return $out;
    }

    private function sanitize_conversion_events_from_post() {
        $rows = isset($_POST['conversion_events']) && is_array($_POST['conversion_events']) ? wp_unslash($_POST['conversion_events']) : [];
        $out = [];
        $allowed = ['page_view','click','vsl_view','vsl_play','vsl_unmute','vsl_25','vsl_50','vsl_75','vsl_100','vsl_cta_view','vsl_cta_click','lead_view','lead_submit','lead_redirect','conversion'];
        foreach ($rows as $row) {
            if (!is_array($row)) continue;
            $name = sanitize_text_field((string)($row['name'] ?? ''));
            $code = current_user_can('unfiltered_html') ? (string)($row['code'] ?? '') : sanitize_textarea_field((string)($row['code'] ?? ''));
            $url_contains = sanitize_text_field((string)($row['url_contains'] ?? ''));
            if (trim($name) === '' && trim($code) === '' && trim($url_contains) === '') continue;
            $trigger = sanitize_key($row['trigger'] ?? 'lead_submit');
            if (!in_array($trigger, $allowed, true)) $trigger = 'lead_submit';
            $currency = strtoupper(preg_replace('/[^A-Z]/', '', (string)($row['currency'] ?? 'USD')));
            if ($currency === '') $currency = 'USD';
            $value = preg_replace('/[^0-9.,-]/', '', (string)($row['value'] ?? ''));
            $out[] = [
                'enabled' => !empty($row['enabled']) ? '1' : '0',
                'name' => $name,
                'trigger' => $trigger,
                'url_contains' => $url_contains,
                'value' => substr($value, 0, 32),
                'currency' => substr($currency, 0, 8),
                'internal' => !empty($row['internal']) ? '1' : '0',
                'code' => $code,
            ];
        }
        return $out;
    }


    private function global_pixel_defaults() {
        return [
            'enabled' => '1',
            'global_head' => '',
            'global_footer' => '',
            'custom_pixels' => [],
            'conversion_events' => [],
            'auto_url_conversions' => '1',
            'sitewide_runtime' => '1',
            'public_token' => '',
            'webhook_secret' => '',
            'webhook_history' => [],
        ];
    }

    private function global_pixel_settings() {
        $raw = get_option(self::PIXEL_OPT, []);
        if (!is_array($raw)) $raw = [];
        $settings = array_merge($this->global_pixel_defaults(), $raw);
        $changed = false;
        if (empty($settings['public_token'])) {
            $settings['public_token'] = wp_generate_password(32, false, false);
            $changed = true;
        }
        if (empty($settings['webhook_secret'])) {
            $settings['webhook_secret'] = wp_generate_password(48, false, false);
            $changed = true;
        }
        if ($changed) {
            update_option(self::PIXEL_OPT, $settings, false);
        }
        return $settings;
    }

    private function save_global_pixel_settings_from_post() {
        if (!current_user_can('manage_options')) return false;
        if ($_SERVER['REQUEST_METHOD'] !== 'POST' || empty($_POST['itp_pixels_nonce'])) return false;
        check_admin_referer('itp_save_pixels', 'itp_pixels_nonce');
        $existing_pixel_settings = get_option(self::PIXEL_OPT, []);
        if (!is_array($existing_pixel_settings)) $existing_pixel_settings = [];
        $existing_custom_pixels = isset($existing_pixel_settings['custom_pixels']) && is_array($existing_pixel_settings['custom_pixels']) ? $existing_pixel_settings['custom_pixels'] : [];
        $existing_conversion_events = isset($existing_pixel_settings['conversion_events']) && is_array($existing_pixel_settings['conversion_events']) ? $existing_pixel_settings['conversion_events'] : [];
        $data = [
            'enabled' => isset($_POST['itp_pixels_enabled']) ? '1' : '0',
            'global_head' => current_user_can('unfiltered_html') ? wp_unslash($_POST['custom_head'] ?? '') : sanitize_textarea_field(wp_unslash($_POST['custom_head'] ?? '')),
            'global_footer' => current_user_can('unfiltered_html') ? wp_unslash($_POST['custom_footer'] ?? '') : sanitize_textarea_field(wp_unslash($_POST['custom_footer'] ?? '')),
            'custom_pixels' => isset($_POST['custom_pixels']) ? $this->sanitize_custom_pixels_from_post() : $existing_custom_pixels,
            'conversion_events' => isset($_POST['conversion_events']) ? $this->sanitize_conversion_events_from_post() : $existing_conversion_events,
            'public_token' => sanitize_text_field(wp_unslash($_POST['public_token'] ?? ($existing_pixel_settings['public_token'] ?? ''))),
            'webhook_secret' => sanitize_text_field(wp_unslash($_POST['webhook_secret'] ?? ($existing_pixel_settings['webhook_secret'] ?? ''))),
            'auto_url_conversions' => isset($_POST['auto_url_conversions']) ? '1' : (isset($existing_pixel_settings['auto_url_conversions']) ? (string)$existing_pixel_settings['auto_url_conversions'] : '1'),
            'sitewide_runtime' => isset($_POST['sitewide_runtime']) ? '1' : '0',
        ];
        if (!empty($existing_pixel_settings['webhook_history']) && is_array($existing_pixel_settings['webhook_history'])) {
            $data['webhook_history'] = array_slice($existing_pixel_settings['webhook_history'], 0, 80);
        }
        update_option(self::PIXEL_OPT, $data, false);
        wp_safe_redirect(admin_url('admin.php?page=itp-pixels&saved=1'));
        exit;
    }

    public function render_global_pixel_head() {
        if (is_admin()) return;
        $settings = $this->global_pixel_settings();
        if (($settings['enabled'] ?? '1') !== '1') return;
        $out = '';
        if (!empty($settings['global_head'])) $out .= "\n" . $settings['global_head'] . "\n";
        foreach ($this->normalized_custom_pixels($settings) as $pixel) {
            if ($pixel['enabled'] !== '1' || $pixel['placement'] !== 'head' || trim((string)$pixel['code']) === '') continue;
            $out .= "\n<!-- Invalid Traffic global pixel: " . esc_html($pixel['name'] ?: 'head') . " -->\n" . $pixel['code'] . "\n";
        }
        $rules = $this->public_conversion_rules($settings);
        $ctx = [
            'ajax' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('itp_global_conversion'),
            'publicToken' => (string)($settings['public_token'] ?? ''),
            'rules' => $rules,
            'autoUrl' => ($settings['auto_url_conversions'] ?? '1') === '1',
            'home' => home_url('/'),
        ];
        $out .= "\n<script>window.ITP_GLOBAL_PIXEL=" . wp_json_encode($ctx, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT) . ";</script>\n";
        echo $out; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
    }

    public function render_global_pixel_footer() {
        if (is_admin()) return;
        $settings = $this->global_pixel_settings();
        if (($settings['enabled'] ?? '1') !== '1') return;
        $out = '';
        if (!empty($settings['global_footer'])) $out .= "\n" . $settings['global_footer'] . "\n";
        foreach ($this->normalized_custom_pixels($settings) as $pixel) {
            if ($pixel['enabled'] !== '1' || $pixel['placement'] !== 'footer' || trim((string)$pixel['code']) === '') continue;
            $out .= "\n<!-- Invalid Traffic global pixel: " . esc_html($pixel['name'] ?: 'footer') . " -->\n" . $pixel['code'] . "\n";
        }
        if (($settings['sitewide_runtime'] ?? '1') === '1') {
            $out .= "\n<script src=\"" . esc_url(plugin_dir_url(__FILE__) . 'assets/it-global.js?ver=' . self::VERSION) . "\" defer></script>\n";
        }
        echo $out; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
    }

    private function tracking_head_markup($meta) {
        $out = '';
        if (!empty($meta['custom_head'])) $out .= "\n" . $meta['custom_head'] . "\n";
        foreach ($this->normalized_custom_pixels($meta) as $pixel) {
            if ($pixel['enabled'] !== '1' || $pixel['placement'] !== 'head' || trim((string)$pixel['code']) === '') continue;
            $out .= "\n<!-- Invalid Traffic custom pixel: " . esc_html($pixel['name'] ?: 'head') . " -->\n" . $pixel['code'] . "\n";
        }
        return $out;
    }

    private function tracking_footer_markup($meta) {
        $out = '';
        if (!empty($meta['custom_footer'])) $out .= "\n" . $meta['custom_footer'] . "\n";
        foreach ($this->normalized_custom_pixels($meta) as $pixel) {
            if ($pixel['enabled'] !== '1' || $pixel['placement'] !== 'footer' || trim((string)$pixel['code']) === '') continue;
            $out .= "\n<!-- Invalid Traffic custom pixel: " . esc_html($pixel['name'] ?: 'footer') . " -->\n" . $pixel['code'] . "\n";
        }
        return $out;
    }

    private function public_conversion_rules($meta = null) {
        $global = $this->global_pixel_settings();
        $rows = $this->normalized_conversion_events($global);
        if (!$rows && is_array($meta)) {
            $rows = $this->normalized_conversion_events($meta);
        }
        $out = [];
        foreach ($rows as $idx => $event) {
            if ($event['enabled'] !== '1') continue;
            $out[] = [
                'id' => 'rule_' . (int)$idx . '_' . substr(md5(($event['name'] ?? '') . '|' . ($event['trigger'] ?? '')), 0, 8),
                'name' => (string)($event['name'] ?: 'Conversion'),
                'trigger' => (string)($event['trigger'] ?: 'lead_submit'),
                'urlContains' => (string)($event['url_contains'] ?? ''),
                'value' => (string)($event['value'] ?? ''),
                'currency' => (string)($event['currency'] ?? 'USD'),
                'internal' => ($event['internal'] ?? '0') === '1',
                'code' => (string)($event['code'] ?? ''),
            ];
        }
        return $out;
    }

    private function tracking_runtime_script($meta, $id) {
        $rules = $this->public_conversion_rules($meta);
        $ctx = [
            'bridgeId' => (int)$id,
            'bridgeTitle' => get_the_title((int)$id),
            'landingUrl' => home_url(add_query_arg([], $GLOBALS['wp']->request ?? '')),
        ];
        return "\n<script>window.ITP_CONVERSION_RULES=" . wp_json_encode($rules, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT) . ";window.ITP_PIXEL_CONTEXT=" . wp_json_encode($ctx, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT) . ";</script>\n";
    }

    private function vsl_embed_url($post_or_slug) {
        if (is_numeric($post_or_slug)) {
            $post = get_post((int)$post_or_slug);
            $slug = $post ? $post->post_name : '';
        } else {
            $slug = sanitize_title((string)$post_or_slug);
        }
        if (!$slug) {
            $slug = 'your-vsl';
        }
        return home_url(user_trailingslashit('it-vsl-embed/' . $slug));
    }

    private function render_vsl_studio_fields($id, $is_edit, $meta) {
        if (($meta['vsl_overlay_template'] ?? '') === 'large') {
            $meta['vsl_overlay_template'] = 'full';
        }
        $tpl = $meta['template'] ?: 'blue_cards';
        $show = in_array($tpl, ['vsl_video','vsl_landing'], true);
        $preview_url = $is_edit ? add_query_arg('itp_preview', '1', $this->vsl_embed_url($id)) : $this->vsl_embed_url($meta['slug'] ?: 'your-vsl');
        $admin_preview_video = $meta['vsl_preview_video_url'] ?: $meta['vsl_video_url'];
        $has_video = !empty($admin_preview_video);
        $permalink = $is_edit ? get_permalink($id) : '';
        $embed_url = $is_edit ? $this->vsl_embed_url($id) : '';
        $embed_preview_url = $is_edit ? add_query_arg('itp_preview', '1', $embed_url) : '';
        $player_js_url = $is_edit ? $this->vsl_player_js_url($id) : '';
        $player_slug = $is_edit ? ($meta['slug'] ?: get_post_field('post_name', $id)) : 'your-vsl';
        $js = $is_edit ? '<it-vsl-player id="itp-vsl-' . esc_attr($player_slug) . '" style="display:block;margin:0 auto;width:100%;"></it-vsl-player> <script type="text/javascript">var s=document.createElement("script");s.src="' . esc_url($player_js_url) . '";s.async=true;document.head.appendChild(s);</script>' : '';
        $speed_code = $is_edit ? '<script>!function(w,p){w._itp_vsl_start=w._itp_vsl_start||(p&&p.timeOrigin?p.timeOrigin+p.now():Date.now())}(window,performance);</script>\n<link rel="preload" href="' . esc_url($player_js_url) . '" as="script">\n<link rel="preconnect" href="' . esc_url(home_url('/')) . '">\n<link rel="dns-prefetch" href="//' . esc_attr(wp_parse_url(home_url('/'), PHP_URL_HOST)) . '">' : '';
        $iframe = $is_edit ? '<iframe src="' . esc_url($embed_url) . '" width="100%" height="760" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" loading="eager" style="display:block;width:100%;margin:0 auto;border:0;overflow:hidden;background:transparent;"></iframe>' : '';

        // 0.18.152: the Studio preview stage must use the same real video aspect
        // as the iframe/public renderer. Otherwise Edit overlay position saves x/y/w
        // against a fake canvas and the final embed looks shifted or resized.
        $admin_aspect_w = 9;
        $admin_aspect_h = 16;
        $admin_aspect_probe_url = !empty($meta['vsl_video_url']) ? $meta['vsl_video_url'] : $admin_preview_video;
        if ($admin_aspect_probe_url && function_exists('attachment_url_to_postid')) {
            $admin_aspect_att = attachment_url_to_postid($admin_aspect_probe_url);
            if ($admin_aspect_att) {
                $admin_aspect_meta = wp_get_attachment_metadata($admin_aspect_att);
                $maybe_w = isset($admin_aspect_meta['width']) ? (int)$admin_aspect_meta['width'] : 0;
                $maybe_h = isset($admin_aspect_meta['height']) ? (int)$admin_aspect_meta['height'] : 0;
                if ($maybe_w > 0 && $maybe_h > 0) {
                    $admin_aspect_w = $maybe_w;
                    $admin_aspect_h = $maybe_h;
                }
            }
        }
        $admin_aspect_ratio = $admin_aspect_w / max(1, $admin_aspect_h);
        $admin_preview_ratio_class = $admin_aspect_ratio < 0.82 ? 'itp-preview-ratio-vertical' : ($admin_aspect_ratio > 1.22 ? 'itp-preview-ratio-horizontal' : 'itp-preview-ratio-square');
        $admin_preview_frame_style = '--itp-vsl-preview-aspect:' . (int)$admin_aspect_w . ' / ' . (int)$admin_aspect_h . ';';
        ?>
        <section class="itp-card itp-vsl-studio-card itp-vsl-studio-card-v2 <?php echo $show ? '' : 'itp-template-options-hidden'; ?>" data-itp-template-options="vsl_video" data-itp-template-options-also="vsl_landing">
            <div class="itp-card-head itp-vsl-card-head">
                <div><span class="itp-kicker">Video player</span><h2>2. VSL Studio</h2><p>WordPress Media MP4 player with smart autoplay, progress, CTA, recovery thumbnail, mini hooks, pixels and clean embed output.</p></div>
                <div class="itp-row-actions"><?php if ($is_edit) : ?><button type="button" class="itp-btn itp-btn-ghost" data-itp-go-vsl-tab="embed">Embed / Copy</button><?php endif; ?></div>
            </div>
            <div class="itp-vsl-studio itp-vsl-studio-v2" data-itp-vsl-studio>
                <aside class="itp-vsl-control-panel">
                    <nav class="itp-vsl-tabs" aria-label="VSL editor sections">
                        <button type="button" class="<?php echo $active_vsl_tab === 'video' ? 'active' : ''; ?>" data-itp-vsl-tab="video"><span>01</span> Video</button>
                        <button type="button" class="<?php echo $active_vsl_tab === 'style' ? 'active' : ''; ?>" data-itp-vsl-tab="style"><span>02</span> Controls / Smart Progress</button>
                        <button type="button" class="<?php echo $active_vsl_tab === 'autoplay' ? 'active' : ''; ?>" data-itp-vsl-tab="autoplay"><span>03</span> Smart Autoplay</button>
                        <button type="button" class="<?php echo $active_vsl_tab === 'turbo' ? 'active' : ''; ?>" data-itp-vsl-tab="turbo"><span>04</span> Turbo</button>
                        <button type="button" class="<?php echo $active_vsl_tab === 'cta' ? 'active' : ''; ?>" data-itp-vsl-tab="cta"><span>05</span> Action Button</button>
                        <button type="button" class="<?php echo $active_vsl_tab === 'pause' ? 'active' : ''; ?>" data-itp-vsl-tab="pause"><span>06</span> Thumb Recovery</button>
                        <button type="button" class="<?php echo $active_vsl_tab === 'hook' ? 'active' : ''; ?>" data-itp-vsl-tab="hook"><span>07</span> Mini Hook</button>
                        <button type="button" class="<?php echo $active_vsl_tab === 'resume' ? 'active' : ''; ?>" data-itp-vsl-tab="resume"><span>08</span> Continue Watching</button>
                        <button type="button" class="<?php echo $active_vsl_tab === 'embed' ? 'active' : ''; ?>" data-itp-vsl-tab="embed"><span>09</span> Embed</button>
                    </nav>
                    <div class="itp-vsl-save-inline">
                        <button type="submit" class="itp-btn itp-btn-small" data-itp-save-vsl>Save & update embed</button>
                        <span><?php echo $is_edit ? 'Use this after changing a VSL option. The copied JS/iFrame always uses the last saved version.' : 'First time? Save this VSL to generate the JavaScript/iFrame embed.'; ?></span>
                    </div>
                    <div class="itp-vsl-settings-panel">
                        <input type="hidden" name="vsl_source_type" value="wp_media">
                        <input type="hidden" name="vsl_embed_code" value="">

                        <div class="itp-vsl-panel <?php echo $active_vsl_tab === 'style' ? 'active' : ''; ?>" data-itp-vsl-panel="style">
                            <h3>Controls / Smart Progress</h3>
                            <p class="itp-note">Choose which visitor controls appear over the VSL and how the progress bar looks. The click-to-hear overlay is configured in Smart Autoplay.</p>
                            <div class="itp-vsl-mini-section itp-vsl-controls-list">
                                <strong>Control style</strong>
                                <p class="itp-note">These are optional visitor controls shown over the video. The Smart Autoplay overlay controls the first click-to-hear play. The colors below style only the large pause/resume button.</p>
                                <div class="itp-vsl-style-grid">
                                    <label>Primary color<input type="color" name="vsl_ctrl_color" value="<?php echo esc_attr($meta['vsl_ctrl_color'] ?? '#ffffff'); ?>"><small>Used by the large pause/resume icon only.</small></label>
                                    <label>Background<input type="color" name="vsl_ctrl_bg" value="<?php echo esc_attr($meta['vsl_ctrl_bg'] ?? '#111827'); ?>"><small>Used by the large pause/resume button only.</small></label>
                                </div>
                                <label class="itp-checkrow"><span>Large pause/resume button</span><input type="checkbox" name="vsl_ctrl_big_play" value="1" <?php checked($meta['vsl_ctrl_big_play'], '1'); ?>></label>
                                <label class="itp-checkrow"><span>Small play / pause</span><input type="checkbox" name="vsl_ctrl_small_play" value="1" <?php checked($meta['vsl_ctrl_small_play'], '1'); ?>></label>
                                <label class="itp-checkrow"><span>Allow pause / resume</span><input type="checkbox" name="vsl_ctrl_allow_pause" value="1" <?php checked($meta['vsl_ctrl_allow_pause'], '1'); ?>></label>
                                <label class="itp-checkrow"><span>Video time</span><input type="checkbox" name="vsl_ctrl_time" value="1" <?php checked($meta['vsl_ctrl_time'], '1'); ?>></label>
                                <label class="itp-checkrow"><span>Back 10 seconds</span><input type="checkbox" name="vsl_ctrl_back10" value="1" <?php checked($meta['vsl_ctrl_back10'], '1'); ?>></label>
                                <label class="itp-checkrow"><span>Forward 10 seconds</span><input type="checkbox" name="vsl_ctrl_forward10" value="1" <?php checked($meta['vsl_ctrl_forward10'], '1'); ?>></label>
                                <label class="itp-checkrow"><span>Volume</span><input type="checkbox" name="vsl_ctrl_volume" value="1" <?php checked($meta['vsl_ctrl_volume'], '1'); ?>></label>
                                <label class="itp-checkrow"><span>Fullscreen</span><input type="checkbox" name="vsl_ctrl_fullscreen" value="1" <?php checked($meta['vsl_ctrl_fullscreen'], '1'); ?>></label>
                                <label class="itp-checkrow"><span>Speed control</span><input type="checkbox" name="vsl_ctrl_speed" value="1" <?php checked($meta['vsl_ctrl_speed'], '1'); ?>></label>
                            </div>
                            <div class="itp-vsl-mini-section itp-vsl-controls-list">
                                <strong>Smart progress</strong>
                                <p class="itp-note">Real progress follows the video. Smart visual progress can move faster at the start and slow down later.</p>
                                <label class="itp-toggle"><input type="checkbox" name="vsl_progress_on" value="1" <?php checked($meta['vsl_progress_on'], '1'); ?>><span></span><strong>Show progress bar</strong><em>Shown at the bottom of the player.</em></label>
                                <label class="itp-checkrow"><span>Use button background color</span><input type="checkbox" name="vsl_progress_use_cta_color" value="1" <?php checked($meta['vsl_progress_use_cta_color'], '1'); ?>></label>
                                <div class="itp-form-grid two">
                                    <label>Progress type<select name="vsl_progress_type"><option value="real" <?php selected($meta['vsl_progress_type'], 'real'); ?>>Real progress</option><option value="smart" <?php selected($meta['vsl_progress_type'], 'smart'); ?>>Smart visual progress</option></select></label>
                                    <label data-itp-progress-color-field>Progress color<input type="color" name="vsl_progress_color" value="<?php echo esc_attr($meta['vsl_progress_color']); ?>"></label>
                                    <label>Progress height<input type="range" min="2" max="30" step="1" name="vsl_progress_height" value="<?php echo esc_attr($meta['vsl_progress_height']); ?>"><small><output data-itp-progress-height-output><?php echo esc_html((int)$meta['vsl_progress_height']); ?></output> px</small></label>
                                </div>
                            </div>
                        </div>

                        <div class="itp-vsl-panel <?php echo $active_vsl_tab === 'video' ? 'active' : ''; ?>" data-itp-vsl-panel="video">
                            <h3>Video asset</h3>
                            <p class="itp-note">Use a direct MP4 selected from the WordPress Media Library. This gives the cleanest autoplay, pause detection and watch-depth analytics.</p>
                            <div class="itp-form-grid two">
                                <label>Campaign name<input name="title" value="<?php echo esc_attr($is_edit ? get_the_title($id) : $meta['title']); ?>" placeholder="Ex: Course VSL test 01"></label>
                                <label>Player ID / embed slug<input name="slug" value="<?php echo esc_attr($meta['slug']); ?>" placeholder="Ex: course-vsl-test"></label>
                            </div>
                            <div class="itp-final-url">Embed-only player URL: <strong data-itp-final-url><?php echo esc_html($preview_url); ?></strong><small>This technical URL is auto-generated with /it-vsl-embed/ so the player can load without the WordPress theme header/footer.</small></div>
                            <label>Campaign label optional<input name="category" value="<?php echo esc_attr($meta['category']); ?>" placeholder="Ex: Meta Ads / Affiliate / Course"></label>
                            <div class="itp-media-pick-row"><label>Main WordPress Media MP4<input class="itp-media-url" name="vsl_video_url" value="<?php echo esc_url($meta['vsl_video_url']); ?>" placeholder="Select a video from the WordPress Media Library" readonly><small class="itp-vsl-duration-pill" data-itp-duration-for="vsl_video_url">Duration: detecting…</small></label><button type="button" class="itp-btn itp-btn-ghost" data-itp-media="vsl-video" data-itp-media-type="video">Choose video</button></div>
                            <div class="itp-media-pick-row"><label>Autoplay background video optional<input class="itp-media-url" name="vsl_preview_video_url" value="<?php echo esc_url($meta['vsl_preview_video_url']); ?>" placeholder="Optional: separate muted teaser/background video" readonly><small class="itp-vsl-duration-pill" data-itp-duration-for="vsl_preview_video_url">Duration: optional</small></label><button type="button" class="itp-btn itp-btn-ghost" data-itp-media="vsl-preview-video" data-itp-media-type="video">Choose background</button></div>
                            <div class="itp-vsl-mini-section itp-vsl-timing-card" data-itp-bg-timing-card>
                                <strong>Background video segment</strong>
                                <span data-itp-time-helper="background">Use this to start the muted teaser at a specific moment and loop only the selected window.</span>
                                <div class="itp-form-grid two">
                                    <label>Start seconds<input type="number" min="0" max="7200" name="vsl_bg_start" data-itp-time-source="background" value="<?php echo esc_attr($meta['vsl_bg_start']); ?>"></label>
                                    <label>End seconds<input type="number" min="0" max="7200" name="vsl_bg_end" data-itp-time-source="background" value="<?php echo esc_attr($meta['vsl_bg_end']); ?>"></label>
                                </div>
                            </div>
                            <input type="hidden" name="vsl_poster_url" value="<?php echo esc_url($meta['vsl_poster_url']); ?>">
                            <input type="hidden" name="vsl_native_controls" value="0">
                            <input type="hidden" name="vsl_click_pause" value="<?php echo ($meta['vsl_ctrl_allow_pause'] ?? '0') === '1' ? '1' : '0'; ?>">
                            <input type="hidden" name="vsl_headline" value="<?php echo esc_attr($meta['vsl_headline']); ?>">
                            <input type="hidden" name="vsl_subheadline" value="<?php echo esc_attr($meta['vsl_subheadline']); ?>">
                        </div>

                        <div class="itp-vsl-panel <?php echo $active_vsl_tab === 'autoplay' ? 'active' : ''; ?>" data-itp-vsl-panel="autoplay">
                            <h3>Smart Autoplay</h3>
                            <p class="itp-note">Configure the click-to-hear overlay. While editing this tab, the preview keeps the muted background video running so you can position the overlay naturally.</p>
                            <label class="itp-toggle"><input type="checkbox" name="vsl_smart_autoplay" value="1" <?php checked($meta['vsl_smart_autoplay'], '1'); ?>><span></span><strong>Smart autoplay overlay</strong><em>Starts muted when possible. Visitor clicks the overlay to unmute and start the main video.</em></label>

                            <div class="itp-vsl-autoplay-layout">
                                <strong>Layout</strong>
                                <p class="itp-note">Choose a ready template or use your own image/GIF. Templates use a lightweight animated volume icon.</p>
                                <select name="vsl_overlay_template" class="itp-hidden-select" data-itp-overlay-template-select>
                                    <option value="wide" <?php selected($meta['vsl_overlay_template'], 'wide'); ?>>Wide card</option>
                                    <option value="square" <?php selected($meta['vsl_overlay_template'], 'square'); ?>>Square card</option>
                                    <option value="play" <?php selected($meta['vsl_overlay_template'], 'play'); ?>>Dark play card</option>
                                    <option value="full" <?php selected($meta['vsl_overlay_template'], 'full'); ?>>Full screen</option>
                                    <option value="compact" <?php selected($meta['vsl_overlay_template'], 'compact'); ?>>Compact pulse</option>
                                    <option value="icon_sound" <?php selected($meta['vsl_overlay_template'], 'icon_sound'); ?>>Icon only: sound</option>
                                    <option value="icon_play" <?php selected($meta['vsl_overlay_template'], 'icon_play'); ?>>Icon only: play</option>
                                    <option value="icon_blink" <?php selected($meta['vsl_overlay_template'], 'icon_blink'); ?>>Icon only: blink</option>
                                    <option value="custom" <?php selected($meta['vsl_overlay_template'], 'custom'); ?>>Custom image/GIF</option>
                                </select>
                                <div class="itp-vsl-autoplay-mode-tabs">
                                    <button type="button" class="active" data-itp-overlay-mode="template">Template</button>
                                    <button type="button" data-itp-overlay-mode="custom">Custom</button>
                                </div>
                                <div class="itp-vsl-autoplay-template-grid" data-itp-overlay-template-grid>
                                    <?php
                                    $overlay_templates = [
                                        'wide' => ['Your video has already started', 'Click to hear', 'Wide card'],
                                        'square' => ['', 'Click to hear', 'Vertical card'],
                                        'play' => ['', 'CLICK TO HEAR', 'Dark play'],
                                        'full' => ['Your video has already started', 'Click to hear', 'Full screen'],
                                        'compact' => ['', 'Click to hear', 'Compact bar'],
                                        'icon_sound' => ['', '', 'Icon only: sound'],
                                        'icon_play' => ['', '', 'Icon only: play'],
                                        'icon_blink' => ['', '', 'Icon only: blink'],
                                    ];
                                    foreach ($overlay_templates as $ot_key => $ot_data) :
                                    ?>
                                    <button type="button" class="itp-vsl-autoplay-template <?php echo $meta['vsl_overlay_template'] === $ot_key ? 'active' : ''; ?>" data-itp-overlay-template-option="<?php echo esc_attr($ot_key); ?>" title="<?php echo esc_attr($ot_data[2]); ?>">
                                        <?php
                                            $ot_icon_key = ($ot_key === 'play' || $ot_key === 'icon_play') ? 'play' : (($ot_key === 'compact' || $ot_key === 'icon_blink') ? 'blink' : 'sound');
                                            $ot_icon_url = plugins_url('assets/invalid-traffic-smartplay-' . $ot_icon_key . '.svg', __FILE__);
                                        ?>
                                        <span class="itp-vsl-template-swatch template-<?php echo esc_attr($ot_key); ?>">
                                            <?php if ($ot_data[0] !== '') : ?><span class="itp-vsl-template-top"><?php echo esc_html($ot_data[0]); ?></span><?php endif; ?>
                                            <img class="itp-vsl-template-main-icon itp-vsl-template-main-icon-<?php echo esc_attr($ot_icon_key); ?>" src="<?php echo esc_url($ot_icon_url); ?>" alt="" aria-hidden="true">
                                            <?php if ($ot_data[1] !== '') : ?><span class="itp-vsl-template-bottom"><?php echo esc_html($ot_data[1]); ?></span><?php endif; ?>
                                        </span>
                                    </button>
                                    <?php endforeach; ?>
                                </div>
                            </div>

                            <div class="itp-form-grid two" data-itp-autoplay-template-settings>
                                <label data-itp-autoplay-text>Top text<input name="vsl_overlay_top" maxlength="28" value="<?php echo esc_attr($meta['vsl_overlay_top']); ?>" placeholder="Your video has already started"><small><span data-itp-count-for="vsl_overlay_top">0</span> / 28</small></label>
                                <label data-itp-autoplay-text>Bottom text<input name="vsl_overlay_bottom" maxlength="28" value="<?php echo esc_attr($meta['vsl_overlay_bottom']); ?>" placeholder="Click to hear"><small><span data-itp-count-for="vsl_overlay_bottom">0</span> / 28</small></label>
                                <label data-itp-autoplay-text>Text font<select name="vsl_overlay_font"><?php echo $this->vsl_font_options_html($meta['vsl_overlay_font'] ?? 'Inter'); ?></select><small>Applies to the Smart Autoplay overlay text in preview and embed.</small></label>
                                <label data-itp-autoplay-text>Custom text font .woff2 URL<input name="vsl_overlay_font_woff2" value="<?php echo esc_url($meta['vsl_overlay_font_woff2'] ?? ''); ?>" placeholder="https://yourdomain.com/fonts/font.woff2"><small>Optional. Overrides the font select for Smart Autoplay text.</small></label>
                                <label data-itp-autoplay-text>Text color<input type="color" name="vsl_overlay_text_color" value="<?php echo esc_attr($meta['vsl_overlay_text_color']); ?>"></label>
                                <label data-itp-autoplay-bg>Background color<input type="color" name="vsl_overlay_color" value="<?php echo esc_attr($meta['vsl_overlay_color']); ?>"></label>
                                <label data-itp-autoplay-bg>Background opacity<input type="range" min="20" max="100" step="1" name="vsl_overlay_opacity" value="<?php echo esc_attr($meta['vsl_overlay_opacity'] ?? 92); ?>"><small><output data-itp-overlay-opacity-output><?php echo esc_html((int)($meta['vsl_overlay_opacity'] ?? 92)); ?></output>%</small></label>
                            </div>
                            <div class="itp-media-pick-row" data-itp-autoplay-custom><label>Custom overlay image / GIF<input class="itp-media-url" name="vsl_overlay_image" value="<?php echo esc_url($meta['vsl_overlay_image']); ?>" placeholder="Upload an image or GIF for the click-to-hear overlay" readonly></label><button type="button" class="itp-btn itp-btn-ghost" data-itp-media="vsl-overlay-image" data-itp-media-type="image">Choose image</button></div>
                            <div class="itp-vsl-design-fields" data-itp-design-fields="overlay">
                                <strong>Position</strong><span>Click “Edit overlay position” to unlock drag and resize handles in the preview.</span>
                                <button type="button" class="itp-btn itp-btn-ghost" data-itp-overlay-position-toggle>Edit overlay position</button>
                                <input type="hidden" name="vsl_overlay_x" value="<?php echo esc_attr($meta['vsl_overlay_x']); ?>" data-itp-design-input="overlay:x">
                                <input type="hidden" name="vsl_overlay_y" value="<?php echo esc_attr($meta['vsl_overlay_y']); ?>" data-itp-design-input="overlay:y">
                                <input type="hidden" name="vsl_overlay_width" value="<?php echo esc_attr($meta['vsl_overlay_width']); ?>" data-itp-design-input="overlay:w">
                                <input type="hidden" name="vsl_overlay_rotate" value="<?php echo esc_attr($meta['vsl_overlay_rotate'] ?? 0); ?>" data-itp-design-input="overlay:r">
                                <input type="hidden" name="vsl_overlay_mobile_x" value="<?php echo esc_attr($meta['vsl_overlay_mobile_x'] ?? $meta['vsl_overlay_x']); ?>" data-itp-design-input="overlay-mobile:x">
                                <input type="hidden" name="vsl_overlay_mobile_y" value="<?php echo esc_attr($meta['vsl_overlay_mobile_y'] ?? $meta['vsl_overlay_y']); ?>" data-itp-design-input="overlay-mobile:y">
                                <input type="hidden" name="vsl_overlay_mobile_width" value="<?php echo esc_attr($meta['vsl_overlay_mobile_width'] ?? $meta['vsl_overlay_width']); ?>" data-itp-design-input="overlay-mobile:w">
                                <input type="hidden" name="vsl_overlay_mobile_rotate" value="<?php echo esc_attr($meta['vsl_overlay_mobile_rotate'] ?? ($meta['vsl_overlay_rotate'] ?? 0)); ?>" data-itp-design-input="overlay-mobile:r">
                            </div>
                            <label class="itp-toggle"><input type="checkbox" name="vsl_overlay_pulse" value="1" <?php checked($meta['vsl_overlay_pulse'], '1'); ?>><span></span><strong>Pulse overlay</strong><em>Small animation to increase the chance of the click-to-hear action.</em></label>
                            <div class="itp-vsl-mini-section" data-itp-overlay-pulse-settings <?php echo ($meta['vsl_overlay_pulse'] ?? '0') === '1' ? '' : 'hidden'; ?>>
                                <strong>Overlay pulse speed</strong>
                                <span>Lower values pulse faster. The preview stays animated while you adjust it.</span>
                                <label>Speed (seconds)
                                    <input type="number" min="0.60" max="3.00" step="0.05" name="vsl_overlay_pulse_speed" value="<?php echo esc_attr($meta['vsl_overlay_pulse_speed'] ?? '1.30'); ?>">
                                </label>
                                <label>Pulse style
                                    <select name="vsl_overlay_pulse_mode">
                                        <option value="scale" <?php selected($meta['vsl_overlay_pulse_mode'] ?? 'scale', 'scale'); ?>>Scale ring</option>
                                        <option value="soft" <?php selected($meta['vsl_overlay_pulse_mode'] ?? 'scale', 'soft'); ?>>Soft breathe ring</option>
                                        <option value="glow" <?php selected($meta['vsl_overlay_pulse_mode'] ?? 'scale', 'glow'); ?>>Glow only</option>
                                        <option value="premium" <?php selected($meta['vsl_overlay_pulse_mode'] ?? 'scale', 'premium'); ?>>Premium pulse</option>
                                        <option value="heartbeat" <?php selected($meta['vsl_overlay_pulse_mode'] ?? 'scale', 'heartbeat'); ?>>Heartbeat</option>
                                        <option value="breathe" <?php selected($meta['vsl_overlay_pulse_mode'] ?? 'scale', 'breathe'); ?>>Breathe</option>
                                        <option value="bounce" <?php selected($meta['vsl_overlay_pulse_mode'] ?? 'scale', 'bounce'); ?>>Soft bounce</option>
                                        <option value="flash" <?php selected($meta['vsl_overlay_pulse_mode'] ?? 'scale', 'flash'); ?>>Flash</option>
                                        <option value="shake" <?php selected($meta['vsl_overlay_pulse_mode'] ?? 'scale', 'shake'); ?>>Tiny shake</option>
                                    </select>
                                </label>
                            </div>
                            <label class="itp-toggle"><input type="checkbox" name="vsl_interaction_sound" value="1" <?php checked($meta['vsl_interaction_sound'], '1'); ?>><span></span><strong>Detect page interaction</strong><em>After any page interaction, the player tries to start audio. The overlay still remains available as fallback.</em></label>
                        </div>

                        <div class="itp-vsl-panel <?php echo $active_vsl_tab === 'turbo' ? 'active' : ''; ?>" data-itp-vsl-panel="turbo">
                            <h3>Turbo speed</h3>
                            <p class="itp-note">Set a playback speed for this player. The analytics will show whether retention improves or drops for this version.</p>
                            <label>Playback speed<select name="vsl_speed">
                                <?php foreach (['0.9'=>'0.9x','1.0'=>'1.0x','1.1'=>'1.1x','1.2'=>'1.2x','1.3'=>'1.3x','1.4'=>'1.4x','1.5'=>'1.5x','1.75'=>'1.75x','2.0'=>'2.0x'] as $v=>$label) echo '<option value="'.esc_attr($v).'" '.selected($meta['vsl_speed'], $v, false).'>'.esc_html($label).'</option>'; ?>
                            </select></label>
                            <p class="itp-note"><strong>How duration works:</strong> the browser reads the MP4 metadata after the video loads. WordPress Media videos usually report duration automatically; if the file/server blocks metadata, the player still works and time-based features continue as soon as metadata becomes available.</p>
                        </div>

                        <div class="itp-vsl-panel <?php echo $active_vsl_tab === 'cta' ? 'active' : ''; ?>" data-itp-vsl-panel="cta">
                            <h3>Action button</h3>
                            <p class="itp-vsl-time-hint" data-itp-time-helper="main">Timing uses the main video duration after metadata is detected.</p>
                            <label class="itp-toggle"><input type="checkbox" name="vsl_cta_on" value="1" <?php checked($meta['vsl_cta_on'], '1'); ?>><span></span><strong>Show action button</strong><em>Reveal the CTA after the pitch or after a delay.</em></label>
                            <div class="itp-form-grid two itp-vsl-cta-basic-grid">
                                <label>Button text<input name="vsl_cta_text" value="<?php echo esc_attr($meta['vsl_cta_text']); ?>" placeholder="Continue"></label>
                                <label>Button action<select name="vsl_cta_action" data-itp-cta-action><option value="url" <?php selected($meta['vsl_cta_action'] ?? 'url', 'url'); ?>>Open URL</option><option value="lead" <?php selected($meta['vsl_cta_action'] ?? 'url', 'lead'); ?>>Open lead popup</option><option value="lead_redirect" <?php selected($meta['vsl_cta_action'] ?? 'url', 'lead_redirect'); ?>>Capture lead, then redirect</option></select><small>Keep the CTA simple: send visitors to a URL or collect an email first.</small></label>
                                <label>URL / redirect URL optional<input name="vsl_cta_url" value="<?php echo esc_url($meta['vsl_cta_url']); ?>" placeholder="https://example.com/checkout-or-offer"><small>Used for Open URL, or after lead capture when selected.</small></label>
                            </div>
                            <div class="itp-vsl-lead-settings" data-itp-cta-lead-settings <?php echo in_array(($meta['vsl_cta_action'] ?? 'url'), ['lead','lead_redirect'], true) ? '' : 'hidden'; ?>>
                                <div class="itp-vsl-mini-section itp-vsl-lead-mini">
                                    <strong>Lead popup</strong><p class="itp-note">Email is always active. Name and phone are optional, so the popup can stay simple.</p>
                                    <div class="itp-form-grid two"><label>Popup title<input name="vsl_lead_title" value="<?php echo esc_attr($meta['vsl_lead_title'] ?? 'Get the next step by email'); ?>"></label><label>Submit button text<input name="vsl_lead_button_text" value="<?php echo esc_attr($meta['vsl_lead_button_text'] ?? 'Continue by email'); ?>"></label></div>
                                    <label>Popup copy<textarea name="vsl_lead_copy" rows="2"><?php echo esc_textarea($meta['vsl_lead_copy'] ?? 'Enter your email below to continue.'); ?></textarea></label>
                                    <div class="itp-form-grid two"><label>Email placeholder<input name="vsl_lead_email_placeholder" value="<?php echo esc_attr($meta['vsl_lead_email_placeholder'] ?? 'you@example.com'); ?>"></label><label>Success message<input name="vsl_lead_success" value="<?php echo esc_attr($meta['vsl_lead_success'] ?? 'Thanks. Your email was received.'); ?>"></label></div>
                                    <div class="itp-form-grid two"><label class="itp-checkrow"><span>Ask for name</span><input type="checkbox" name="vsl_lead_name_on" value="1" <?php checked($meta['vsl_lead_name_on'] ?? '0', '1'); ?>></label><label class="itp-checkrow"><span>Ask for phone</span><input type="checkbox" name="vsl_lead_phone_on" value="1" <?php checked($meta['vsl_lead_phone_on'] ?? '0', '1'); ?>></label></div>
                                    <div class="itp-form-grid two"><label>Name placeholder<input name="vsl_lead_name_placeholder" value="<?php echo esc_attr($meta['vsl_lead_name_placeholder'] ?? 'Full name'); ?>"></label><label>Phone placeholder<input name="vsl_lead_phone_placeholder" value="<?php echo esc_attr($meta['vsl_lead_phone_placeholder'] ?? 'Phone number'); ?>"></label></div>
                                    <details class="itp-advanced itp-lead-design-details"><summary>Lead popup design</summary>
                                        <div class="itp-form-grid two">
                                            <label>Popup background<input type="color" name="vsl_lead_bg" value="<?php echo esc_attr($meta['vsl_lead_bg'] ?? '#050816'); ?>"></label>
                                            <label>Background image URL<input name="vsl_lead_bg_image" value="<?php echo esc_url($meta['vsl_lead_bg_image'] ?? ''); ?>" placeholder="https://example.com/background.jpg"></label>
                                            <label>Title color<input type="color" name="vsl_lead_title_color" value="<?php echo esc_attr($meta['vsl_lead_title_color'] ?? '#ffffff'); ?>"></label>
                                            <label>Text color<input type="color" name="vsl_lead_text_color" value="<?php echo esc_attr($meta['vsl_lead_text_color'] ?? '#ffffff'); ?>"></label>
                                            <label>Button background<input type="color" name="vsl_lead_button_bg" value="<?php echo esc_attr($meta['vsl_lead_button_bg'] ?? '#16c653'); ?>"></label>
                                            <label>Button text<input type="color" name="vsl_lead_button_text_color" value="<?php echo esc_attr($meta['vsl_lead_button_text_color'] ?? '#02120a'); ?>"></label>
                                            <label>Close X color<input type="color" name="vsl_lead_close_color" value="<?php echo esc_attr($meta['vsl_lead_close_color'] ?? '#ffffff'); ?>"></label>
                                            <label>Close X background<input type="color" name="vsl_lead_close_bg" value="<?php echo esc_attr($meta['vsl_lead_close_bg'] ?? '#060b14'); ?>"></label>
                                            <label>Popup radius<input type="range" min="0" max="42" name="vsl_lead_radius" value="<?php echo esc_attr((int)($meta['vsl_lead_radius'] ?? 24)); ?>"><small><span data-itp-output-for="vsl_lead_radius"><?php echo esc_html((int)($meta['vsl_lead_radius'] ?? 24)); ?></span>px — lower = square, higher = rounded.</small></label>
                                            <label>Close button style<select name="vsl_lead_close_style"><option value="round" <?php selected($meta['vsl_lead_close_style'] ?? 'round', 'round'); ?>>Round button</option><option value="plain" <?php selected($meta['vsl_lead_close_style'] ?? 'round', 'plain'); ?>>Plain X only</option></select></label>
                                        </div>
                                    </details>
                                    <div class="itp-lead-popup-preview" data-itp-lead-preview>
                                        <div class="itp-lead-popup-preview-head"><span>Lead popup preview</span><em>How it opens after the CTA click.</em></div>
                                        <div class="itp-lead-preview-box <?php echo ($meta['vsl_lead_close_style'] ?? 'round') === 'plain' ? 'close-plain' : 'close-round'; ?>" style="<?php echo esc_attr('--itp-lead-bg:' . ($meta['vsl_lead_bg'] ?? '#050816') . ';--itp-lead-title:' . ($meta['vsl_lead_title_color'] ?? '#ffffff') . ';--itp-lead-text:' . ($meta['vsl_lead_text_color'] ?? '#ffffff') . ';--itp-lead-btn:' . ($meta['vsl_lead_button_bg'] ?? '#16c653') . ';--itp-lead-btn-text:' . ($meta['vsl_lead_button_text_color'] ?? '#02120a') . ';--itp-lead-close:' . ($meta['vsl_lead_close_color'] ?? '#ffffff') . ';--itp-lead-close-bg:' . ($meta['vsl_lead_close_bg'] ?? '#060b14') . ';--itp-lead-radius:' . (int)($meta['vsl_lead_radius'] ?? 24) . 'px;--itp-lead-bg-image:url(' . esc_url($meta['vsl_lead_bg_image'] ?? '') . ');'); ?>">
                                            <button type="button" aria-label="Close">×</button>
                                            <h4><?php echo esc_html($meta['vsl_lead_title'] ?? 'Get the next step by email'); ?></h4>
                                            <p><?php echo esc_html($meta['vsl_lead_copy'] ?? 'Enter your email below to continue.'); ?></p>
                                            <input class="itp-lead-preview-field itp-lead-preview-name" readonly value="" placeholder="<?php echo esc_attr($meta['vsl_lead_name_placeholder'] ?? 'Full name'); ?>" <?php echo (($meta['vsl_lead_name_on'] ?? '0') === '1') ? '' : 'hidden'; ?>>
                                            <input class="itp-lead-preview-field itp-lead-preview-email" readonly value="" placeholder="<?php echo esc_attr($meta['vsl_lead_email_placeholder'] ?? 'you@example.com'); ?>">
                                            <input class="itp-lead-preview-field itp-lead-preview-phone" readonly value="" placeholder="<?php echo esc_attr($meta['vsl_lead_phone_placeholder'] ?? 'Phone number'); ?>" <?php echo (($meta['vsl_lead_phone_on'] ?? '0') === '1') ? '' : 'hidden'; ?>>
                                            <strong><?php echo esc_html($meta['vsl_lead_button_text'] ?? 'Continue by email'); ?></strong>
                                        </div>
                                    </div>
                                </div>
                            </div>
                            <details class="itp-vsl-advanced-cta"><summary>Advanced button design</summary>
                            <div class="itp-media-pick-row"><label>Button image optional<input class="itp-media-url" name="vsl_cta_image" value="<?php echo esc_url($meta['vsl_cta_image']); ?>" placeholder="Optional image/GIF button" readonly></label><button type="button" class="itp-btn itp-btn-ghost" data-itp-media="vsl-cta-image" data-itp-media-type="image">Choose image</button></div>
                            <div class="itp-vsl-design-fields" data-itp-design-fields="cta">
                                <strong>Inside-video placement</strong><span>When Position is “Inside video”, drag/resize the button in the preview.</span>
                                <input type="hidden" name="vsl_cta_x" value="<?php echo esc_attr($meta['vsl_cta_x']); ?>" data-itp-design-input="cta:x">
                                <input type="hidden" name="vsl_cta_y" value="<?php echo esc_attr($meta['vsl_cta_y']); ?>" data-itp-design-input="cta:y">
                                <input type="hidden" name="vsl_cta_width" value="<?php echo esc_attr($meta['vsl_cta_width']); ?>" data-itp-design-input="cta:w">
                            </div>
                            <div class="itp-form-grid two">
                                <label>Show at seconds<input type="number" min="0" max="7200" name="vsl_cta_delay" data-itp-time-source="main" value="<?php echo esc_attr($meta['vsl_cta_delay']); ?>"><small>CTA appears when the main video reaches this second.</small></label>
                                <label>Pitch marker optional<input type="number" min="0" max="7200" name="vsl_pitch_time" data-itp-time-source="main" value="<?php echo esc_attr($meta['vsl_pitch_time']); ?>"><small>Optional marker. Used only if “Show at seconds” is 0.</small></label>
                                <label>Hide after seconds<input type="number" min="0" max="7200" name="vsl_cta_end" data-itp-time-source="main" value="<?php echo esc_attr($meta['vsl_cta_end']); ?>"><small>0 = never hide after it appears.</small></label>
                                <label>Position<select name="vsl_cta_position"><option value="below" <?php selected($meta['vsl_cta_position'], 'below'); ?>>Below video</option><option value="inside" <?php selected($meta['vsl_cta_position'], 'inside'); ?>>Inside video</option></select></label>
                                <label>Font<select name="vsl_cta_font"><option value="Inter" <?php selected($meta['vsl_cta_font'], 'Inter'); ?>>Inter</option><option value="Poppins" <?php selected($meta['vsl_cta_font'], 'Poppins'); ?>>Poppins</option><option value="Montserrat" <?php selected($meta['vsl_cta_font'], 'Montserrat'); ?>>Montserrat</option><option value="Roboto" <?php selected($meta['vsl_cta_font'], 'Roboto'); ?>>Roboto</option><option value="Open Sans" <?php selected($meta['vsl_cta_font'], 'Open Sans'); ?>>Open Sans</option><option value="Oswald" <?php selected($meta['vsl_cta_font'], 'Oswald'); ?>>Oswald</option><option value="Bebas Neue" <?php selected($meta['vsl_cta_font'], 'Bebas Neue'); ?>>Bebas Neue</option><option value="Arial" <?php selected($meta['vsl_cta_font'], 'Arial'); ?>>Arial</option><option value="system" <?php selected($meta['vsl_cta_font'], 'system'); ?>>System</option></select><small>Use a normal stack, or paste a .woff2 URL below to force the exact font in the embed.</small></label>
                                <label>Weight<select name="vsl_cta_weight"><option value="400" <?php selected($meta['vsl_cta_weight'], '400'); ?>>400</option><option value="600" <?php selected($meta['vsl_cta_weight'], '600'); ?>>600</option><option value="700" <?php selected($meta['vsl_cta_weight'], '700'); ?>>700</option><option value="900" <?php selected($meta['vsl_cta_weight'], '900'); ?>>900</option></select></label>
                                <label>Custom font .woff2 URL<input name="vsl_cta_font_woff2" value="<?php echo esc_url($meta['vsl_cta_font_woff2'] ?? ''); ?>" placeholder="https://yourdomain.com/fonts/font.woff2"><small>Optional. When filled, this overrides the font select for the CTA in preview and public embed.</small></label>
                                <label>Font size<input type="number" min="10" max="48" name="vsl_cta_font_size" value="<?php echo esc_attr($meta['vsl_cta_font_size']); ?>"></label>
                                <label>Border radius<input type="number" min="0" max="999" name="vsl_cta_radius" value="<?php echo esc_attr($meta['vsl_cta_radius']); ?>"></label>
                                <label>Padding vertical<input type="number" min="4" max="60" name="vsl_cta_padding_y" value="<?php echo esc_attr($meta['vsl_cta_padding_y']); ?>"></label>
                                <label>Padding horizontal<input type="number" min="8" max="120" name="vsl_cta_padding_x" value="<?php echo esc_attr($meta['vsl_cta_padding_x']); ?>"></label>
                                <label>Text color<input type="color" name="vsl_cta_text_color" value="<?php echo esc_attr($meta['vsl_cta_text_color']); ?>"></label>
                                <label>Text hover<input type="color" name="vsl_cta_text_hover_color" value="<?php echo esc_attr($meta['vsl_cta_text_hover_color']); ?>"></label>
                                <label>Background<input type="color" name="vsl_cta_color" value="<?php echo esc_attr($meta['vsl_cta_color']); ?>"></label>
                                <label>Background hover<input type="color" name="vsl_cta_hover_color" value="<?php echo esc_attr($meta['vsl_cta_hover_color']); ?>"></label>
                            </div>
                            <label class="itp-toggle"><input type="checkbox" name="vsl_cta_pulse" value="1" <?php checked($meta['vsl_cta_pulse'], '1'); ?>><span></span><strong>Pulse CTA</strong><em>Draw attention after the button appears.</em></label>
                            <div class="itp-vsl-mini-section" data-itp-cta-pulse-settings <?php echo ($meta['vsl_cta_pulse'] ?? '1') === '1' ? '' : 'hidden'; ?>>
                                <strong>CTA pulse speed</strong>
                                <span>Lower values pulse faster. The preview stays animated while you adjust it.</span>
                                <label>Speed (seconds)
                                    <input type="number" min="0.60" max="3.00" step="0.05" name="vsl_cta_pulse_speed" value="<?php echo esc_attr($meta['vsl_cta_pulse_speed'] ?? '1.45'); ?>">
                                </label>
                                <label>Animation style<select name="vsl_cta_animation"><option value="premium" <?php selected($meta['vsl_cta_animation'] ?? 'premium', 'premium'); ?>>Premium pulse</option><option value="heartbeat" <?php selected($meta['vsl_cta_animation'] ?? 'premium', 'heartbeat'); ?>>Heartbeat</option><option value="breathe" <?php selected($meta['vsl_cta_animation'] ?? 'premium', 'breathe'); ?>>Breathe</option><option value="bounce" <?php selected($meta['vsl_cta_animation'] ?? 'premium', 'bounce'); ?>>Soft bounce</option><option value="flash" <?php selected($meta['vsl_cta_animation'] ?? 'premium', 'flash'); ?>>Flash</option><option value="shake" <?php selected($meta['vsl_cta_animation'] ?? 'premium', 'shake'); ?>>Tiny shake</option></select><small>Lightweight Animate.css-style effects without importing the whole library.</small></label>
                            </div>
                            <label class="itp-toggle"><input type="checkbox" name="vsl_cta_shadow" value="1" <?php checked($meta['vsl_cta_shadow'], '1'); ?>><span></span><strong>Button shadow</strong><em>Add depth to the action button.</em></label>
                            <label class="itp-toggle"><input type="checkbox" name="vsl_cta_autoscroll" value="1" <?php checked($meta['vsl_cta_autoscroll'], '1'); ?>><span></span><strong>Auto-scroll to button</strong><em>Useful when the button is below the video.</em></label>
                            <label class="itp-toggle"><input type="checkbox" name="vsl_cta_remember" value="1" <?php checked($meta['vsl_cta_remember'], '1'); ?>><span></span><strong>Keep visible in future visits</strong><em>After the CTA appears once, the same browser can see it immediately next time.</em></label>
                            </details>
                        </div>

                        <div class="itp-vsl-panel <?php echo $active_vsl_tab === 'pause' ? 'active' : ''; ?>" data-itp-vsl-panel="pause">
                            <h3>Thumb Recovery</h3>
                            <p class="itp-vsl-time-hint" data-itp-time-helper="main">Thumb Recovery uses the main video timeline. Show a custom image/GIF when the visitor pauses inside the selected window, or when the video ends.</p>
                            <label class="itp-toggle"><input type="checkbox" name="vsl_pause_on" value="1" <?php checked($meta['vsl_pause_on'], '1'); ?>><span></span><strong>Enable Thumb Recovery</strong><em>Shows a pause/end recovery card. If enabled, Allow pause / resume is kept on automatically.</em></label><p class="itp-note itp-thumb-recovery-clean-note" data-itp-thumb-pause-sync-note>Tip: use Start/End seconds to decide when the card is allowed to appear. End = 0 means no time limit.</p>
                            <label>Thumbnail name<input name="vsl_pause_name" value="<?php echo esc_attr($meta['vsl_pause_name']); ?>" placeholder="Recovery offer"></label>
                            <div class="itp-form-grid two">
                                <label>Start seconds<input type="number" min="0" max="7200" name="vsl_pause_start" data-itp-time-source="main" value="<?php echo esc_attr($meta['vsl_pause_start']); ?>"><small>Recovery can appear after this second.</small></label>
                                <label>End seconds<input type="number" min="0" max="7200" name="vsl_pause_end" data-itp-time-source="main" value="<?php echo esc_attr($meta['vsl_pause_end']); ?>"><small>0 = keep recovery available until the end.</small></label>
                                <label>Show recovery on<select name="vsl_pause_trigger"><option value="pause" <?php selected($meta['vsl_pause_trigger'], 'pause'); ?>>When visitor pauses</option><option value="end" <?php selected($meta['vsl_pause_trigger'], 'end'); ?>>When video ends</option><option value="both" <?php selected($meta['vsl_pause_trigger'], 'both'); ?>>Pause or video end</option></select><small>Start/End seconds only apply to pause behavior. End trigger appears after the video finishes.</small></label>
                            </div>
                            <div class="itp-media-pick-row"><label>Recovery image / GIF<input class="itp-media-url" name="vsl_pause_image" value="<?php echo esc_url($meta['vsl_pause_image']); ?>" placeholder="Select a recovery image" readonly></label><button type="button" class="itp-btn itp-btn-ghost" data-itp-media="vsl-pause" data-itp-media-type="image">Choose image</button></div>
                            <label class="itp-toggle"><input type="checkbox" name="vsl_pause_button_on" value="1" <?php checked($meta['vsl_pause_button_on'], '1'); ?>><span></span><strong>Add button to recovery card</strong><em>Adds a clickable CTA inside the pause/end recovery image or GIF.</em></label>
                            <div class="itp-form-grid two">
                                <label>Button text<input name="vsl_pause_button_text" value="<?php echo esc_attr($meta['vsl_pause_button_text']); ?>" placeholder="Continue"></label>
                                <label>Button URL<input name="vsl_pause_button_url" value="<?php echo esc_url($meta['vsl_pause_button_url']); ?>" placeholder="https://example.com/offer"></label>
                                <label>Button size<select name="vsl_pause_button_size"><option value="small" <?php selected($meta['vsl_pause_button_size'], 'small'); ?>>Small</option><option value="medium" <?php selected($meta['vsl_pause_button_size'], 'medium'); ?>>Medium</option><option value="large" <?php selected($meta['vsl_pause_button_size'], 'large'); ?>>Large</option></select></label>
                                <label>Button position<select name="vsl_pause_button_position"><option value="bottom" <?php selected($meta['vsl_pause_button_position'], 'bottom'); ?>>Bottom</option><option value="center" <?php selected($meta['vsl_pause_button_position'], 'center'); ?>>Center</option><option value="top" <?php selected($meta['vsl_pause_button_position'], 'top'); ?>>Top</option></select></label>
                                <label>Text color<input type="color" name="vsl_pause_button_text_color" value="<?php echo esc_attr($meta['vsl_pause_button_text_color']); ?>"></label>
                                <label>Background<input type="color" name="vsl_pause_button_color" value="<?php echo esc_attr($meta['vsl_pause_button_color']); ?>"></label>
                                <label>Background hover<input type="color" name="vsl_pause_button_hover_color" value="<?php echo esc_attr($meta['vsl_pause_button_hover_color']); ?>"></label>
                                <label>Button font<select name="vsl_pause_font"><?php echo $this->vsl_font_options_html($meta['vsl_pause_font'] ?? 'Inter'); ?></select></label>
                                <label>Custom button font .woff2 URL<input name="vsl_pause_font_woff2" value="<?php echo esc_url($meta['vsl_pause_font_woff2'] ?? ''); ?>" placeholder="https://yourdomain.com/fonts/font.woff2"></label>
                            </div>
                        </div>

                        <div class="itp-vsl-panel <?php echo $active_vsl_tab === 'hook' ? 'active' : ''; ?>" data-itp-vsl-panel="hook">
                            <h3>Mini hook</h3>
                            <p class="itp-vsl-time-hint" data-itp-time-helper="main">Mini hooks appear/disappear on the main video timeline.</p>
                            <label class="itp-toggle"><input type="checkbox" name="vsl_hook_on" value="1" <?php checked($meta['vsl_hook_on'], '1'); ?>><span></span><strong>Enable mini hook</strong><em>Small message/image that appears at a specific timestamp.</em></label>
                            <label>Mini hook name<input name="vsl_hook_name" value="<?php echo esc_attr($meta['vsl_hook_name']); ?>" placeholder="Mini hook 1"></label>
                            <div class="itp-form-grid two">
                                <label>Appears at seconds<input type="number" min="0" max="7200" name="vsl_hook_start" data-itp-time-source="main" value="<?php echo esc_attr($meta['vsl_hook_start']); ?>"></label>
                                <label>Disappears at seconds<input type="number" min="0" max="7200" name="vsl_hook_end" data-itp-time-source="main" value="<?php echo esc_attr($meta['vsl_hook_end']); ?>"></label>
                                <label>Position<select name="vsl_hook_position"><option value="bottom_left" <?php selected($meta['vsl_hook_position'], 'bottom_left'); ?>>Bottom left</option><option value="bottom_right" <?php selected($meta['vsl_hook_position'], 'bottom_right'); ?>>Bottom right</option><option value="top_right" <?php selected($meta['vsl_hook_position'], 'top_right'); ?>>Top right</option><option value="top_left" <?php selected($meta['vsl_hook_position'], 'top_left'); ?>>Top left</option></select></label>
                                <label>Opacity<input type="number" min="10" max="100" name="vsl_hook_opacity" value="<?php echo esc_attr($meta['vsl_hook_opacity']); ?>"></label>
                            </div>
                            <div class="itp-vsl-design-fields itp-vsl-hook-position-box" data-itp-design-fields="hook">
                                <strong>Position</strong><span>Click “Edit mini hook position” to unlock drag and resize handles in the preview. Works for text only, image only, or text + image hooks.</span>
                                <button type="button" class="itp-btn itp-btn-ghost" data-itp-hook-position-toggle="1">Edit mini hook position</button>
                                <input type="hidden" name="vsl_hook_x" value="<?php echo esc_attr($meta['vsl_hook_x']); ?>" data-itp-design-input="hook:x">
                                <input type="hidden" name="vsl_hook_y" value="<?php echo esc_attr($meta['vsl_hook_y']); ?>" data-itp-design-input="hook:y">
                                <input type="hidden" name="vsl_hook_width" value="<?php echo esc_attr($meta['vsl_hook_width']); ?>" data-itp-design-input="hook:w">
                            </div>
                            <label>Mini hook text<input name="vsl_hook_text" value="<?php echo esc_attr($meta['vsl_hook_text']); ?>" placeholder="Wait — this next part matters..."></label>
                            <div class="itp-form-grid two">
                                <label>Text font<select name="vsl_hook_font"><?php echo $this->vsl_font_options_html($meta['vsl_hook_font'] ?? 'Inter'); ?></select></label>
                                <label>Custom text font .woff2 URL<input name="vsl_hook_font_woff2" value="<?php echo esc_url($meta['vsl_hook_font_woff2'] ?? ''); ?>" placeholder="https://yourdomain.com/fonts/font.woff2"></label>
                            </div>
                            <div class="itp-media-pick-row"><label>Mini hook image / GIF<input class="itp-media-url" name="vsl_hook_image" value="<?php echo esc_url($meta['vsl_hook_image']); ?>" placeholder="Optional mini hook image" readonly></label><button type="button" class="itp-btn itp-btn-ghost" data-itp-media="vsl-hook" data-itp-media-type="image">Choose image</button></div>
                        </div>

                        <div class="itp-vsl-panel <?php echo $active_vsl_tab === 'resume' ? 'active' : ''; ?>" data-itp-vsl-panel="resume">
                            <h3>Continue Watching</h3>
                            <label class="itp-toggle"><input type="checkbox" name="vsl_resume_on" value="1" <?php checked($meta['vsl_resume_on'], '1'); ?>><span></span><strong>Continue Watching</strong><em>Resume screen for returning visitors. It appears after the visitor watches part of the main video, leaves, and comes back before finishing. It is not an end screen.</em></label>
                            <div class="itp-form-grid">
                                <label>Message<textarea name="vsl_resume_message" rows="3"><?php echo esc_textarea($meta['vsl_resume_message']); ?></textarea></label>
                                <label>Continue button<input name="vsl_resume_continue_text" value="<?php echo esc_attr($meta['vsl_resume_continue_text']); ?>"></label>
                                <label>Restart button<input name="vsl_resume_restart_text" value="<?php echo esc_attr($meta['vsl_resume_restart_text']); ?>"></label>
                            </div>
                            <div class="itp-form-grid two">
                                <label>Text color<input type="color" name="vsl_resume_text_color" value="<?php echo esc_attr($meta['vsl_resume_text_color']); ?>"></label>
                                <label>Background<input type="color" name="vsl_resume_bg" value="<?php echo esc_attr($meta['vsl_resume_bg']); ?>"></label>
                                <label>Text font<select name="vsl_resume_font"><?php echo $this->vsl_font_options_html($meta['vsl_resume_font'] ?? 'Inter'); ?></select></label>
                                <label>Custom text font .woff2 URL<input name="vsl_resume_font_woff2" value="<?php echo esc_url($meta['vsl_resume_font_woff2'] ?? ''); ?>" placeholder="https://yourdomain.com/fonts/font.woff2"></label>
                            </div>
                            <p class="itp-note">Preview: while this tab is open, the right preview shows the resume screen. Public behavior: it appears only when someone returns before finishing the video.</p>
                        </div>

                        <div class="itp-vsl-panel <?php echo $active_vsl_tab === 'embed' ? 'active' : ''; ?>" data-itp-vsl-panel="embed">
                            <h3>Embed output</h3>
                            <?php if ($is_edit) : ?>
                                <div class="itp-vsl-embed-studio-box">
                                    <div class="itp-vsl-embed-hero">
                                        <div>
                                            <strong>Incorporate this player anywhere.</strong>
                                            <p>Copy the JavaScript embed into Elementor HTML, WordPress Custom HTML, a sales page, or a funnel builder. The output is player-only — no website header/footer.</p>
                                        </div>
                                        <div class="itp-vsl-embed-hero-actions">
                                            <button type="button" class="itp-btn itp-btn-primary" data-itp-copy-field="js">Copy JS</button>
                                            <button type="button" class="itp-btn itp-btn-ghost" data-itp-copy-field="iframe">Copy iFrame</button>
                                        </div>
                                    </div>
                                    <div class="itp-vsl-embed-tabs">
                                        <span class="active">Recommended</span><span>JavaScript</span><span>iFrame</span>
                                    </div>
                                    <div class="itp-vsl-embed-code-grid">
                                        <label>JavaScript embed<textarea class="itp-code-area" readonly data-itp-copy-source="js"><?php echo esc_textarea($js); ?></textarea></label>
                                        <label>iFrame embed<textarea class="itp-code-area" readonly data-itp-copy-source="iframe"><?php echo esc_textarea($iframe); ?></textarea></label>
                                    </div>
                                    <div class="itp-vsl-preload-box">
                                        <strong>Optimize loading speed</strong>
                                        <p>Optional: paste this in the page &lt;head&gt; to preload the player script faster. The main JavaScript embed already works without this.</p>
                                        <textarea class="itp-code-area" readonly data-itp-copy-source="speed"><?php echo esc_textarea($speed_code); ?></textarea>
                                        <button type="button" class="itp-btn itp-btn-ghost" data-itp-copy-field="speed">Copy speed code</button>
                                    </div>
                                    <div class="itp-row-actions"><a class="itp-btn itp-btn-ghost" href="<?php echo esc_url(admin_url('admin.php?page=itp-analytics&id=' . $id . $this->analytics_query_suffix())); ?>">Analytics</a></div>
                                </div>
                            <?php else : ?>
                                <p class="itp-note">Nothing has been saved yet. Use the Save & update embed button above to generate the embed output.</p>
                            <?php endif; ?>
                        </div>
                    </div>
                </aside>
                <section class="itp-vsl-preview-panel">
                    <div class="itp-vsl-preview-shell <?php echo esc_attr($admin_preview_ratio_class); ?>">
                        <div class="itp-vsl-preview-top"><span>Live preview</span><em>What the visitor sees</em><div class="itp-vsl-preview-devices" aria-label="Preview size"><button type="button" class="active" data-itp-vsl-device="desktop">Desktop</button><button type="button" data-itp-vsl-device="mobile">Mobile</button></div></div>
                        <div class="itp-vsl-preview-box" data-itp-admin-vsl-preview>
                            <div class="itp-vsl-preview-video itp-preview-aspect-locked <?php echo esc_attr($admin_preview_ratio_class); ?> <?php echo $has_video ? 'has-video' : 'no-video'; ?> <?php echo esc_attr('hook-' . ($meta['vsl_hook_position'] ?: 'bottom_left')); ?>" style="<?php echo esc_attr($admin_preview_frame_style); ?>" data-itp-preview-video-box>
                                <video data-itp-preview-video muted playsinline autoplay loop preload="metadata" <?php echo $meta['vsl_poster_url'] ? 'poster="' . esc_url($meta['vsl_poster_url']) . '"' : ''; ?>><?php if ($admin_preview_video) : ?><source src="<?php echo esc_url($admin_preview_video); ?>" type="video/mp4"><?php endif; ?></video>
                                <button type="button" class="itp-vsl-preview-big-play" data-preview-control="big_play" <?php echo ($meta['vsl_ctrl_big_play'] ?? '0') === '1' ? '' : 'hidden'; ?> style="--itp-vsl-control-color:<?php echo esc_attr($meta['vsl_ctrl_color'] ?? '#ffffff'); ?>;--itp-vsl-control-bg:<?php echo esc_attr($meta['vsl_ctrl_bg'] ?? '#111827'); ?>;">▶</button>
                                <button type="button" class="itp-vsl-preview-skip itp-vsl-preview-skip-back" data-preview-control="back10" <?php echo ($meta['vsl_ctrl_back10'] ?? '0') === '1' ? '' : 'hidden'; ?>>↶<b>10</b></button>
                                <button type="button" class="itp-vsl-preview-skip itp-vsl-preview-skip-forward" data-preview-control="forward10" <?php echo ($meta['vsl_ctrl_forward10'] ?? '0') === '1' ? '' : 'hidden'; ?>><b>10</b>↷</button>
                                <div class="itp-vsl-preview-controls" data-itp-preview-controls style="--itp-vsl-control-color:<?php echo esc_attr($meta['vsl_ctrl_color'] ?? '#ffffff'); ?>;--itp-vsl-control-bg:<?php echo esc_attr($meta['vsl_ctrl_bg'] ?? '#050816'); ?>;">
                                    <span data-preview-control="small_play" <?php echo ($meta['vsl_ctrl_small_play'] ?? '0') === '1' ? '' : 'hidden'; ?>>▶</span>
                                    <span data-preview-control="volume" <?php echo ($meta['vsl_ctrl_volume'] ?? '0') === '1' ? '' : 'hidden'; ?>>🔊</span>
                                    <span data-preview-control="time" <?php echo ($meta['vsl_ctrl_time'] ?? '0') === '1' ? '' : 'hidden'; ?>>0:12</span>
                                    <span data-preview-control="speed" <?php echo ($meta['vsl_ctrl_speed'] ?? '0') === '1' ? '' : 'hidden'; ?>>1×</span>
                                    <span data-preview-control="fullscreen" <?php echo ($meta['vsl_ctrl_fullscreen'] ?? '0') === '1' ? '' : 'hidden'; ?>>⛶</span>
                                </div>
                                <div class="itp-vsl-preview-progress" data-itp-preview-progress style="height:<?php echo esc_attr((int)$meta['vsl_progress_height']); ?>px" <?php echo ($meta['vsl_progress_on'] ?? '1') === '1' ? '' : 'hidden'; ?>><i style="background:<?php echo esc_attr($meta['vsl_progress_color']); ?>;"></i></div>
                                <div class="itp-vsl-preview-empty" data-itp-preview-empty <?php echo $has_video ? 'hidden' : ''; ?>><strong>Select a WordPress Media MP4</strong><span>The preview will update here.</span></div>
                                <?php
                                    $preview_overlay_template = ($meta['vsl_overlay_template'] ?? 'wide') === 'large' ? 'full' : ($meta['vsl_overlay_template'] ?? 'wide');
                                    $preview_overlay_icon_key = ($preview_overlay_template === 'play' || $preview_overlay_template === 'icon_play') ? 'play' : (($preview_overlay_template === 'compact' || $preview_overlay_template === 'icon_blink') ? 'blink' : 'sound');
                                    $preview_overlay_icon_url = plugins_url('assets/invalid-traffic-smartplay-' . $preview_overlay_icon_key . '.svg', __FILE__);
                                ?>
                                <button type="button" class="itp-vsl-preview-overlay itp-smartplay-object template-<?php echo esc_attr($meta['vsl_overlay_template']); ?> <?php echo ($meta['vsl_overlay_template'] === 'custom' && !empty($meta['vsl_overlay_image'])) ? 'has-custom-image' : ''; ?> <?php echo ($meta['vsl_overlay_pulse'] === '1' && ($meta['vsl_overlay_template'] ?? 'wide') !== 'full') ? 'pulse pulse-' . esc_attr($meta['vsl_overlay_pulse_mode'] ?? 'scale') . ' effect-' . esc_attr($meta['vsl_overlay_pulse_mode'] ?? 'scale') : ''; ?>" style="background:<?php echo esc_attr($meta['vsl_overlay_color']); ?>;color:<?php echo esc_attr($meta['vsl_overlay_text_color']); ?>;--itp-overlay-bg:<?php echo esc_attr($meta['vsl_overlay_color']); ?>;--itp-overlay-fg:<?php echo esc_attr($meta['vsl_overlay_text_color']); ?>;--itp-overlay-opacity:<?php echo esc_attr(((int)($meta['vsl_overlay_opacity'] ?? 92)) / 100); ?>;--itp-overlay-opacity-pct:<?php echo esc_attr((int)($meta['vsl_overlay_opacity'] ?? 92)); ?>%;--itp-design-x:<?php echo esc_attr($meta['vsl_overlay_x']); ?>%;--itp-design-y:<?php echo esc_attr($meta['vsl_overlay_y']); ?>%;--itp-design-w:<?php echo esc_attr($meta['vsl_overlay_width']); ?>%;--itp-design-r:<?php echo esc_attr($meta['vsl_overlay_rotate'] ?? 0); ?>deg;--itp-overlay-pulse-duration:<?php echo esc_attr($meta['vsl_overlay_pulse_speed'] ?? '1.30'); ?>s;--itp-icon-blink-duration:<?php echo esc_attr(max(0.40, ((float)($meta['vsl_overlay_pulse_speed'] ?? '1.30')) * 1.35)); ?>s;--itp-overlay-font:<?php echo esc_attr($this->vsl_font_stack($meta['vsl_overlay_font'] ?? 'Inter')); ?>;font-family:<?php echo esc_attr($this->vsl_font_stack($meta['vsl_overlay_font'] ?? 'Inter')); ?>;" data-itp-preview-overlay data-itp-designable="overlay" <?php echo ($meta['vsl_smart_autoplay'] ?? '1') === '1' ? '' : 'hidden'; ?>>
                                    <span class="itp-smartplay-inner" aria-hidden="true">
                                        <?php if (!empty($meta['vsl_overlay_image'])) : ?><img class="itp-vsl-custom-overlay-image" src="<?php echo esc_url($meta['vsl_overlay_image']); ?>" alt=""><?php endif; ?>
                                        <img class="itp-vsl-itbrand-icon itp-vsl-itbrand-icon-<?php echo esc_attr($preview_overlay_icon_key); ?>" data-itp-template-icon src="<?php echo esc_url($preview_overlay_icon_url); ?>" alt="" aria-hidden="true">
                                        <strong data-itp-preview-overlay-top><?php echo esc_html($meta['vsl_overlay_top']); ?></strong><span data-itp-preview-overlay-bottom><?php echo esc_html($meta['vsl_overlay_bottom']); ?></span>
                                    </span>
                                </button>
                                <div class="itp-vsl-preview-mini <?php echo esc_attr($meta['vsl_hook_position'] ?: 'bottom_left'); ?>" style="opacity:<?php echo esc_attr(((int)$meta['vsl_hook_opacity']) / 100); ?>;--itp-hook-font:<?php echo esc_attr($this->vsl_font_stack($meta['vsl_hook_font'] ?? 'Inter')); ?>;font-family:<?php echo esc_attr($this->vsl_font_stack($meta['vsl_hook_font'] ?? 'Inter')); ?>;--itp-design-x:<?php echo esc_attr($meta['vsl_hook_x']); ?>%;--itp-design-y:<?php echo esc_attr($meta['vsl_hook_y']); ?>%;--itp-design-w:<?php echo esc_attr($meta['vsl_hook_width']); ?>%;" data-itp-preview-hook data-itp-designable="hook" <?php echo ($meta['vsl_hook_on'] !== '1' || (empty($meta['vsl_hook_text']) && empty($meta['vsl_hook_image']))) ? 'hidden' : ''; ?>><?php if (!empty($meta['vsl_hook_image'])) : ?><img src="<?php echo esc_url($meta['vsl_hook_image']); ?>" alt=""><?php endif; ?><span><?php echo esc_html($meta['vsl_hook_text']); ?></span></div>
                                <div class="itp-vsl-preview-recovery pos-<?php echo esc_attr($meta['vsl_pause_button_position'] ?? 'bottom'); ?>" data-itp-preview-recovery hidden>
                                    <button type="button" class="itp-vsl-preview-recovery-bg" data-itp-preview-recovery-resume>
                                        <?php if (!empty($meta['vsl_pause_image'])) : ?><img src="<?php echo esc_url($meta['vsl_pause_image']); ?>" alt=""><?php else : ?><span>Recovery preview</span><em>Pause image / GIF appears here.</em><?php endif; ?>
                                    </button>
                                    <a class="itp-vsl-preview-recovery-button size-<?php echo esc_attr($meta['vsl_pause_button_size'] ?? 'medium'); ?>" data-itp-preview-recovery-button href="<?php echo esc_url($meta['vsl_pause_button_url'] ?: '#'); ?>" style="--itp-pause-btn:<?php echo esc_attr($meta['vsl_pause_button_color'] ?? '#2563eb'); ?>;--itp-pause-btn-hover:<?php echo esc_attr($meta['vsl_pause_button_hover_color'] ?? '#1d4ed8'); ?>;--itp-pause-btn-text:<?php echo esc_attr($meta['vsl_pause_button_text_color'] ?? '#fff'); ?>;--itp-pause-font:<?php echo esc_attr($this->vsl_font_stack($meta['vsl_pause_font'] ?? 'Inter')); ?>;font-family:<?php echo esc_attr($this->vsl_font_stack($meta['vsl_pause_font'] ?? 'Inter')); ?>;" <?php echo ($meta['vsl_pause_button_on'] ?? '0') === '1' ? '' : 'hidden'; ?>><?php echo esc_html($meta['vsl_pause_button_text'] ?: 'Continue'); ?></a>
                                </div>
                                <div class="itp-vsl-preview-resume-screen" data-itp-preview-resume hidden style="<?php echo esc_attr('--itp-resume-bg:' . ($meta['vsl_resume_bg'] ?? '#581c87') . ';--itp-resume-text:' . ($meta['vsl_resume_text_color'] ?? '#ffffff') . ';--itp-resume-font:' . $this->vsl_font_stack($meta['vsl_resume_font'] ?? 'Inter') . ';'); ?>">
                                    <strong><?php echo esc_html($meta['vsl_resume_message'] ?: 'You already started watching this video'); ?></strong>
                                    <button type="button"><span>▶</span><?php echo esc_html($meta['vsl_resume_continue_text'] ?: 'Continue watching?'); ?></button>
                                    <button type="button"><span>↻</span><?php echo esc_html($meta['vsl_resume_restart_text'] ?: 'Start from the beginning?'); ?></button>
                                </div>
                            </div>
                            <button type="button" class="itp-vsl-preview-cta <?php echo $meta['vsl_cta_shadow'] === '1' ? 'shadow' : ''; ?> <?php echo $meta['vsl_cta_pulse'] === '1' ? 'pulse effect-' . esc_attr($meta['vsl_cta_animation'] ?? 'premium') : ''; ?>" style="background:<?php echo esc_attr($meta['vsl_cta_color']); ?>;color:<?php echo esc_attr($meta['vsl_cta_text_color']); ?>;font-size:<?php echo esc_attr((int)$meta['vsl_cta_font_size']); ?>px;font-weight:<?php echo esc_attr($meta['vsl_cta_weight']); ?>;font-family:<?php echo esc_attr(($meta['vsl_cta_font'] ?? 'Inter') === 'system' ? 'system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif' : ($meta['vsl_cta_font'] ?? 'Inter')); ?>;border-radius:<?php echo esc_attr((int)$meta['vsl_cta_radius']); ?>px;padding:<?php echo esc_attr((int)$meta['vsl_cta_padding_y']); ?>px <?php echo esc_attr((int)$meta['vsl_cta_padding_x']); ?>px;--itp-design-x:<?php echo esc_attr($meta['vsl_cta_x']); ?>%;--itp-design-y:<?php echo esc_attr($meta['vsl_cta_y']); ?>%;--itp-design-w:<?php echo esc_attr($meta['vsl_cta_width']); ?>%;--itp-cta-pulse-duration:<?php echo esc_attr($meta['vsl_cta_pulse_speed'] ?? '1.45'); ?>s;" data-itp-preview-cta data-itp-designable="cta" <?php echo ($meta['vsl_cta_on'] ?? '0') === '1' ? '' : 'hidden'; ?>><?php if (!empty($meta['vsl_cta_image'])) : ?><img src="<?php echo esc_url($meta['vsl_cta_image']); ?>" alt=""><?php else : ?><?php echo esc_html($meta['vsl_cta_text'] ?: 'Continue'); ?><?php endif; ?></button>
                        </div>
                        <div class="itp-vsl-preview-help">
                            <strong>Recommended flow</strong>
                            <span>Select the main MP4 → set autoplay/progress/CTA/recovery/hooks → save → copy JS or iFrame → test analytics.</span>
                        </div>
                    </div>
                </section>
            </div>
        </section>
        <?php
    }


    public function shortcode_vsl_player($atts) {
        $atts = shortcode_atts(['id' => 0, 'slug' => '', 'max_width' => '100%'], $atts, 'invalid_traffic_vsl');
        $id = absint($atts['id']);
        if (!$id && !empty($atts['slug'])) {
            $post = get_page_by_path(sanitize_title($atts['slug']), OBJECT, self::CPT);
            if ($post) $id = (int)$post->ID;
        }
        if (!$id || get_post_type($id) !== self::CPT || get_post_status($id) !== 'publish') {
            return '<div class="itp-vsl-shortcode-missing">Invalid Traffic VSL not found.</div>';
        }
        $slug = sanitize_title(get_post_field('post_name', $id));
        $player_js_url = $this->vsl_player_js_url($id);
        $max_width = preg_replace('/[^0-9a-zA-Z.%\-]/', '', (string)$atts['max_width']);
        if (!$max_width) $max_width = '100%';
        $element_id = 'itp-vsl-' . $slug . '-' . $id;
        return '<div class="itp-vsl-shortcode-wrap" style="display:block;width:100%;max-width:' . esc_attr($max_width) . ';margin:0 auto;">'
            . '<it-vsl-player id="' . esc_attr($element_id) . '" data-player="' . esc_attr($slug) . '" style="display:block;margin:0 auto;width:100%;"></it-vsl-player>'
            . '<script type="text/javascript">(function(){var s=document.createElement("script");s.src=' . wp_json_encode($player_js_url) . ';s.async=true;document.head.appendChild(s);})();</script>'
            . '</div>';
    }

    private function elementor_template_options() {
        $base = plugin_dir_path(__FILE__) . 'assets/elementor-templates';
        $items = [];
        if (!is_dir($base)) return $items;
        $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($base, FilesystemIterator::SKIP_DOTS));
        foreach ($iterator as $file) {
            if (!$file->isFile() || strtolower($file->getExtension()) !== 'json') continue;
            $path = $file->getPathname();
            $rel = ltrim(str_replace([$base, '\\'], ['', '/'], $path), '/');
            $dir = trim(dirname($rel), '.');
            $label = $dir ? str_replace('/', ' / ', $dir) : preg_replace('/\.json$/i', '', basename($rel));
            $key = sanitize_title($dir ? $dir : basename($rel, '.json'));
            if (!$key) $key = 'template-' . substr(md5($rel), 0, 8);
            $base_key = $key;
            $i = 2;
            while (isset($items[$key])) { $key = $base_key . '-' . $i; $i++; }
            $items[$key] = ['label' => $label, 'path' => $path, 'rel' => $rel];
        }
        uasort($items, static function($a, $b){ return strcasecmp($a['label'], $b['label']); });
        return $items;
    }

    private function elementor_vsl_section($bridge_id) {
        $shortcode = '[invalid_traffic_vsl id="' . (int)$bridge_id . '"]';
        return [
            'id' => 'itpvsl' . substr(md5((string)$bridge_id), 0, 5),
            'elType' => 'section',
            'settings' => [
                'layout' => 'boxed',
                'background_background' => 'classic',
                'background_color' => '#000000',
                'padding' => ['unit' => 'px', 'top' => '60', 'right' => '20', 'bottom' => '60', 'left' => '20', 'isLinked' => false],
            ],
            'elements' => [[
                'id' => 'itpcol' . substr(md5('col' . $bridge_id), 0, 5),
                'elType' => 'column',
                'settings' => ['_column_size' => 100],
                'elements' => [[
                    'id' => 'itpshort' . substr(md5('short' . $bridge_id), 0, 5),
                    'elType' => 'widget',
                    'widgetType' => 'shortcode',
                    'settings' => ['shortcode' => $shortcode],
                    'elements' => [],
                ]],
                'isInner' => false,
            ]],
            'isInner' => false,
        ];
    }

    private function inject_vsl_into_elementor_content($content, $bridge_id, $position = 'after_first') {
        if (!is_array($content)) $content = [];
        $section = $this->elementor_vsl_section($bridge_id);
        $position = in_array($position, ['top','after_first','bottom'], true) ? $position : 'after_first';
        if ($position === 'top' || count($content) === 0) {
            array_unshift($content, $section);
        } elseif ($position === 'bottom') {
            $content[] = $section;
        } else {
            array_splice($content, 1, 0, [$section]);
        }
        return $content;
    }

    private function landing_page_ids_meta($bridge_id) {
        $bridge_id = absint($bridge_id);
        if (!$bridge_id) return [];
        $stored = get_post_meta($bridge_id, '_itp_elementor_landing_page_ids', true);
        if (!is_array($stored)) $stored = [];
        $ids = [];
        foreach ($stored as $pid) {
            $pid = absint($pid);
            if ($pid && get_post_type($pid) === 'page') $ids[] = $pid;
        }
        $ids = array_values(array_unique($ids));
        return $ids;
    }

    private function remember_landing_page_for_bridge($bridge_id, $page_id) {
        $bridge_id = absint($bridge_id);
        $page_id = absint($page_id);
        if (!$bridge_id || !$page_id || get_post_type($page_id) !== 'page') return;
        $ids = $this->landing_page_ids_meta($bridge_id);
        if (!in_array($page_id, $ids, true)) $ids[] = $page_id;
        update_post_meta($bridge_id, '_itp_elementor_landing_page_ids', array_values(array_unique(array_map('absint', $ids))));
        update_post_meta($page_id, '_itp_source_bridge_id', (int)$bridge_id);
    }

    private function forget_landing_page_for_bridge($bridge_id, $page_id) {
        $bridge_id = absint($bridge_id);
        $page_id = absint($page_id);
        if (!$bridge_id || !$page_id) return;
        $ids = array_values(array_filter($this->landing_page_ids_meta($bridge_id), function($pid) use ($page_id) {
            return (int)$pid !== (int)$page_id;
        }));
        update_post_meta($bridge_id, '_itp_elementor_landing_page_ids', $ids);
    }

    private function landing_pages_for_bridge($bridge_id) {
        $bridge_id = absint($bridge_id);
        if (!$bridge_id) return [];
        $current_id = (int)get_post_meta($bridge_id, '_itp_elementor_landing_page_id', true);
        if (!$current_id) {
            $meta = $this->bridge_meta($bridge_id);
            $current_id = (int)($meta['elementor_landing_page_id'] ?? 0);
        }

        // v0.18.219: keep a real many-landing relationship per VSL.
        // Older versions only remembered the current landing page; this merges the saved list,
        // the current pointer and every WordPress page marked as generated from this bridge.
        $page_ids = $this->landing_page_ids_meta($bridge_id);
        if ($current_id) $page_ids[] = $current_id;

        $posts = get_posts([
            'post_type' => 'page',
            'post_status' => ['publish','draft','private'],
            'numberposts' => 100,
            'orderby' => 'date',
            'order' => 'ASC',
            'meta_key' => '_itp_source_bridge_id',
            'meta_value' => $bridge_id,
        ]);
        foreach ($posts as $page) {
            if ($page && $page->post_type === 'page') $page_ids[] = (int)$page->ID;
        }
        $page_ids = array_values(array_unique(array_filter(array_map('absint', $page_ids))));

        $rows = [];
        foreach ($page_ids as $page_id) {
            if (get_post_type($page_id) !== 'page') continue;
            $status = get_post_status($page_id);
            if (!in_array($status, ['publish','draft','private'], true)) continue;
            $source_bridge = (int)get_post_meta($page_id, '_itp_source_bridge_id', true);
            if ($source_bridge && $source_bridge !== (int)$bridge_id) continue;
            if (!$source_bridge) update_post_meta($page_id, '_itp_source_bridge_id', (int)$bridge_id);
            $url = get_permalink($page_id);
            $path = wp_parse_url($url, PHP_URL_PATH);
            if (!$path) $path = '/' . trim((string)get_post_field('post_name', $page_id), '/') . '/';
            $rows[] = [
                'id' => (int)$page_id,
                'title' => get_the_title($page_id),
                'slug' => (string)get_post_field('post_name', $page_id),
                'url' => $url,
                'path' => $path,
                'template' => (string)get_post_meta($page_id, '_itp_elementor_template', true),
                'date' => (string)get_post_field('post_date', $page_id),
                'current' => ((int)$page_id === (int)$current_id),
            ];
        }
        usort($rows, function($a, $b) {
            return strcmp((string)($a['date'] ?? ''), (string)($b['date'] ?? ''));
        });
        if (!empty($rows)) {
            $has_current = false;
            foreach ($rows as $row) { if (!empty($row['current'])) { $has_current = true; break; } }
            if (!$has_current) {
                $last_index = count($rows) - 1;
                $rows[$last_index]['current'] = true;
            }
            // Persist the merged list so My Bridges and the editor stay in sync even after older saves.
            update_post_meta($bridge_id, '_itp_elementor_landing_page_ids', array_values(array_unique(array_map(function($row){ return (int)$row['id']; }, $rows))));
        }
        return $rows;
    }

    private function set_current_landing_page($bridge_id, $page_id, $meta = null) {
        $bridge_id = absint($bridge_id);
        $page_id = absint($page_id);
        if (!$bridge_id) return 0;
        if ($page_id && (get_post_type($page_id) !== 'page' || (int)get_post_meta($page_id, '_itp_source_bridge_id', true) !== $bridge_id)) {
            return 0;
        }
        if ($page_id) {
            $this->remember_landing_page_for_bridge($bridge_id, $page_id);
            update_post_meta($bridge_id, '_itp_elementor_landing_page_id', $page_id);
            update_post_meta($bridge_id, '_itp_elementor_landing_page_url', esc_url_raw(get_permalink($page_id)));
            $actual_slug = (string)get_post_field('post_name', $page_id);
            if ($actual_slug !== '') update_post_meta($bridge_id, '_itp_elementor_landing_actual_slug', $actual_slug);
        } else {
            delete_post_meta($bridge_id, '_itp_elementor_landing_page_id');
            delete_post_meta($bridge_id, '_itp_elementor_landing_page_url');
            delete_post_meta($bridge_id, '_itp_elementor_landing_actual_slug');
        }
        if (is_array($meta)) {
            $meta['elementor_landing_page_id'] = $page_id;
            $meta['elementor_landing_slug'] = $page_id ? (string)get_post_field('post_name', $page_id) : '';
            $meta['elementor_landing_mode'] = $page_id ? 'keep' : 'create';
            update_post_meta($bridge_id, '_itp_meta', $meta);
        }
        return $page_id;
    }

    private function maybe_create_elementor_landing_page($bridge_id, $meta) {
        if (($meta['template'] ?? '') !== 'vsl_landing') return 0;
        $page_id = (int)($meta['elementor_landing_page_id'] ?? 0);
        if ($page_id && get_post_type($page_id) !== 'page') $page_id = 0;
        if (($meta['elementor_landing_autocreate'] ?? '1') !== '1') return $page_id;

        $mode = sanitize_key($meta['elementor_landing_mode'] ?? '');
        if (!$mode) $mode = $page_id ? 'keep' : 'create';

        // Safe default: once a landing page exists, a normal bridge save must NOT overwrite Elementor edits.
        // It may still update the title/slug only, because those fields are URL controls, not template content.
        if ($page_id && $mode === 'keep') {
            $desired_title = sanitize_text_field($meta['elementor_landing_title'] ?: get_the_title($page_id));
            $desired_slug = sanitize_title($meta['elementor_landing_slug'] ?: get_post_field('post_name', $page_id));
            $current_slug = (string)get_post_field('post_name', $page_id);
            $post_update = ['ID' => $page_id];
            if ($desired_title && $desired_title !== get_the_title($page_id)) $post_update['post_title'] = $desired_title;
            if ($desired_slug && $desired_slug !== $current_slug) $post_update['post_name'] = $desired_slug;
            if (count($post_update) > 1) wp_update_post($post_update, true);
            update_post_meta($page_id, '_itp_source_bridge_id', (int)$bridge_id);
            $this->set_current_landing_page($bridge_id, $page_id);
            return (int)$page_id;
        }

        $templates = $this->elementor_template_options();
        $selected = sanitize_key($meta['elementor_landing_template'] ?? '');
        if (!$selected || empty($templates[$selected]['path']) || !is_readable($templates[$selected]['path'])) return $page_id;
        $raw = file_get_contents($templates[$selected]['path']);
        $json = json_decode($raw, true);
        if (!is_array($json)) return $page_id;
        $content = $json['content'] ?? [];
        $content = $this->inject_vsl_into_elementor_content($content, $bridge_id, $meta['elementor_landing_position'] ?? 'after_first');
        $title = sanitize_text_field($meta['elementor_landing_title'] ?: get_the_title($bridge_id));
        if (!$title) $title = 'VSL Landing Page';
        $slug = sanitize_title($meta['elementor_landing_slug'] ?: ($title . '-landing'));
        if (!$slug) $slug = 'vsl-landing-' . (int)$bridge_id;

        // Create new copy means: leave the old Elementor page untouched, create a fresh page, then make the bridge point to the new copy.
        if ($mode === 'copy') {
            $page_id = 0;
        }

        $postarr = [
            'post_type' => 'page',
            'post_status' => 'publish',
            'post_title' => $title,
            'post_name' => $slug,
            'post_content' => '[invalid_traffic_vsl id="' . (int)$bridge_id . '"]',
        ];
        if ($page_id && $mode === 'regenerate') $postarr['ID'] = $page_id;
        $saved_page_id = (!empty($postarr['ID'])) ? wp_update_post($postarr, true) : wp_insert_post($postarr, true);
        if (is_wp_error($saved_page_id) || !$saved_page_id) return $page_id;
        update_post_meta($saved_page_id, '_elementor_edit_mode', 'builder');
        update_post_meta($saved_page_id, '_elementor_template_type', 'wp-page');
        update_post_meta($saved_page_id, '_elementor_version', sanitize_text_field($json['version'] ?? '3.0.0'));
        update_post_meta($saved_page_id, '_elementor_data', wp_slash(wp_json_encode($content)));
        update_post_meta($saved_page_id, '_elementor_page_settings', $json['page_settings'] ?? []);
        update_post_meta($saved_page_id, '_wp_page_template', 'elementor_canvas');
        update_post_meta($saved_page_id, '_itp_source_bridge_id', (int)$bridge_id);
        update_post_meta($saved_page_id, '_itp_elementor_template', $selected);
        update_post_meta($saved_page_id, '_itp_landing_generation_mode', $mode);
        $this->remember_landing_page_for_bridge($bridge_id, $saved_page_id);
        $this->set_current_landing_page($bridge_id, $saved_page_id);
        return (int)$saved_page_id;
    }

    private function render_landing_setup_fields($meta) {
        $tpl = $meta['template'] ?: 'blue_cards';
        $show = in_array($tpl, ['vsl_landing'], true);
        $elementor_templates = $this->elementor_template_options();
        $selected_template = $meta['elementor_landing_template'] ?: (isset($elementor_templates['digital-product-01']) ? 'digital-product-01' : array_key_first($elementor_templates));
        $bridge_id = isset($_GET['id']) ? absint($_GET['id']) : absint(get_the_ID());
        $landing_pages = $bridge_id ? $this->landing_pages_for_bridge($bridge_id) : [];
        $landing_page_id = (int)($meta['elementor_landing_page_id'] ?? ($bridge_id ? get_post_meta($bridge_id, '_itp_elementor_landing_page_id', true) : 0));
        $landing_url = $landing_page_id && get_post_status($landing_page_id) ? get_permalink($landing_page_id) : '';
        if (!$landing_url && !empty($landing_pages)) {
            foreach ($landing_pages as $lp_tmp) {
                if (!empty($lp_tmp['current'])) { $landing_page_id = (int)$lp_tmp['id']; $landing_url = $lp_tmp['url']; break; }
            }
            if (!$landing_url) { $lp_tmp = end($landing_pages); $landing_page_id = (int)($lp_tmp['id'] ?? 0); $landing_url = $lp_tmp['url'] ?? ''; }
        }
        $default_landing_slug = trim((string)($meta['elementor_landing_slug'] ?? ''));
        if ($default_landing_slug === '' && $landing_page_id) {
            $default_landing_slug = (string)get_post_field('post_name', $landing_page_id);
        }
        if ($default_landing_slug === '') {
            $default_landing_slug = !empty($meta['slug']) ? sanitize_title($meta['slug'] . '-landing') : 'landing';
        }
        // When editing an existing landing, show the template that page was actually generated with.
        // The user can change it, then click + New landing page to create another version or Save to regenerate.
        if ($landing_page_id) {
            $current_page_template = sanitize_key((string)get_post_meta($landing_page_id, '_itp_elementor_template', true));
            if ($current_page_template && isset($elementor_templates[$current_page_template])) {
                $selected_template = $current_page_template;
            }
        }
        ?>
        <section class="itp-card itp-landing-options <?php echo $show ? '' : 'itp-template-options-hidden'; ?>" data-itp-template-options="vsl_landing">
            <div class="itp-card-head">
                <div>
                    <span class="itp-kicker">Elementor landing automation</span>
                    <h2>3. Create landing page from template</h2>
                    <p>Select one of the bonus Elementor templates. One VSL can have multiple connected landing pages, each with its own slug/template.</p>
                </div>
                <?php if ($landing_url && $bridge_id) : ?><div class="itp-row-actions"><a class="itp-btn itp-btn-ghost" href="<?php echo esc_url($landing_url); ?>" target="_blank" rel="noopener">Open landing page</a><a class="itp-btn itp-btn-ghost danger" href="<?php echo esc_url(wp_nonce_url(admin_url('admin.php?page=itp-create&id=' . $bridge_id . '&itp_action=delete_landing&landing_page_id=' . (int)$landing_page_id), 'itp_action_' . $bridge_id)); ?>" onclick="return confirm('This deletes the WordPress landing page only. The VSL, analytics and leads stay saved.');">Delete landing page</a></div><?php endif; ?>
            </div>
            <div class="itp-form-grid two">
                <label>Landing page title<input name="elementor_landing_title" value="<?php echo esc_attr($meta['elementor_landing_title'] ?: $meta['title']); ?>" placeholder="Ex: Finance VSL page"></label>
                <label>Landing URL slug<input name="elementor_landing_slug" value="<?php echo esc_attr($default_landing_slug); ?>" placeholder="Ex: finance-offer"></label>
            </div>
            <div class="itp-final-url">Landing page URL: <strong data-itp-landing-final-url data-itp-landing-base-url="<?php echo esc_attr(home_url('/')); ?>"><?php echo esc_html($landing_url ?: $this->preview_url($default_landing_slug)); ?></strong></div>
            <div class="itp-form-grid two itp-landing-create-controls">
                <label>Bonus Elementor template
                    <select name="elementor_landing_template" data-itp-elementor-template-select data-itp-current-template="<?php echo esc_attr($selected_template); ?>" data-itp-has-landing="<?php echo $landing_url ? '1' : '0'; ?>">
                        <?php if (!$elementor_templates) : ?><option value="">No templates bundled</option><?php endif; ?>
                        <?php foreach ($elementor_templates as $key => $item) : ?>
                            <option value="<?php echo esc_attr($key); ?>" <?php selected($selected_template, $key); ?>><?php echo esc_html($item['label']); ?></option>
                        <?php endforeach; ?>
                    </select>
                </label>
                <label>VSL insert position
                    <select name="elementor_landing_position">
                        <option value="after_first" <?php selected($meta['elementor_landing_position'] ?? 'after_first', 'after_first'); ?>>After first template section</option>
                        <option value="top" <?php selected($meta['elementor_landing_position'] ?? 'after_first', 'top'); ?>>Top of page</option>
                        <option value="bottom" <?php selected($meta['elementor_landing_position'] ?? 'after_first', 'bottom'); ?>>Bottom of page</option>
                    </select>
                </label>
            </div>
            <label class="itp-toggle">
                <input type="checkbox" name="elementor_landing_autocreate" value="1" <?php checked($meta['elementor_landing_autocreate'] ?? '1', '1'); ?>>
                <span></span>
                <strong>Elementor landing automation</strong>
                <em>Create landing pages automatically. Choose a template + slug, then use + New landing page to add another version to this same VSL.</em>
            </label>
            <input type="hidden" name="elementor_landing_page_id" value="<?php echo esc_attr((int)$landing_page_id); ?>">
            <input type="hidden" name="elementor_landing_mode" value="<?php echo $landing_url ? 'keep' : 'create'; ?>" data-itp-landing-mode>
            <div class="itp-landing-choice-status" data-itp-landing-choice-status hidden></div>
            <?php if (!empty($landing_pages) && $bridge_id) : ?>
                <div class="itp-connected-landings" data-itp-connected-landings>
                    <strong>Connected landing pages</strong>
                    <div>
                    <?php foreach ($landing_pages as $lp) : $lp_tpl_key = sanitize_key((string)($lp['template'] ?? '')); $lp_tpl_label = ($lp_tpl_key && isset($elementor_templates[$lp_tpl_key])) ? $elementor_templates[$lp_tpl_key]['label'] : 'Elementor template'; ?>
                        <span class="itp-connected-landing <?php echo !empty($lp['current']) ? 'is-current' : ''; ?>">
                            <code><?php echo esc_html($lp['path']); ?></code>
                            <small><?php echo esc_html($lp_tpl_label); ?></small>
                            <?php if (!empty($lp['current'])) : ?><em>Current</em><?php endif; ?>
                            <a href="<?php echo esc_url($lp['url']); ?>" target="_blank" rel="noopener">Open</a>
                            <a href="<?php echo esc_url(admin_url('post.php?post=' . (int)$lp['id'] . '&action=elementor')); ?>" target="_blank" rel="noopener">Edit Elementor</a>
                            <?php if (empty($lp['current'])) : ?><a href="<?php echo esc_url(wp_nonce_url(admin_url('admin.php?page=itp-create&id=' . $bridge_id . '&itp_action=set_current_landing&landing_page_id=' . (int)$lp['id']), 'itp_action_' . $bridge_id)); ?>">Set current</a><?php endif; ?>
                            <a class="danger" href="<?php echo esc_url(wp_nonce_url(admin_url('admin.php?page=itp-create&id=' . $bridge_id . '&itp_action=delete_landing&landing_page_id=' . (int)$lp['id']), 'itp_action_' . $bridge_id)); ?>" onclick="return confirm('Delete this WordPress landing page? The VSL, analytics and leads stay saved.');">Delete</a>
                        </span>
                    <?php endforeach; ?>
                        <button type="button" class="itp-connected-landing itp-add-landing-page" data-itp-add-landing-page><code>+ New landing page</code><span>Create now with selected template + slug</span></button>
                    </div>
                </div>
            <?php elseif ($bridge_id) : ?>
                <div class="itp-connected-landings is-empty" data-itp-connected-landings><strong>Connected landing pages</strong><span>No landing page connected yet. Save this campaign to create the first page with the selected template.</span></div>
            <?php endif; ?>
            <?php if ($landing_url) : ?>
                <div class="itp-help-card itp-landing-safety-note">
                    <strong>Elementor safety mode</strong>
                    <span>Choose template + slug, then use <strong>+ New landing page</strong> to immediately create another landing page for this same VSL. If you only change the template and click Save, the popup asks before regenerating the current page.</span>
                </div>
                <div class="itp-landing-modal-backdrop" data-itp-landing-template-modal hidden>
                    <div class="itp-landing-modal" role="dialog" aria-modal="true" aria-labelledby="itp-landing-modal-title">
                        <button type="button" class="itp-landing-modal-x" data-itp-landing-cancel aria-label="Close">×</button>
                        <span class="itp-kicker">Template change</span>
                        <h3 id="itp-landing-modal-title">This landing page already exists.</h3>
                        <p>You changed the bonus Elementor template. Choose how Invalid Traffic should apply it when you save this bridge.</p>
                        <div class="itp-landing-modal-options is-regenerate-only">
                            <button type="button" class="itp-landing-modal-option danger" data-itp-landing-action="regenerate">
                                <strong>Regenerate this page</strong>
                                <span>Replace only the currently selected Elementor landing page with this template. This can overwrite edits made in Elementor.</span>
                            </button>
                            <div class="itp-landing-modal-option safe itp-landing-modal-tip">
                                <strong>Need another landing page?</strong>
                                <span>Close this popup and use <b>+ New landing page</b>. That keeps the old page connected and creates a second slug/template immediately.</span>
                            </div>
                        </div>
                        <button type="button" class="itp-btn itp-btn-ghost" data-itp-landing-cancel>Cancel and keep current page</button>
                    </div>
                </div>
            <?php else : ?>
                <div class="itp-help-card">
                    <strong>First save creates the landing page</strong>
                    <span>The selected Elementor template becomes a real WordPress page with this VSL inserted automatically.</span>
                </div>
            <?php endif; ?>
            <div class="itp-help-card">
                <strong>Simple workflow</strong>
                <span>Create the VSL here → choose a template + slug → save or click + New landing page → open the Elementor page and edit the design normally. Tracking stays attached to this VSL.</span>
            </div>
        </section>
        <?php
    }

    private function card_editor_row($idx, $card) {
        $card = array_merge($this->empty_card(), is_array($card) ? $card : []);
        ?>
        <div class="itp-card-row" data-itp-card-row>
            <div class="itp-form-grid two">
                <label>Card title<input name="cards[<?php echo esc_attr($idx); ?>][title]" value="<?php echo esc_attr($card['title']); ?>" placeholder="Ex: Continue to article"></label>
                <label>Card destination URL<input name="cards[<?php echo esc_attr($idx); ?>][url]" value="<?php echo esc_url($card['url']); ?>" placeholder="https://example.com/final-page"></label>
            </div>
            <div class="itp-form-grid two">
                <label>Card short text<input name="cards[<?php echo esc_attr($idx); ?>][text]" value="<?php echo esc_attr($card['text']); ?>" placeholder="Short supporting text."></label>
                <label>Button label
                    <select data-itp-card-preset>
                        <?php foreach ($this->cta_presets() as $label) echo '<option value="' . esc_attr($label) . '" ' . selected($card['button'], $label, false) . '>' . esc_html($label) . '</option>'; ?>
                        <option value="custom" <?php selected(!in_array($card['button'], $this->cta_presets(), true)); ?>>Custom</option>
                    </select>
                    <input class="itp-custom-label-input" name="cards[<?php echo esc_attr($idx); ?>][button]" value="<?php echo esc_attr($card['button']); ?>" placeholder="Write custom label">
                </label>
            </div>
            <div class="itp-form-grid logo-row itp-card-image-row">
                <label>Card image URL optional
                    <input name="cards[<?php echo esc_attr($idx); ?>][image]" value="<?php echo esc_url($card['image']); ?>" placeholder="https://.../image.jpg">
                </label>
                <button type="button" class="itp-btn itp-btn-ghost" data-itp-media="card">Media</button>
                <button type="button" class="itp-btn itp-btn-light" data-itp-clear-card-image>Clear image</button>
            </div>
            <p class="itp-field-help">Recommended: 1200×675 (16:9), JPG/WebP/PNG. The image is cropped to fit the card.</p>
            <div class="itp-card-row-actions"><button type="button" class="itp-btn itp-btn-light" data-itp-remove-card>Remove card</button></div>
        </div>
        <?php
    }


    private function analytics_period() {
        $period = sanitize_key(wp_unslash($_GET['itp_period'] ?? 'all'));
        if (!in_array($period, ['today','yesterday','7d','30d','all','custom'], true)) $period = 'all';
        $tz = wp_timezone();
        $now = new DateTime('now', $tz);
        $start = null;
        $end = null;
        $label = 'All time';
        if ($period === 'today') {
            $start = (clone $now)->setTime(0,0,0);
            $end = (clone $start)->modify('+1 day');
            $label = 'Today';
        } elseif ($period === 'yesterday') {
            $end = (clone $now)->setTime(0,0,0);
            $start = (clone $end)->modify('-1 day');
            $label = 'Yesterday';
        } elseif ($period === '7d') {
            $start = (clone $now)->modify('-6 days')->setTime(0,0,0);
            $end = (clone $now)->modify('+1 day')->setTime(0,0,0);
            $label = 'Last 7 days';
        } elseif ($period === '30d') {
            $start = (clone $now)->modify('-29 days')->setTime(0,0,0);
            $end = (clone $now)->modify('+1 day')->setTime(0,0,0);
            $label = 'Last 30 days';
        } elseif ($period === 'custom') {
            $raw_start = sanitize_text_field(wp_unslash($_GET['itp_start'] ?? ''));
            $raw_end = sanitize_text_field(wp_unslash($_GET['itp_end'] ?? ''));
            if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $raw_start) && preg_match('/^\d{4}-\d{2}-\d{2}$/', $raw_end)) {
                $start = DateTime::createFromFormat('Y-m-d H:i:s', $raw_start . ' 00:00:00', $tz);
                $end = DateTime::createFromFormat('Y-m-d H:i:s', $raw_end . ' 00:00:00', $tz);
                if ($end) $end->modify('+1 day');
                $label = $raw_start . ' → ' . $raw_end;
            }
        }
        if (!$start || !$end || $period === 'all') return ['period' => 'all', 'start' => '', 'end' => '', 'label' => 'All time'];
        $utc = new DateTimeZone('UTC');
        $start->setTimezone($utc);
        $end->setTimezone($utc);
        return ['period' => $period, 'start' => $start->format('Y-m-d H:i:s'), 'end' => $end->format('Y-m-d H:i:s'), 'label' => $label];
    }

    private function analytics_date_sql($has_where = true) {
        global $wpdb;
        $range = $this->analytics_period();
        if (empty($range['start']) || empty($range['end'])) return '';
        return ($has_where ? ' AND ' : ' WHERE ') . $wpdb->prepare('created_at >= %s AND created_at < %s', $range['start'], $range['end']);
    }

    private function analytics_period_bar($base_url) {
        $range = $this->analytics_period();
        $current = $range['period'];
        $links = ['today' => 'Today', 'yesterday' => 'Yesterday', '7d' => '7 days', '30d' => '30 days', 'all' => 'All time'];
        $html = '<section class="itp-card itp-period-card"><div><span class="itp-kicker">Period</span><strong>' . esc_html($range['label']) . '</strong></div><nav>';
        foreach ($links as $key => $label) {
            $url = add_query_arg('itp_period', $key, $base_url);
            $html .= '<a class="' . esc_attr($current === $key ? 'active' : '') . '" href="' . esc_url($url) . '">' . esc_html($label) . '</a>';
        }
        $html .= '</nav><form method="get" class="itp-period-custom"><input type="hidden" name="page" value="' . esc_attr(sanitize_text_field(wp_unslash($_GET['page'] ?? 'itp-analytics'))) . '">';
        if (!empty($_GET['id'])) $html .= '<input type="hidden" name="id" value="' . esc_attr(absint($_GET['id'])) . '">';
        $html .= '<input type="hidden" name="itp_period" value="custom"><input type="date" name="itp_start" value="' . esc_attr(sanitize_text_field(wp_unslash($_GET['itp_start'] ?? ''))) . '"><input type="date" name="itp_end" value="' . esc_attr(sanitize_text_field(wp_unslash($_GET['itp_end'] ?? ''))) . '"><button class="itp-btn itp-btn-ghost" type="submit">Apply</button></form></section>';
        return $html;
    }

    private function analytics_query_suffix() {
        $args = [];
        foreach (['itp_period','itp_start','itp_end'] as $k) {
            if (isset($_GET[$k])) $args[$k] = sanitize_text_field(wp_unslash($_GET[$k]));
        }
        return $args ? '&' . http_build_query($args) : '';
    }

    private function reset_confirmed() {
        $confirm = strtolower(trim(sanitize_text_field(wp_unslash($_GET['itp_reset_confirm'] ?? ''))));
        return in_array($confirm, ['delete', 'remover'], true);
    }

    private function reset_metrics_scope_url($scope, $label = '') {
        $scope = sanitize_key($scope);
        $page = sanitize_key($_GET['page'] ?? 'itp-dashboard');
        $args = [
            'page' => $page ?: 'itp-dashboard',
            'itp_action' => 'reset_metrics_scope',
            'itp_scope' => $scope,
        ];
        foreach (['itp_period','itp_start','itp_end'] as $k) {
            if (isset($_GET[$k])) $args[$k] = sanitize_text_field(wp_unslash($_GET[$k]));
        }
        $url = add_query_arg($args, admin_url('admin.php'));
        return wp_nonce_url($url, 'itp_reset_metrics');
    }

    private function reset_metrics_single_url($id) {
        $page = sanitize_key($_GET['page'] ?? 'itp-analytics');
        $args = [
            'page' => $page ?: 'itp-analytics',
            'id' => absint($id),
            'itp_action' => 'reset_metrics',
        ];
        foreach (['itp_period','itp_start','itp_end'] as $k) {
            if (isset($_GET[$k])) $args[$k] = sanitize_text_field(wp_unslash($_GET[$k]));
        }
        return wp_nonce_url(add_query_arg($args, admin_url('admin.php')), 'itp_action_' . absint($id));
    }

    private function reset_metrics_panel($id = 0, $context = 'dashboard') {
        $id = absint($id);
        $is_single = $id > 0 && get_post_type($id) === self::CPT;
        $buttons = [];
        if ($is_single) {
            $url = $this->reset_metrics_single_url($id);
            $buttons[] = '<a class="itp-reset-chip danger" role="button" data-itp-reset-scope="bridge" data-itp-reset-label="this bridge" data-itp-reset-url="' . esc_url($url) . '" href="#">Reset this bridge</a>';
        } else {
            $scope_labels = [
                'all' => 'All metrics',
                'cards' => 'Cards',
                'cookie' => 'Cookie',
                'vsl_video' => 'VSL / Video',
                'vsl_landing' => 'VSL + Landing',
            ];
            foreach ($scope_labels as $scope => $label) {
                $class = $scope === 'all' ? ' danger' : '';
                $url = $this->reset_metrics_scope_url($scope);
                $buttons[] = '<a class="itp-reset-chip' . esc_attr($class) . '" role="button" data-itp-reset-scope="' . esc_attr($scope) . '" data-itp-reset-label="' . esc_attr($label) . '" data-itp-reset-url="' . esc_url($url) . '" href="#">' . esc_html($label) . '</a>';
            }
        }
        $title = $is_single ? 'Reset metrics for this bridge' : 'Reset metrics by layer';
        $text = $is_single ? 'Remove only the saved events for this bridge. Typed confirmation is required before any data is deleted.' : 'Remove all metrics or only one campaign type. Typed confirmation is required before any data is deleted.';
        return '<section class="itp-reset-panel"><div><span class="itp-kicker">Metrics maintenance</span><h3>' . esc_html($title) . '</h3><p>' . esc_html($text) . '</p></div><div class="itp-reset-actions">' . implode('', $buttons) . '</div></section>';
    }

    private function bridge_ids_for_reset_scope($scope) {
        $scope = sanitize_key($scope);
        $ids = get_posts([
            'post_type' => self::CPT,
            'post_status' => 'any',
            'posts_per_page' => -1,
            'fields' => 'ids',
            'no_found_rows' => true,
        ]);
        if (!$ids) return [];
        if ($scope === 'all') return array_map('absint', $ids);
        $out = [];
        foreach ($ids as $id) {
            $meta = $this->bridge_meta($id);
            $tpl = sanitize_key($meta['template'] ?? 'blue_cards');
            if ($scope === 'cookie' && $tpl === 'cookie_modal') $out[] = (int)$id;
            elseif ($scope === 'vsl_video' && $tpl === 'vsl_video') $out[] = (int)$id;
            elseif ($scope === 'vsl_landing' && $tpl === 'vsl_landing') $out[] = (int)$id;
            elseif ($scope === 'cards' && !in_array($tpl, ['cookie_modal', 'vsl_video', 'vsl_landing'], true)) $out[] = (int)$id;
        }
        return $out;
    }

    private function delete_metrics_for_scope($scope) {
        global $wpdb;
        $scope = sanitize_key($scope);
        $table = $wpdb->prefix . self::EVENT_TABLE;
        if ($scope === 'all') {
            return (int)$wpdb->query("DELETE FROM {$table}");
        }
        $ids = $this->bridge_ids_for_reset_scope($scope);
        if (!$ids) return 0;
        $placeholders = implode(',', array_fill(0, count($ids), '%d'));
        return (int)$wpdb->query($wpdb->prepare("DELETE FROM {$table} WHERE presell_id IN ({$placeholders})", $ids));
    }

    private function parse_time_to_seconds($value) {
        $value = trim((string)$value);
        if ($value === '') return 0;
        if (is_numeric($value)) return max(0, min(7200, (int)$value));
        if (preg_match('/^(\d{1,2}):(\d{2})(?::(\d{2}))?$/', $value, $m)) {
            if (isset($m[3]) && $m[3] !== '') return max(0, min(7200, ((int)$m[1] * 3600) + ((int)$m[2] * 60) + (int)$m[3]));
            return max(0, min(7200, ((int)$m[1] * 60) + (int)$m[2]));
        }
        return 0;
    }

    private function format_seconds_mmss($seconds) {
        $seconds = max(0, (int)$seconds);
        return sprintf('%02d:%02d', floor($seconds / 60), $seconds % 60);
    }

    private function countup_attrs($value, $suffix = '', $decimals = 0, $prefix = '') {
        $num = is_numeric($value) ? (float)$value : 0;
        return ' data-itp-count-up data-itp-count-to="' . esc_attr($num) . '" data-itp-count-decimals="' . esc_attr((int)$decimals) . '" data-itp-count-prefix="' . esc_attr($prefix) . '" data-itp-count-suffix="' . esc_attr((string)$suffix) . '"';
    }

    private function vsl_estimate_retention_percent($percent, $r25, $r50, $r75, $r100) {
        $percent = max(0, min(100, (float)$percent));
        $points = [
            0 => 100.0,
            25 => (float)$r25,
            50 => (float)$r50,
            75 => (float)$r75,
            100 => (float)$r100,
        ];
        $last_x = 0;
        $last_y = 100.0;
        foreach ($points as $x => $y) {
            if ($percent <= $x) {
                $span = max(1, $x - $last_x);
                $ratio = ($percent - $last_x) / $span;
                return round($last_y + (($y - $last_y) * $ratio), 2);
            }
            $last_x = $x;
            $last_y = $y;
        }
        return round((float)$r100, 2);
    }

    private function vsl_metric_at_seconds($seconds, $duration, $plays, $r25, $r50, $r75, $r100) {
        $seconds = max(0, (int)$seconds);
        $duration = max(1, (int)$duration);
        $plays = max(0, (int)$plays);
        $pct = min(100, max(0, round(($seconds / $duration) * 100, 2)));
        $retention = $this->vsl_estimate_retention_percent($pct, $r25, $r50, $r75, $r100);
        $audience = $plays ? (int)round(($retention / 100) * $plays) : 0;
        return ['pct' => $pct, 'retention' => $retention, 'audience' => $audience, 'label' => $this->format_seconds_mmss($seconds)];
    }

    private function vsl_analytics_meta($id) {
        $raw = get_post_meta($id, '_itp_vsl_analytics', true);
        if (!is_array($raw)) $raw = [];
        return array_merge(['pitch_seconds' => 0, 'custom_name' => '', 'custom_seconds' => 0, 'revenue' => 'R$ 0,00'], $raw);
    }

    private function handle_analytics_post() {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST' || empty($_POST['itp_analytics_action'])) return;
        if (!current_user_can('manage_options')) return;
        $id = absint($_POST['itp_analytics_id'] ?? 0);
        if (!$id || get_post_type($id) !== self::CPT) return;
        check_admin_referer('itp_save_vsl_analytics_' . $id, 'itp_analytics_nonce');
        $meta = [
            'pitch_seconds' => $this->parse_time_to_seconds(wp_unslash($_POST['itp_pitch_time'] ?? '00:05')),
            'custom_name' => sanitize_text_field(wp_unslash($_POST['itp_custom_metric_name'] ?? '')),
            'custom_seconds' => $this->parse_time_to_seconds(wp_unslash($_POST['itp_custom_metric_time'] ?? '')),
            'revenue' => sanitize_text_field(wp_unslash($_POST['itp_revenue'] ?? 'R$ 0,00')),
        ];
        update_post_meta($id, '_itp_vsl_analytics', $meta);
        wp_safe_redirect(admin_url('admin.php?page=itp-analytics&id=' . $id . '&saved_metrics=1' . $this->analytics_query_suffix()));
        exit;
    }

    public function page_analytics() {
        if (!current_user_can('manage_options')) return;
        $this->handle_analytics_post();
        $this->handle_actions();
        $id = isset($_GET['id']) ? absint($_GET['id']) : 0;
        if ($id && get_post_type($id) === self::CPT) {
            $this->page_single_analytics($id);
            return;
        }
        $this->page_global_analytics();
    }

    private function page_global_analytics() {
        $this->admin_shell_start('Analytics', 'Simple visits, clicks, CTR, traffic quality, source, geo and click-forensics data from your bridge pages.');
        echo $this->analytics_period_bar(admin_url('admin.php?page=itp-analytics'));
        echo $this->reset_metrics_panel(0, 'analytics');
        $rows = $this->analytics_rows();
        $quality = $this->quality_breakdown(0);
        $daily = $this->daily_series(0);
        $stats = $this->global_stats();
        $vsl_global = $this->global_vsl_stats();
        $risk_total = $quality['suspicious'] + $quality['bot_like'] + $quality['repeated'];
        $global_ctr = $stats['visits'] ? round(($stats['clicks'] / max(1, $stats['visits'])) * 100, 1) : 0;
        $source_rows = $this->breakdown_rows('source', 0, 8, "event_type IN ('visit','click')");
        $country_rows = $this->breakdown_rows('country_code', 0, 8, "event_type IN ('visit','click')");
        $device_rows = $this->breakdown_rows('device', 0, 6, "event_type IN ('visit','click')");
        $channel_rows = $this->channel_mix_rows(0, 6);
        ?>
        <section class="itp-card itp-analytics-command-card">
            <div class="itp-card-head"><div><span class="itp-kicker">Filtered command summary</span><h2>Bridge layer vs VSL layer</h2><p>The period filter above applies to the dashboard numbers, tables, charts and exports on this page.</p></div><a class="itp-btn itp-btn-ghost" href="<?php echo esc_url(wp_nonce_url(admin_url('admin.php?page=itp-analytics&itp_action=export_csv' . $this->analytics_query_suffix()), 'itp_export_csv')); ?>">Export filtered CSV</a></div>
            <div class="itp-layer-metric-grid">
                <div class="itp-layer-card bridge"><small>Bridge layer</small><strong<?php echo $this->countup_attrs($stats['visits']); ?>><?php echo esc_html($stats['visits']); ?></strong><span>visits</span><em><?php echo esc_html($stats['clicks']); ?> clicks · <?php echo esc_html($global_ctr); ?>% CTR</em></div>
                <div class="itp-layer-card vsl"><small>VSL layer</small><strong<?php echo $this->countup_attrs($vsl_global['play_rate'], '%', 1); ?>><?php echo esc_html($vsl_global['play_rate']); ?>%</strong><span>play rate</span><em><?php echo esc_html($vsl_global['plays']); ?> plays · <?php echo esc_html($vsl_global['cta_clicks']); ?> CTA clicks</em></div>
                <div class="itp-layer-card quality"><small>Clean signal</small><strong<?php echo $this->countup_attrs($quality['valid'] + $quality['engaged']); ?>><?php echo esc_html($quality['valid'] + $quality['engaged']); ?></strong><span>valid + engaged</span><em><?php echo esc_html($risk_total); ?> sessions in risk review</em></div>
                <div class="itp-layer-card pulse"><small>Live-style pulse</small><b class="itp-layer-spark"><i></i><i></i><i></i><i></i></b><span>sources, geo and devices stay separated</span></div>
            </div>
        </section>

        <section class="itp-card">
            <div class="itp-card-head"><div><h2>All bridge pages</h2><p>Template-aware summary: Cards/Cookie focus on forward clicks; VSL focuses on views, plays and play rate.</p></div><a class="itp-btn itp-btn-ghost" href="<?php echo esc_url(wp_nonce_url(admin_url('admin.php?page=itp-analytics&itp_action=export_csv' . $this->analytics_query_suffix()), 'itp_export_csv')); ?>">Export CSV</a></div>
            <div class="itp-table-wrap"><table class="itp-table"><thead><tr><th>Bridge</th><th>Type</th><th>URL path</th><th>Source</th><th>Country</th><th>Visits</th><th>Clicks</th><th>VSL plays</th><th>Play rate</th><th>Quality</th></tr></thead><tbody>
            <?php foreach ($rows as $row) : ?>
                <tr><td><a href="<?php echo esc_url(admin_url('admin.php?page=itp-analytics&id=' . $row['id'])); ?>"><?php echo esc_html($row['title']); ?></a></td><td><?php echo esc_html($row['type']); ?></td><td><code><?php echo esc_html($row['path']); ?></code></td><td><?php echo esc_html($row['source']); ?></td><td><?php echo esc_html($row['country']); ?></td><td><?php echo esc_html($row['visits']); ?></td><td><?php echo esc_html($row['clicks']); ?></td><td><?php echo esc_html($row['vsl_plays']); ?></td><td><?php echo is_numeric($row['vsl_play_rate']) ? esc_html($row['vsl_play_rate']) . '%' : '—'; ?></td><td><?php echo esc_html($row['quality']); ?></td></tr>
            <?php endforeach; if (empty($rows)) : ?><tr><td colspan="10">No events yet.</td></tr><?php endif; ?>
            </tbody></table></div>
        </section>

        <section class="itp-dashboard-grid">
            <div class="itp-card"><h2>Global visits by day</h2><p>Visits and clicks across all bridge pages.</p><canvas class="itp-chart" data-itp-line='<?php echo esc_attr(wp_json_encode($daily)); ?>'></canvas></div>
            <div class="itp-card"><h2>Global quality mix</h2><p>Session-level quality. Heartbeats are consolidated into the session, not double-counted.</p><canvas class="itp-chart small" data-itp-bar='<?php echo esc_attr(wp_json_encode($quality)); ?>'></canvas></div>
        </section>

        <section class="itp-card itp-country-map-card">
            <div class="itp-card-head"><div><h2>Country traffic map</h2><p>Bridge traffic by country. Uses server/CDN geo when available, otherwise browser timezone/language inference.</p></div></div>
            <?php echo $this->country_map_html($country_rows, 'No country data yet.'); ?>
        </section>

        <section class="itp-insight-grid four">
            <div class="itp-card"><h2>Sources</h2><?php echo wp_kses_post($this->mini_breakdown_html($source_rows, 'No source data yet.')); ?></div>
            <div class="itp-card"><h2>Traffic type</h2><?php echo wp_kses_post($this->mini_breakdown_html($channel_rows, 'No channel data yet.')); ?></div>
            <div class="itp-card"><h2>Countries</h2><?php echo wp_kses_post($this->mini_breakdown_html($country_rows, 'No country data yet.')); ?></div>
            <div class="itp-card"><h2>Devices</h2><?php echo wp_kses_post($this->mini_breakdown_html($device_rows, 'No device data yet.')); ?></div>
        </section>
        <?php
        $this->admin_shell_end();
    }


    private function page_single_analytics($id) {
        $stats = $this->stats_for_presell($id);
        $quality = $this->quality_breakdown($id);
        $daily = $this->daily_series($id);
        $events = $this->recent_events($id, 100);
        $validate = $this->validate_bridge($id);
        $ctr = $stats['visits'] ? round($stats['clicks'] / $stats['visits'] * 100, 1) : 0;
        $source_rows = $this->breakdown_rows('source', $id, 6, "event_type IN ('visit','click')");
        $country_rows = $this->breakdown_rows('country_code', $id, 6, "event_type IN ('visit','click')");
        $device_rows = $this->breakdown_rows('device', $id, 5, "event_type IN ('visit','click')");
        $risk_rows = $this->risk_rows($id, 6);
        $channel_rows = $this->channel_mix_rows($id, 6);
        $this->admin_shell_start('Analytics', 'Template-aware campaign analytics for bridge pages, VSL players and traffic-quality signals.');
        $meta = $this->bridge_meta($id);
        $template = $meta['template'] ?? 'cards';
        $is_vsl = in_array($template, ['vsl_video','vsl_landing'], true);
        $campaign_type = $this->campaign_type_label($template);
        $landing_pages = ($template === 'vsl_landing') ? $this->landing_pages_for_bridge($id) : [];
        $current_landing = null;
        foreach ($landing_pages as $lp_tmp) {
            if (!empty($lp_tmp['current'])) { $current_landing = $lp_tmp; break; }
        }
        if (!$current_landing && !empty($landing_pages)) {
            $current_landing = end($landing_pages);
        }
        $landing_page_id = (int)($current_landing['id'] ?? ($meta['elementor_landing_page_id'] ?? get_post_meta($id, '_itp_elementor_landing_page_id', true)));
        $landing_url = ($template === 'vsl_landing' && $current_landing && !empty($current_landing['url'])) ? $current_landing['url'] : (($template === 'vsl_landing' && $landing_page_id && get_post_status($landing_page_id)) ? get_permalink($landing_page_id) : '');
        $campaign_url = ($template === 'vsl_landing' && $landing_url) ? $landing_url : get_permalink($id);
        $campaign_path = wp_parse_url($campaign_url, PHP_URL_PATH) ?: $this->bridge_path($id);
        $internal_player_url = get_permalink($id);
        $internal_player_path = wp_parse_url($internal_player_url, PHP_URL_PATH) ?: $this->bridge_path($id);
        $copy_url = $campaign_url;
        $lead_stats_single = $is_vsl ? $this->lead_summary($id) : null;
        ?>
        <section class="itp-card itp-readiness">
            <div class="itp-score-circle <?php echo esc_attr($validate['class']); ?>"><strong><?php echo esc_html($validate['score']); ?>%</strong><span>/100</span></div>
            <div class="itp-readiness-body">
                <h2>Campaign Readiness Check: <?php echo esc_html($validate['title']); ?></h2>
                <p>This is a setup checklist, not a Google Ads, Meta, AdSense or affiliate-network approval guarantee.</p>
                <div class="itp-check-grid">
                    <?php foreach ($validate['checks'] as $check) : ?>
                        <div class="<?php echo $check['ok'] ? 'ok' : 'warn'; ?>"><strong><?php echo esc_html($check['label']); ?></strong><span><?php echo esc_html($check['text']); ?></span></div>
                    <?php endforeach; ?>
                </div>
                <?php if (!empty($validate['fixes'])) : ?>
                    <div class="itp-fixes"><strong>Recommended fixes</strong><?php foreach ($validate['fixes'] as $fix) echo '<p>' . esc_html($fix) . '</p>'; ?></div>
                <?php else : ?>
                    <p class="itp-ok-text">No obvious setup problems detected.</p>
                <?php endif; ?>
                <div class="itp-row-actions"><a class="itp-btn" href="<?php echo esc_url($campaign_url); ?>" target="_blank"><?php echo $landing_url ? 'Open landing page' : 'Open bridge'; ?></a><a class="itp-btn itp-btn-ghost" href="<?php echo esc_url(admin_url('admin.php?page=itp-create&id=' . $id)); ?>">Edit bridge</a></div>
            </div>
        </section>

        <section class="itp-card itp-page-url-card itp-campaign-url-card">
            <div class="itp-url-main">
                <span><?php echo $template === 'vsl_landing' ? 'Analyzing current landing page' : 'Analyzing campaign URL'; ?></span>
                <strong>Bridge: <?php echo esc_html(get_the_title($id) ?: ('Untitled campaign #' . (int)$id)); ?></strong>
                <code><?php echo esc_html($template === 'vsl_landing' ? ('Current landing: ' . $campaign_path) : $campaign_path); ?></code>
                <em class="itp-template-pill"><?php echo esc_html($campaign_type); ?></em>
                <a class="itp-primary-url" href="<?php echo esc_url($campaign_url); ?>" target="_blank" rel="noopener"><?php echo esc_html($campaign_url); ?></a>
                <?php if ($template === 'vsl_landing') : ?>
                    <div class="itp-url-submeta">
                        <span>Internal player only: <code><?php echo esc_html($internal_player_path); ?></code></span>
                        <?php if (!empty($landing_pages)) : ?>
                            <span>Connected landings:
                                <?php foreach ($landing_pages as $lp) : ?>
                                    <a class="<?php echo !empty($lp['current']) ? 'is-current' : ''; ?>" href="<?php echo esc_url($lp['url']); ?>" target="_blank" rel="noopener"><?php echo esc_html($lp['path']); ?><?php if (!empty($lp['current'])) : ?> <b>current</b><?php endif; ?></a>
                                <?php endforeach; ?>
                            </span>
                        <?php endif; ?>
                    </div>
                <?php endif; ?>
            </div>
            <div class="itp-url-actions">
                <button type="button" class="itp-btn itp-btn-ghost" data-itp-copy-url="<?php echo esc_attr($copy_url); ?>">Copy campaign URL</button>
                <a class="itp-btn itp-btn-ghost" href="<?php echo esc_url(admin_url('admin.php?page=itp-reports&id=' . $id)); ?>">Evidence Report</a>
                <button class="itp-btn" data-itp-validate-inline="<?php echo esc_attr($id); ?>">Validate</button>
                <a class="itp-btn itp-btn-ghost" href="<?php echo esc_url(wp_nonce_url(admin_url('admin.php?page=itp-analytics&id=' . $id . '&itp_action=export_csv' . $this->analytics_query_suffix()), 'itp_export_csv')); ?>">Export CSV</a>
            </div>
        </section>
        <?php echo $this->analytics_period_bar(admin_url('admin.php?page=itp-analytics&id=' . $id . $this->analytics_query_suffix())); ?>
        <?php echo $this->reset_metrics_panel($id, 'single'); ?>

        <?php if (!$is_vsl) : $conv = $this->conversion_performance_stats($id); ?>
        <section class="itp-card itp-conversion-overview-card">
            <div class="itp-card-head">
                <div><span class="itp-kicker">Conversion attribution</span><h2>Campaign conversions</h2><p>Server/webhook, URL-match and manual helper conversions credited to this Cards/Cookie bridge.</p></div>
                <a class="itp-btn itp-btn-ghost" href="<?php echo esc_url(admin_url('admin.php?page=itp-pixels')); ?>">Open Pixels</a>
            </div>
            <section class="itp-stat-grid four">
                <div class="itp-stat"><span>Conversions</span><strong<?php echo $this->countup_attrs($conv['conversions']); ?>><?php echo esc_html($conv['conversions']); ?></strong><em>tracked purchases/actions</em></div>
                <div class="itp-stat"><span>Revenue</span><strong><?php echo esc_html($conv['revenue']); ?></strong><em>parsed from value/currency</em></div>
                <div class="itp-stat"><span>Visit CVR</span><strong><?php echo esc_html($conv['visit_rate']); ?>%</strong><em>conversion ÷ visits</em></div>
                <div class="itp-stat"><span>Click CVR</span><strong><?php echo esc_html($conv['click_rate']); ?>%</strong><em>conversion ÷ clicks</em></div>
            </section>
            <div class="itp-insight-grid two">
                <div class="itp-card itp-nested-card"><h2>Conversion sources</h2><?php echo wp_kses_post($this->mini_breakdown_html($conv['source_rows'], 'No conversion source yet.')); ?></div>
                <div class="itp-card itp-nested-card"><h2>Recent conversion events</h2><?php echo wp_kses_post($this->mini_breakdown_html($conv['event_rows'], 'No conversion events yet.')); ?></div>
            </div>
        </section>
        <?php endif; ?>

        <?php if ($is_vsl) : $vsl = $this->vsl_performance_stats($id); $vsl_admin = $this->vsl_analytics_meta($id); ?>
        <section class="itp-card itp-vsl-command-card">
            <div class="itp-card-head">
                <div><span class="itp-kicker">Video performance</span><h2>VSL overview</h2><p>Clean video KPIs, granular retention, pitch marker and VSL-specific metrics separated from Cards/Cookie traffic.</p></div>
                <div class="itp-row-actions"><span class="itp-live-data-pill <?php echo !empty($vsl['has_granular_progress']) ? 'is-live' : 'is-fallback'; ?>"><?php echo !empty($vsl['has_granular_progress']) ? 'Real progress events' : 'Quartile estimate'; ?></span><a class="itp-btn itp-btn-ghost" href="<?php echo esc_url(admin_url('admin.php?page=itp-create&id=' . $id . '&itp_tab=embed')); ?>">Open embed</a><a class="itp-btn itp-btn-ghost" href="#" role="button" data-itp-reset-scope="bridge" data-itp-reset-label="this bridge" data-itp-reset-url="<?php echo esc_url(wp_nonce_url(admin_url('admin.php?page=itp-analytics&id=' . $id . '&itp_action=reset_metrics' . $this->analytics_query_suffix()), 'itp_action_' . $id)); ?>">Reset metrics</a></div>
            </div>

            <div class="itp-vsl-retention-board">
                <div class="itp-vsl-retention-stage itp-vsl-retention-stage-live" data-itp-vsl-retention-stage data-duration="<?php echo esc_attr((int)$vsl['retention_duration']); ?>" data-points='<?php echo esc_attr($vsl['retention_points_json']); ?>' style="--itp-pitch-x:<?php echo esc_attr($vsl['pitch_pct']); ?>%;<?php if (empty($vsl['retention_video_url'])) echo 'background-image:linear-gradient(rgba(5,64,15,.78),rgba(5,64,15,.78)),url(' . esc_url($vsl['retention_bg']) . ');'; ?>">
                    <?php if (!empty($vsl['retention_video_url'])) : ?>
                        <video class="itp-vsl-retention-video" src="<?php echo esc_url($vsl['retention_video_url']); ?>" poster="<?php echo esc_url($vsl['retention_bg']); ?>" muted playsinline preload="auto" data-itp-retention-video></video>
                    <?php endif; ?>
                    <div class="itp-vsl-retention-tint"></div>
                    <div class="itp-vsl-retention-status"><b><?php echo !empty($vsl['has_granular_progress']) ? 'Live retention curve' : 'Estimated retention curve'; ?></b><span><?php echo !empty($vsl['has_granular_progress']) ? 'Built from progress events' : 'Built from 25/50/75/100 milestones'; ?></span></div>
                    <div class="itp-vsl-retention-grid"><span>100%</span><span>80%</span><span>60%</span><span>40%</span><span>20%</span><span>0%</span></div>
                    <svg class="itp-vsl-retention-svg" viewBox="0 0 100 100" preserveAspectRatio="none" aria-hidden="true"><path class="shade" d="<?php echo esc_attr($vsl['curve_shade_d']); ?>"></path><path class="line" d="<?php echo esc_attr($vsl['curve_line_d']); ?>"></path></svg>
                    <?php if (!empty($vsl['pitch_enabled'])) : ?><div class="itp-vsl-pitch-marker"><i></i><b>Pitch <?php echo esc_html($vsl['pitch_label']); ?></b></div><?php endif; ?>
                    <div class="itp-vsl-retention-caption" data-itp-retention-caption><strong><?php echo !empty($vsl['pitch_enabled']) ? esc_html($vsl['pitch_retention']) . '%' : 'Hover'; ?></strong><span><?php echo !empty($vsl['pitch_enabled']) ? 'Retention at pitch' : 'Move over the chart'; ?></span><em><?php echo !empty($vsl['pitch_enabled']) ? esc_html($vsl['pitch_audience']) . ' audience at pitch' : 'frame + retention preview'; ?></em></div>
                </div>
                <form method="post" class="itp-vsl-pitch-form">
                    <?php wp_nonce_field('itp_save_vsl_analytics_' . $id, 'itp_analytics_nonce'); ?>
                    <input type="hidden" name="itp_analytics_action" value="save_vsl_settings">
                    <input type="hidden" name="itp_analytics_id" value="<?php echo esc_attr($id); ?>">
                    <label>Pitch marker optional<input name="itp_pitch_time" value="<?php echo (int)$vsl_admin['pitch_seconds'] > 0 ? esc_attr($this->format_seconds_mmss($vsl_admin['pitch_seconds'])) : ''; ?>" placeholder="00:00 = off"></label>
                    <label>Custom metric name<input name="itp_custom_metric_name" value="<?php echo esc_attr($vsl_admin['custom_name']); ?>" placeholder="Ex: First offer"></label>
                    <label>Custom time<input name="itp_custom_metric_time" value="<?php echo esc_attr($this->format_seconds_mmss($vsl_admin['custom_seconds'])); ?>" placeholder="00:30"><small>Estimated from the retention curve.</small></label>
                    <label>Revenue<input name="itp_revenue" value="<?php echo esc_attr($vsl_admin['revenue']); ?>" placeholder="R$ 0,00"></label>
                    <button class="itp-btn" type="submit">Save metrics</button>
                </form>
            </div>

            <div class="itp-vsl-kpi-grid">
                <div class="itp-vsl-kpi"><span>Views</span><strong><?php echo esc_html($vsl['vsl_view']); ?></strong><em>video loaded</em></div>
                <div class="itp-vsl-kpi"><span>Unique views</span><strong><?php echo esc_html($vsl['unique_views']); ?></strong><em>unique visitors</em></div>
                <div class="itp-vsl-kpi"><span>Plays</span><strong><?php echo esc_html($vsl['vsl_play']); ?></strong><em>starts/unmutes</em></div>
                <div class="itp-vsl-kpi"><span>Unique plays</span><strong><?php echo esc_html($vsl['unique_plays']); ?></strong><em>unique starters</em></div>
                <div class="itp-vsl-kpi highlight"><span>Play rate</span><strong><?php echo esc_html($vsl['play_rate']); ?>%</strong><em>unique plays ÷ views</em></div>
                <div class="itp-vsl-kpi highlight"><span>Engagement</span><strong><?php echo esc_html($vsl['engagement_rate']); ?>%</strong><em>quartile depth</em></div>
                <?php if (!empty($vsl['pitch_enabled'])) : ?>
                <div class="itp-vsl-kpi highlight"><span>Retention at pitch</span><strong><?php echo esc_html($vsl['pitch_retention']); ?>%</strong><em><?php echo esc_html($vsl['pitch_label']); ?></em></div>
                <div class="itp-vsl-kpi"><span>Audience at pitch</span><strong><?php echo esc_html($vsl['pitch_audience']); ?></strong><em>estimated viewers at marker</em></div>
                <?php endif; ?>
                <div class="itp-vsl-kpi"><span>Button clicks</span><strong><?php echo esc_html($vsl['button_clicks']); ?></strong><em>VSL CTA clicks</em></div>
                <div class="itp-vsl-kpi"><span>Conversions</span><strong><?php echo esc_html($vsl['conversion']); ?></strong><em>tracked conversion</em></div>
                <div class="itp-vsl-kpi"><span>Conversion rate</span><strong><?php echo esc_html($vsl['conversion_rate']); ?>%</strong><em>conversion ÷ plays</em></div>
                <div class="itp-vsl-kpi"><span>Revenue</span><strong><?php echo esc_html($vsl['revenue']); ?></strong><em>tracked value / manual fallback</em></div>
                <?php if ($vsl['custom_metric_name']) : ?><div class="itp-vsl-kpi"><span><?php echo esc_html($vsl['custom_metric_name']); ?></span><strong><?php echo esc_html($vsl['custom_metric_value']); ?>%</strong><em><?php echo esc_html($vsl['custom_metric_label']); ?> · <?php echo esc_html($vsl['custom_metric_audience'] ?? 0); ?> estimated</em></div><?php endif; ?>
            </div>
            <div class="itp-vsl-retention-mini" aria-label="VSL retention milestones">
                <span style="--w:<?php echo esc_attr($vsl['r25']); ?>%"><b>25%</b><i></i><em><?php echo esc_html($vsl['vsl_25']); ?></em></span>
                <span style="--w:<?php echo esc_attr($vsl['r50']); ?>%"><b>50%</b><i></i><em><?php echo esc_html($vsl['vsl_50']); ?></em></span>
                <span style="--w:<?php echo esc_attr($vsl['r75']); ?>%"><b>75%</b><i></i><em><?php echo esc_html($vsl['vsl_75']); ?></em></span>
                <span style="--w:<?php echo esc_attr($vsl['r100']); ?>%"><b>100%</b><i></i><em><?php echo esc_html($vsl['vsl_100']); ?></em></span>
            </div>
        </section>
        <?php else : ?>
        <section class="itp-card itp-template-summary-card">
            <div class="itp-card-head"><div><span class="itp-kicker">Bridge performance</span><h2><?php echo esc_html($campaign_type); ?> overview</h2><p>This campaign uses bridge-page metrics: visits, unique visitors, forward clicks, CTR and traffic quality.</p></div></div>
        </section>
        <?php endif; ?>

        <section class="itp-card itp-traffic-summary-card"><div class="itp-card-head"><div><h2>Bridge traffic summary</h2><p>Generic bridge metrics shared by Cards, Cookie Modal, VSL and VSL + Landing Page.</p></div></div><div class="itp-stat-grid five">
            <div class="itp-stat"><span>Visits</span><strong><?php echo esc_html($stats['visits']); ?></strong></div>
            <div class="itp-stat"><span>Unique visits</span><strong><?php echo esc_html($stats['unique_visits']); ?></strong></div>
            <div class="itp-stat"><span>Clicks</span><strong><?php echo esc_html($stats['clicks']); ?></strong></div>
            <div class="itp-stat"><span>CTR</span><strong><?php echo esc_html($ctr); ?>%</strong></div>
            <div class="itp-stat"><span>Avg time</span><strong><?php echo esc_html($stats['avg_time']); ?>s</strong></div>
        </div></section>
        <?php if ($is_vsl) : ?>
        <section class="itp-card itp-lead-analytics-card">
            <div class="itp-card-head"><div><span class="itp-kicker">Lead capture</span><h2>Lead popup summary</h2><p>Email captures from this VSL button and landing page. The same period filter applies here.</p></div><div class="itp-row-actions"><a class="itp-btn itp-btn-ghost" href="<?php echo esc_url(admin_url('admin.php?page=itp-leads&id=' . $id . $this->analytics_query_suffix())); ?>">Open leads</a><a class="itp-btn itp-btn-ghost" href="<?php echo esc_url(wp_nonce_url(admin_url('admin.php?page=itp-leads&id=' . $id . '&itp_action=export_leads_csv' . $this->analytics_query_suffix()), 'itp_export_leads_csv')); ?>">Export leads</a></div></div>
            <div class="itp-stat-grid four">
                <div class="itp-stat"><span>Total leads</span><strong><?php echo esc_html($lead_stats_single['leads'] ?? 0); ?></strong><em>captured emails</em></div>
                <div class="itp-stat"><span>Unique emails</span><strong><?php echo esc_html($lead_stats_single['unique_emails'] ?? 0); ?></strong><em>deduplicated</em></div>
                <div class="itp-stat"><span>Popup views</span><strong><?php echo esc_html($lead_stats_single['views'] ?? 0); ?></strong><em>lead_view events</em></div>
                <div class="itp-stat"><span>Submit rate</span><strong><?php echo esc_html($lead_stats_single['submit_rate'] ?? 0); ?>%</strong><em>leads ÷ popup views</em></div>
            </div>
        </section>
        <details class="itp-card itp-vsl-analytics-card itp-analytics-details">
            <summary><span><strong>VSL event details</strong><em>Raw video events for debugging, pixels and retention checkpoints.</em></span><b>Expand</b></summary>
            <div class="itp-stat-grid five">
                <div class="itp-stat"><span>VSL views</span><strong><?php echo esc_html($vsl['vsl_view']); ?></strong></div>
                <div class="itp-stat"><span>Plays</span><strong><?php echo esc_html($vsl['vsl_play']); ?></strong><em><?php echo esc_html($vsl['play_rate']); ?>% play rate</em></div>
                <div class="itp-stat"><span>50% watched</span><strong><?php echo esc_html($vsl['vsl_50']); ?></strong></div>
                <div class="itp-stat"><span>CTA appeared</span><strong><?php echo esc_html($vsl['vsl_cta_view']); ?></strong></div>
                <div class="itp-stat"><span>CTA clicked</span><strong><?php echo esc_html($vsl['vsl_cta_click']); ?></strong></div>
            </div>
            <div class="itp-stat-grid five">
                <div class="itp-stat"><span>25%</span><strong><?php echo esc_html($vsl['vsl_25']); ?></strong></div>
                <div class="itp-stat"><span>75%</span><strong><?php echo esc_html($vsl['vsl_75']); ?></strong></div>
                <div class="itp-stat"><span>100%</span><strong><?php echo esc_html($vsl['vsl_100']); ?></strong></div>
                <div class="itp-stat"><span>Unmute</span><strong><?php echo esc_html($vsl['vsl_unmute']); ?></strong></div>
                <div class="itp-stat"><span>Pause / Resume</span><strong><?php echo esc_html($vsl['vsl_pause']); ?> / <?php echo esc_html($vsl['vsl_resume']); ?></strong></div>
            </div>
        </details>
        <?php endif; ?>

        <section class="itp-dashboard-grid">
            <div class="itp-card"><h2>Visits by day</h2><p>Filtered by the selected period above. Hover the chart to inspect each day.</p><canvas class="itp-chart" data-itp-line='<?php echo esc_attr(wp_json_encode($daily)); ?>'></canvas></div>
            <div class="itp-card"><h2>Traffic quality</h2><p>Session-level quality based on first-party bridge signals.</p><canvas class="itp-chart small" data-itp-bar='<?php echo esc_attr(wp_json_encode($quality)); ?>'></canvas></div>
        </section>

        <section class="itp-card itp-country-map-card">
            <div class="itp-card-head"><div><h2>Country map for this bridge</h2><p>Visual country signal for this campaign bridge only.</p></div></div>
            <?php echo $this->country_map_html($country_rows, 'No country signal for this bridge yet.'); ?>
        </section>

        <section class="itp-insight-grid four">
            <div class="itp-card"><h2>Sources</h2><?php echo wp_kses_post($this->mini_breakdown_html($source_rows, 'No source data yet.')); ?></div>
            <div class="itp-card"><h2>Traffic type</h2><?php echo wp_kses_post($this->mini_breakdown_html($channel_rows, 'No channel data yet.')); ?></div>
            <div class="itp-card"><h2>Countries / IP</h2><p>Exact country needs CDN/server geo. If missing, v0.10 infers from timezone/language.</p><?php echo wp_kses_post($this->mini_breakdown_html($country_rows, 'Country unknown yet.')); ?></div>
            <div class="itp-card"><h2>Devices</h2><?php echo wp_kses_post($this->mini_breakdown_html($device_rows, 'No device data yet.')); ?></div>
            <div class="itp-card"><h2>Suspicious signals</h2><?php echo wp_kses_post($this->mini_breakdown_html($risk_rows, 'No suspicious signals detected yet.')); ?></div>
        </section>

        <?php $timelines = $this->recent_timeline_sessions($id, 4); ?>
        <details class="itp-card itp-timeline-card itp-analytics-details">
            <summary><span><strong>Click timeline</strong><em>Recent session paths from first page visit to heartbeat and forward click.</em></span><b>Expand</b></summary>
            <?php if (!empty($timelines)) : ?>
                <div class="itp-timeline-list">
                    <?php foreach ($timelines as $session) : ?>
                        <div class="itp-timeline-session">
                            <div class="itp-timeline-session-head"><strong><?php echo esc_html($session['label']); ?></strong><span><?php echo esc_html($session['source']); ?> · <?php echo esc_html($session['country']); ?> · <?php echo esc_html($session['device']); ?></span></div>
                            <div class="itp-timeline-steps">
                                <?php foreach ($session['events'] as $step) : ?>
                                    <span class="<?php echo esc_attr($step['class']); ?>"><b><?php echo esc_html($step['time']); ?></b><?php echo esc_html($step['label']); ?><em><?php echo esc_html($step['reason']); ?></em></span>
                                <?php endforeach; ?>
                            </div>
                        </div>
                    <?php endforeach; ?>
                </div>
            <?php else : ?>
                <div class="itp-empty-mini">No session timeline yet. Open the bridge, wait a few seconds and click forward to populate this view.</div>
            <?php endif; ?>
        </details>

        <details class="itp-card itp-analytics-details">
            <summary><span><strong>Click forensics</strong><em>Recent visits, heartbeats and clicks. IP is masked/hashed by default.</em></span><b>Expand</b></summary>
            <div class="itp-table-wrap"><table class="itp-table"><thead><tr><th>Time</th><th>Event</th><th>Quality</th><th>Reason</th><th>Source</th><th>Campaign</th><th>Geo/IP</th><th>Device</th><th>Scroll</th><th>Time</th><th>Destination</th></tr></thead><tbody>
            <?php foreach ($events as $ev) : ?>
                <tr>
                    <td><?php echo esc_html($ev['created_at']); ?></td>
                    <td><?php echo esc_html($ev['event_type']); ?></td>
                    <td><span class="itp-quality-pill <?php echo esc_attr($ev['quality']); ?>"><?php echo esc_html($this->quality_label($ev['quality'])); ?></span></td>
                    <td><?php echo esc_html($ev['reason']); ?></td>
                    <td><?php echo esc_html($this->clean_source_label($ev['source'] ?: 'direct/internal')); ?></td>
                    <td><?php echo esc_html($ev['campaign'] ?: '—'); ?></td>
                    <td><?php echo esc_html(trim(($ev['country_code'] ?: 'Unknown') . ' ' . ($ev['ip_mask'] ?: ''))); ?></td>
                    <td><?php echo esc_html($ev['device'] ?: 'unknown'); ?></td>
                    <td><?php echo esc_html((int)$ev['scroll_depth']); ?>%</td>
                    <td><?php echo esc_html((int)$ev['time_on_page']); ?>s</td>
                    <td><?php echo !empty($ev['destination']) ? '<a href="' . esc_url($ev['destination']) . '" target="_blank">Open</a>' : '—'; ?></td>
                </tr>
            <?php endforeach; if (empty($events)) : ?><tr><td colspan="11">No events yet.</td></tr><?php endif; ?>
            </tbody></table></div>
        </details>

        <section class="itp-card"><h2>PageSpeed / setup readiness</h2><p>Local quick check. No external API needed.</p><div class="itp-check-grid">
            <div class="ok"><strong>No-ad mode</strong><span>Ready</span></div>
            <div class="ok"><strong>HTTPS URL</strong><span><?php echo is_ssl() ? 'Ready' : 'Review'; ?></span></div>
            <div class="<?php echo $validate['asset_ok'] ? 'ok' : 'warn'; ?>"><strong>At least one image or logo</strong><span><?php echo $validate['asset_ok'] ? 'Ready' : 'Add visual asset'; ?></span></div>
            <div class="<?php echo $validate['dest_ok'] ? 'ok' : 'warn'; ?>"><strong>Destination links added</strong><span><?php echo $validate['dest_ok'] ? 'Ready' : 'Add destination URL'; ?></span></div>
            <div class="<?php echo $validate['policy_ok'] ? 'ok' : 'warn'; ?>"><strong>Policy links available</strong><span><?php echo $validate['policy_ok'] ? 'Ready' : 'Connect pages'; ?></span></div>
        </div></section>
        <div class="itp-modal-host" data-itp-modal-host></div>
        <?php
        $this->admin_shell_end();
    }



    public function page_leads() {
        if (!current_user_can('manage_options')) return;
        $this->handle_actions();
        $id = isset($_GET['id']) ? absint($_GET['id']) : 0;
        if ($id && get_post_type($id) !== self::CPT) $id = 0;
        $lead_stats = $this->lead_summary($id);
        $rows = $this->recent_leads($id, 300);
        $bridges = get_posts(['post_type' => self::CPT, 'post_status' => ['publish','draft'], 'numberposts' => 300, 'orderby' => 'title', 'order' => 'ASC']);
        $base = admin_url('admin.php?page=itp-leads' . ($id ? '&id=' . $id : ''));
        $export_url = wp_nonce_url(admin_url('admin.php?page=itp-leads&itp_action=export_leads_csv' . ($id ? '&id=' . $id : '') . $this->analytics_query_suffix()), 'itp_export_leads_csv');
        $this->admin_shell_start('Leads', 'Email captures collected by VSL lead popups and landing pages.');
        echo $this->analytics_period_bar($base);
        ?>
        <section class="itp-card itp-leads-command">
            <div class="itp-card-head">
                <div><span class="itp-kicker">Lead capture center</span><h2>Captured leads from your VSL buttons.</h2><p>Filter by period or bridge, export all emails, and keep lead capture separated from normal click analytics.</p></div>
                <a class="itp-btn" href="<?php echo esc_url($export_url); ?>">Export leads CSV</a>
            </div>
            <form method="get" class="itp-lead-filterbar">
                <input type="hidden" name="page" value="itp-leads">
                <?php foreach (['itp_period','itp_start','itp_end'] as $k) { if (isset($_GET[$k])) echo '<input type="hidden" name="' . esc_attr($k) . '" value="' . esc_attr(sanitize_text_field(wp_unslash($_GET[$k]))) . '">'; } ?>
                <label>Bridge
                    <select name="id">
                        <option value="0">All bridges</option>
                        <?php foreach ($bridges as $b) : ?><option value="<?php echo esc_attr((int)$b->ID); ?>" <?php selected($id, (int)$b->ID); ?>><?php echo esc_html($b->post_title ?: ('Bridge #' . (int)$b->ID)); ?></option><?php endforeach; ?>
                    </select>
                </label>
                <button class="itp-btn itp-btn-ghost" type="submit">Apply filter</button>
            </form>
        </section>
        <section class="itp-stat-grid four">
            <div class="itp-stat"><span>Total leads</span><strong<?php echo $this->countup_attrs($lead_stats['leads']); ?>><?php echo esc_html($lead_stats['leads']); ?></strong></div>
            <div class="itp-stat"><span>Unique emails</span><strong<?php echo $this->countup_attrs($lead_stats['unique_emails']); ?>><?php echo esc_html($lead_stats['unique_emails']); ?></strong></div>
            <div class="itp-stat"><span>Lead popup views</span><strong<?php echo $this->countup_attrs($lead_stats['views']); ?>><?php echo esc_html($lead_stats['views']); ?></strong></div>
            <div class="itp-stat"><span>Submit rate</span><strong<?php echo $this->countup_attrs($lead_stats['submit_rate'], '%', 1); ?>><?php echo esc_html($lead_stats['submit_rate']); ?>%</strong><em>leads ÷ popup views</em></div>
        </section>
        <section class="itp-card">
            <div class="itp-card-head"><div><h2>Lead database</h2><p>Stored locally in WordPress. IP is not stored in the lead table; traffic details remain in the event log.</p></div><a class="itp-btn itp-btn-ghost" href="<?php echo esc_url($export_url); ?>">Export CSV</a></div>
            <div class="itp-table-wrap"><table class="itp-table itp-leads-table"><thead><tr><th>Date</th><th>Email</th><th>Name</th><th>Phone</th><th>Bridge</th><th>Landing URL</th><th>Source</th><th>Destination</th></tr></thead><tbody>
            <?php foreach ($rows as $lead) : ?>
                <tr>
                    <td><?php echo esc_html($lead['created_at']); ?></td>
                    <td><strong><?php echo esc_html($lead['email']); ?></strong></td>
                    <td><?php echo esc_html($lead['name'] ?: '—'); ?></td>
                    <td><?php echo esc_html($lead['phone'] ?: '—'); ?></td>
                    <td><?php echo !empty($lead['bridge_title']) ? '<a href="' . esc_url(admin_url('admin.php?page=itp-analytics&id=' . (int)$lead['presell_id'])) . '">' . esc_html($lead['bridge_title']) . '</a>' : esc_html('#' . (int)$lead['presell_id']); ?></td>
                    <td><?php echo !empty($lead['landing_url']) ? '<a href="' . esc_url($lead['landing_url']) . '" target="_blank" rel="noopener">Open</a>' : '—'; ?></td>
                    <td><?php echo esc_html($this->clean_source_label($lead['source'] ?: 'direct/internal')); ?></td>
                    <td><?php echo !empty($lead['destination']) ? '<a href="' . esc_url($lead['destination']) . '" target="_blank" rel="noopener">Open</a>' : '—'; ?></td>
                </tr>
            <?php endforeach; if (empty($rows)) : ?><tr><td colspan="8">No leads captured in this period yet.</td></tr><?php endif; ?>
            </tbody></table></div>
        </section>
        <?php
        $this->admin_shell_end();
    }



    public function page_evidence_report() {
        if (!current_user_can('manage_options')) return;
        $this->handle_actions();
        $id = isset($_GET['id']) ? absint($_GET['id']) : 0;
        if ($id && get_post_type($id) !== self::CPT) {
            $id = 0;
        }
        $title = $id ? get_the_title($id) : 'All bridge pages';
        $stats = $id ? $this->stats_for_presell($id) : $this->global_stats();
        $quality = $this->quality_breakdown($id);
        $source_rows = $this->breakdown_rows('source', $id, 8, "event_type IN ('visit','click')");
        $country_rows = $this->breakdown_rows('country_code', $id, 8, "event_type IN ('visit','click')");
        $device_rows = $this->breakdown_rows('device', $id, 6, "event_type IN ('visit','click')");
        $risk_rows = $this->risk_rows($id, 8);
        $channel_rows = $this->channel_mix_rows($id, 8);
        $validate = $id ? $this->validate_bridge($id) : null;
        $bridge_url = $id ? get_permalink($id) : home_url('/');
        $generated = current_time('mysql');
        $this->admin_shell_start('Evidence Report', 'Printable traffic quality summary for support, internal review or paid-traffic audits.');
        $posts = get_posts(['post_type' => self::CPT, 'post_status' => ['publish','draft'], 'numberposts' => 200]);
        ?>
        <section class="itp-card itp-report-picker no-print">
            <div class="itp-card-head"><div><h2>Choose report scope</h2><p>Select one bridge or keep the report global. This is not an ad-platform approval guarantee.</p></div><button type="button" class="itp-btn" onclick="window.print()">Print / Save PDF</button></div>
            <div class="itp-row-actions">
                <a class="itp-btn <?php echo $id ? 'itp-btn-ghost' : ''; ?>" href="<?php echo esc_url(admin_url('admin.php?page=itp-reports')); ?>">All bridges</a>
                <?php foreach ($posts as $p) : ?><a class="itp-btn <?php echo $id === (int)$p->ID ? '' : 'itp-btn-ghost'; ?>" href="<?php echo esc_url(admin_url('admin.php?page=itp-reports&id=' . (int)$p->ID)); ?>"><?php echo esc_html(wp_trim_words($p->post_title, 4, '…')); ?></a><?php endforeach; ?>
            </div>
        </section>

        <section class="itp-report-document">
            <div class="itp-report-head">
                <span class="itp-kicker">Traffic Quality Evidence</span>
                <h2><?php echo esc_html($title); ?></h2>
                <p>Generated <?php echo esc_html($generated); ?>. This report summarizes first-party bridge data only. It helps document traffic quality signals, but does not guarantee approval or enforcement decisions by Google, Meta, Taboola, Outbrain, AdSense or affiliate networks.</p>
            </div>

            <div class="itp-report-meta">
                <div><strong>Bridge URL</strong><span><?php echo esc_html($bridge_url); ?></span></div>
                <div><strong>No-ad bridge mode</strong><span>Enabled on plugin bridge pages</span></div>
                <div><strong>Destination pages</strong><span>Not modified by this plugin</span></div>
                <div><strong>Ad account connection</strong><span>Not required</span></div>
            </div>

            <div class="itp-stat-grid five">
                <div class="itp-stat"><span>Visits</span><strong><?php echo esc_html($stats['visits'] ?? 0); ?></strong></div>
                <div class="itp-stat"><span>Forward clicks</span><strong><?php echo esc_html($stats['clicks'] ?? 0); ?></strong></div>
                <div class="itp-stat"><span>Forward rate</span><strong><?php $vis = (int)($stats['visits'] ?? 0); $clk = (int)($stats['clicks'] ?? 0); echo esc_html($vis ? round($clk / $vis * 100, 1) : 0); ?>%</strong></div>
                <div class="itp-stat"><span>Risk sessions</span><strong><?php echo esc_html((int)$quality['suspicious'] + (int)$quality['bot_like'] + (int)$quality['repeated']); ?></strong></div>
                <div class="itp-stat"><span>Engaged sessions</span><strong><?php echo esc_html((int)$quality['engaged']); ?></strong></div>
            </div>

            <?php if ($validate) : ?>
            <section class="itp-card itp-report-section">
                <h2>Campaign Preflight Checklist</h2>
                <div class="itp-check-grid"><?php foreach ($validate['checks'] as $check) echo '<div class="' . ($check['ok'] ? 'ok' : 'warn') . '"><strong>' . esc_html($check['label']) . '</strong><span>' . esc_html($check['text']) . '</span></div>'; ?></div>
            </section>
            <?php endif; ?>

            <section class="itp-report-grid">
                <div class="itp-card"><h2>Traffic sources</h2><?php echo wp_kses_post($this->mini_breakdown_html($source_rows, 'No source data.')); ?></div>
                <div class="itp-card"><h2>Traffic type mix</h2><?php echo wp_kses_post($this->mini_breakdown_html($channel_rows, 'No channel data.')); ?></div>
                <div class="itp-card"><h2>Countries / IP signal</h2><?php echo wp_kses_post($this->mini_breakdown_html($country_rows, 'No country signal.')); ?></div>
                <div class="itp-card"><h2>Devices</h2><?php echo wp_kses_post($this->mini_breakdown_html($device_rows, 'No device data.')); ?></div>
            </section>

            <section class="itp-card itp-report-section">
                <h2>Risk reasons</h2>
                <?php echo wp_kses_post($this->mini_breakdown_html($risk_rows, 'No risk reasons detected.')); ?>
            </section>

            <?php if ($id) : $events = $this->recent_events($id, 20); ?>
            <section class="itp-card itp-report-section"><h2>Recent click forensics</h2><div class="itp-table-wrap"><table class="itp-table"><thead><tr><th>Time</th><th>Event</th><th>Quality</th><th>Reason</th><th>Source</th><th>Geo/IP</th><th>Device</th><th>Time</th></tr></thead><tbody>
                <?php foreach ($events as $ev) : ?><tr><td><?php echo esc_html($ev['created_at']); ?></td><td><?php echo esc_html($ev['event_type']); ?></td><td><?php echo esc_html($this->quality_label($ev['quality'])); ?></td><td><?php echo esc_html($ev['reason']); ?></td><td><?php echo esc_html($this->clean_source_label($ev['source'] ?: 'direct/internal')); ?></td><td><?php echo esc_html(trim(($ev['country_code'] ?: 'Unknown') . ' ' . ($ev['ip_mask'] ?: ''))); ?></td><td><?php echo esc_html($ev['device'] ?: 'unknown'); ?></td><td><?php echo esc_html((int)$ev['time_on_page']); ?>s</td></tr><?php endforeach; if (empty($events)) : ?><tr><td colspan="8">No events yet.</td></tr><?php endif; ?>
            </tbody></table></div></section>
            <?php endif; ?>
        </section>
        <?php
        $this->admin_shell_end();
    }


    public function page_pixels() {
        if (!current_user_can('manage_options')) return;
        $this->save_global_pixel_settings_from_post();
        $settings = $this->global_pixel_settings();
        $pixels = $this->normalized_custom_pixels($settings);
        $enabled_custom_pixels = 0;
        foreach ($pixels as $pixel) {
            if (($pixel['enabled'] ?? '0') === '1' && trim((string)($pixel['code'] ?? '')) !== '') $enabled_custom_pixels++;
        }
        $head_filled = trim((string)($settings['global_head'] ?? '')) !== '';
        $footer_filled = trim((string)($settings['global_footer'] ?? '')) !== '';
        $active_sources = ($head_filled ? 1 : 0) + ($footer_filled ? 1 : 0) + $enabled_custom_pixels;
        $webhook_url = rest_url('invalid-traffic/v1/conversion');
        $webhook_secret = (string)($settings['webhook_secret'] ?? '');
        $receiver_url = add_query_arg(['token' => $webhook_secret], $webhook_url);
        $thankyou_pixel_url = add_query_arg(['token' => (string)($settings['public_token'] ?? '')], rest_url('invalid-traffic/v1/pixel.js'));
        $thankyou_pixel_tag = '<script async src="' . esc_url($thankyou_pixel_url) . '"></script>';
        $webhook_history = $this->pixel_webhook_history_rows($settings);
        $example_thankyou_url = home_url('/thank-you/?event=purchase&transaction_id=TXN-123&value=29.90&currency=USD&source=checkout');
        $example_postback_url = add_query_arg([
            'itp_bridge' => '123',
            'event' => 'purchase',
            'value' => '29.90',
            'currency' => 'USD',
            'transaction_id' => 'TXN-123',
            'source' => 'checkout',
        ], $receiver_url);
        $bridge_choices = get_posts([
            'post_type' => self::CPT,
            'post_status' => ['publish', 'draft', 'private'],
            'numberposts' => 100,
            'orderby' => 'date',
            'order' => 'DESC',
        ]);
        $external_cfg = [
            'ajax' => admin_url('admin-ajax.php'),
            'publicToken' => (string)($settings['public_token'] ?? ''),
            'rules' => [],
            'autoUrl' => false,
            'home' => home_url('/'),
        ];
        $external_snippet = <<<'ITPHELPER'
<script>
(function () {
  if (window.__ITP_PURCHASE_HELPER_SENT__) return;
  window.__ITP_PURCHASE_HELPER_SENT__ = true;

  function sendInvalidTrafficPurchase() {
    var p = new URLSearchParams(window.location.search || '');
    var orderId = p.get('transaction_id') || p.get('order_id') || p.get('orderId') || p.get('txn_id') || p.get('transaction') || p.get('sale_id') || p.get('payment_id') || p.get('checkout_id') || p.get('external_id') || p.get('event_id') || p.get('session_id') || p.get('purchase_id');

    if (!orderId) {
      console.warn('[Invalid Traffic] Missing transaction_id/order_id. Purchase was not sent.');
      return;
    }

    var payload = {
      name: 'purchase',
      event_name: 'purchase',
      value: p.get('value') || p.get('amount') || p.get('price') || '',
      currency: p.get('currency') || p.get('currency_code') || 'USD',
      external_id: orderId,
      source: p.get('source') || p.get('utm_source') || 'thank-you',
      medium: p.get('medium') || p.get('utm_medium') || 'checkout',
      campaign: p.get('campaign') || p.get('utm_campaign') || '',
      content: p.get('content') || p.get('utm_content') || '',
      term: p.get('term') || p.get('utm_term') || ''
    };

    var localKey = 'itp_purchase_sent_' + payload.external_id;
    try {
      if (window.localStorage && localStorage.getItem(localKey)) {
        console.warn('[Invalid Traffic] Duplicate purchase skipped locally:', payload.external_id);
        return;
      }
    } catch (e) {}

    var fn = window.ITPGlobalTrackConversion || window.ITPTrackConversion;
    if (!fn) {
      console.warn('[Invalid Traffic] Conversion helper not loaded yet.');
      return;
    }

    fn(payload).then(function (res) {
      try { if (window.localStorage) localStorage.setItem(localKey, '1'); } catch (e) {}
      console.log('[Invalid Traffic] Purchase sent:', res);
    }).catch(function (err) {
      console.error('[Invalid Traffic] Purchase error:', err);
    });
  }

  if (document.readyState === 'complete') {
    setTimeout(sendInvalidTrafficPurchase, 700);
  } else {
    window.addEventListener('load', function () {
      setTimeout(sendInvalidTrafficPurchase, 700);
    }, { once: true });
  }
})();
</script>
ITPHELPER;
        $this->admin_shell_start('Pixels', 'Paste ad pixels once. Connect confirmed sales only when your checkout can send purchase data back.');
        if (isset($_GET['saved'])) echo '<div class="itp-notice-ok">Pixels saved. New public bridge visits will use this global setup.</div>';
        ?>
        <form method="post" class="itp-card itp-pixels-conversions-card itp-global-pixels-page itp-simple-pixels-page itp-simplified-pixels-page">
            <?php wp_nonce_field('itp_save_pixels', 'itp_pixels_nonce'); ?>
            <input type="hidden" name="public_token" value="<?php echo esc_attr($settings['public_token'] ?? ''); ?>">
            <input type="hidden" name="webhook_secret" value="<?php echo esc_attr($webhook_secret); ?>">
            <input type="hidden" name="auto_url_conversions" value="1">

            <div class="itp-card-head itp-pixels-head itp-pixel-hub-head">
                <div>
                    <span class="itp-section-kicker">Global pixel setup</span>
                    <h2>Paste once. Load everywhere. Connect sales only when your checkout supports it.</h2>
                    <p>Add trusted platform pixels once and Invalid Traffic loads them across Bridge, Cards, Cookie, VSL and VSL + Landing Page assets. For confirmed sales, paste the one-line thank-you pixel on your success page or use the postback URL when a platform supports it.</p>
                </div>
                <div class="itp-pixel-status-stack">
                    <span><?php echo esc_html($active_sources); ?> active source<?php echo $active_sources === 1 ? '' : 's'; ?></span>
                    <span><?php echo esc_html(count($webhook_history)); ?> conversion delivery<?php echo count($webhook_history) === 1 ? '' : ' logs'; ?></span>
                </div>
            </div>

            <div class="itp-pixel-master-row itp-simple-master-row itp-pixel-toggle-row">
                <label class="itp-toggle"><input type="checkbox" name="itp_pixels_enabled" value="1" <?php checked($settings['enabled'] ?? '1', '1'); ?>><span></span><strong>Enable global pixels</strong><em>Loads saved scripts on bridge, VSL, landing and thank-you pages.</em></label>
                <label class="itp-toggle"><input type="checkbox" name="sitewide_runtime" value="1" <?php checked($settings['sitewide_runtime'] ?? '1', '1'); ?>><span></span><strong>Enable last-touch attribution</strong><em>Lets thank-you pages know which bridge sent the buyer.</em></label>
            </div>

            <section class="itp-pixel-simple-step itp-pixel-main-step">
                <div class="itp-simple-step-number">1</div>
                <div class="itp-simple-step-body">
                    <div class="itp-manager-title">
                        <div>
                            <h3>Global platform pixels</h3>
                            <p>Paste base scripts for Meta, Google, Microsoft UET, Taboola, TikTok, Outbrain, affiliate networks or any custom tracker. These pixels fire globally; purchases need Step 2 when you want revenue inside Invalid Traffic Analytics.</p>
                            <div class="itp-platform-chip-row" aria-label="Common supported platforms">
                                <span>Meta</span><span>Google</span><span>Microsoft UET</span><span>Taboola</span><span>TikTok</span><span>Outbrain</span><span>Affiliate</span><span>Custom</span>
                            </div>
                        </div>
                    </div>

                    <div class="itp-simple-helper-grid itp-pixel-flow-grid">
                        <div class="itp-simple-helper-box"><strong>1. Paste platform pixel</strong><p>Meta, Google, Taboola, TikTok and similar scripts go here once.</p></div>
                        <div class="itp-simple-helper-box"><strong>2. Use bridge buttons</strong><p>Bridge CTAs append <code>itp_bridge</code>, session and visitor data automatically.</p></div>
                        <div class="itp-simple-helper-box"><strong>3. Connect purchase data</strong><p>For revenue, use a thank-you pixel or postback with the standard fields <code>transaction_id</code>, <code>value</code> and <code>currency</code>.</p></div>
                    </div>

                    <div class="itp-pixel-console-grid itp-pixel-console-grid-polished">
                        <div class="itp-pixel-console-box itp-pixel-console-primary">
                            <h3>Head scripts</h3>
                            <p>Use this for scripts the platform recommends in the page head. Most base pixels go here. Do not paste the same pixel in both boxes.</p>
                            <textarea class="itp-code-area" name="custom_head" placeholder="Paste global head pixels here."><?php echo esc_textarea($settings['global_head'] ?? ''); ?></textarea>
                        </div>
                        <div class="itp-pixel-console-box itp-pixel-console-primary">
                            <h3>Footer / body scripts</h3>
                            <p>Use this only when the platform specifically gives body/footer code, noscript code or affiliate scripts that should load after content.</p>
                            <textarea class="itp-code-area" name="custom_footer" placeholder="Paste global footer/body scripts here."><?php echo esc_textarea($settings['global_footer'] ?? ''); ?></textarea>
                        </div>
                    </div>

                    <details class="itp-source-advanced itp-extra-pixel-drawer" data-itp-custom-pixels>
                        <summary>Advanced: separate named pixel blocks</summary>
                        <div class="itp-pixel-manager itp-extra-pixel-manager">
                            <div class="itp-manager-title">
                                <div><h3>Separate pixel blocks</h3><p>Optional. Use this only when you want to keep platforms organized one by one. The two boxes above are enough for most setups.</p></div>
                                <button type="button" class="itp-btn itp-btn-ghost" data-itp-add-custom-pixel>+ Add pixel</button>
                            </div>
                            <div class="itp-repeater-list" data-itp-custom-pixel-list>
                                <?php foreach ($pixels as $idx => $pixel) : $this->render_custom_pixel_row((int)$idx, $pixel); endforeach; if (empty($pixels)) : ?><div class="itp-empty-note">No separated pixel blocks yet. Most users can paste everything in Head scripts or Footer / body scripts.</div><?php endif; ?>
                            </div>
                        </div>
                    </details>
                </div>
            </section>

            <section class="itp-pixel-simple-step itp-universal-receiver-step itp-conversion-receiver-step">
                <div class="itp-simple-step-number">2</div>
                <div class="itp-simple-step-body">
                    <div class="itp-manager-title itp-conversion-receiver-head">
                        <div>
                            <h3>Connect confirmed sales / revenue</h3>
                            <p>Most users only need Step 1 for Meta, Google, Taboola, TikTok and affiliate pixels. Use this section only when you want Invalid Traffic Analytics to show confirmed purchases and revenue.</p>
                        </div>
                        <span class="itp-receiver-badge">Optional setup</span>
                    </div>

                    <div class="itp-receiver-simple-intro">
                        <div>
                            <strong>Simple rule</strong>
                            <p>Ad pixels load automatically after you paste them above. Sales appear in Invalid Traffic when a buyer returns to your thank-you page with the one-line pixel, or when your platform sends a postback.</p>
                        </div>
                        <div>
                            <strong>These codes are unique to this site</strong>
                            <p>The URLs below are generated for this WordPress installation and include this site's private token. A different client/domain gets its own URL.</p>
                        </div>
                        <div>
                            <strong>If your platform cannot send sales back</strong>
                            <p>Leave this section alone. Invalid Traffic will still track bridge visits, clicks and traffic quality, but not confirmed revenue.</p>
                        </div>
                    </div>

                    <div class="itp-admin-demo-purchase-card" data-itp-demo-base="<?php echo esc_url($receiver_url); ?>">
                        <div>
                            <span class="itp-option-kicker">Safe test</span>
                            <h3>Send a demo purchase</h3>
                            <p>Use this to verify that revenue attribution is working before connecting a real checkout. This creates a demo conversion for the selected bridge.</p>
                        </div>
                        <div class="itp-admin-demo-grid">
                            <label>Bridge
                                <select data-itp-demo-bridge <?php disabled(empty($bridge_choices)); ?>>
                                    <?php foreach ($bridge_choices as $bridge_choice) : ?>
                                        <option value="<?php echo esc_attr((int)$bridge_choice->ID); ?>"><?php echo esc_html('#' . (int)$bridge_choice->ID . ' · ' . get_the_title($bridge_choice)); ?></option>
                                    <?php endforeach; ?>
                                </select>
                            </label>
                            <label>Value<input data-itp-demo-value type="text" value="29.90"></label>
                            <label>Currency<input data-itp-demo-currency type="text" value="USD"></label>
                            <label>Source<input data-itp-demo-source type="text" value="admin-demo"></label>
                        </div>
                        <div class="itp-row-actions">
                            <button type="button" class="itp-btn" data-itp-send-demo-purchase <?php disabled(empty($bridge_choices)); ?>>Send demo purchase</button>
                            <a class="itp-btn itp-btn-ghost" data-itp-demo-analytics-link target="_blank" rel="noopener" href="#">Open selected bridge analytics</a>
                            <span class="itp-demo-purchase-status" data-itp-demo-status><?php echo empty($bridge_choices) ? 'Create a bridge first to send a demo purchase.' : 'Demo purchases are counted like real conversions, so use them only on test bridges.'; ?></span>
                        </div>
                    </div>

                    <div class="itp-receiver-path-grid">
                        <div class="itp-source-endpoint-panel itp-simple-endpoint-panel itp-receiver-path-card">
                            <span class="itp-option-kicker">Option A · easiest when available</span>
                            <h3>My checkout sends buyers to a thank-you page</h3>
                            <p>Use this when the checkout lets you send buyers back to any thank-you/success page after payment, including a WordPress page or a custom PHP/HTML page on this site.</p>
                            <ol class="itp-plain-steps">
                                <li>Copy the one-line Invalid Traffic Pixel below.</li>
                                <li>Paste it only on the final thank-you/success page.</li>
                                <li>Set your checkout success/return URL to that page. If the platform can pass <code>transaction_id</code>, <code>value</code> and <code>currency</code>, Invalid Traffic will use them. If the platform uses <code>order_id</code> instead, that also works. If it cannot pass an ID, the pixel can still count one confirmed thank-you return from the last bridge click.</li>
                            </ol>
                            <label>One-line thank-you pixel<textarea class="itp-code-area itp-copy-field" readonly onclick="this.select()" style="min-height:72px"><?php echo esc_textarea($thankyou_pixel_tag); ?></textarea></label>
                            <div class="itp-row-actions"><button type="button" class="itp-btn" data-itp-copy-nearest>Copy one-line pixel</button></div>
                            <label>Example success URL<textarea class="itp-code-area itp-copy-field" readonly onclick="this.select()" style="min-height:72px"><?php echo esc_textarea($example_thankyou_url); ?></textarea></label>
                            <p class="itp-note">When buyers click a bridge button, Invalid Traffic saves the last bridge click in the browser. If the checkout sends the buyer back to this same site, the one-line pixel can match that return to the correct bridge. If the checkout can also preserve <code>itp_bridge</code>, even better.</p>
                            <details class="itp-source-advanced itp-helper-code-drawer">
                                <summary>Advanced: show manual helper code</summary>
                                <label>Manual helper code<textarea class="itp-code-area itp-copy-field" readonly onclick="this.select()" style="min-height:190px"><?php echo esc_textarea($external_snippet); ?></textarea></label>
                                <p class="itp-note"><strong>Most users should use the one-line pixel above.</strong> Use this manual helper only if you need custom JavaScript behavior on a WordPress thank-you page. Do not add it to the global Head/Footer pixel boxes.</p>
                                <div class="itp-row-actions"><button type="button" class="itp-btn" data-itp-copy-nearest>Copy manual helper</button></div>
                            </details>
                        </div>

                        <div class="itp-source-endpoint-panel itp-simple-endpoint-panel itp-receiver-path-card">
                            <span class="itp-option-kicker">Option B · for affiliate/checkouts</span>
                            <h3>My platform has postback / webhook</h3>
                            <p>Use this when Hotmart, ClickBank, BuyGoods, Shopify, WooCommerce, a custom checkout or another affiliate platform asks for a postback, webhook, S2S callback or conversion URL.</p>
                            <label>Your postback URL<textarea class="itp-code-area itp-copy-field" readonly onclick="this.select()" style="min-height:76px"><?php echo esc_textarea($receiver_url); ?></textarea></label>
                            <div class="itp-row-actions"><button type="button" class="itp-btn" data-itp-copy-nearest>Copy postback URL</button></div>
                            <div class="itp-postback-human-note">
                                <strong>Standard fields / safe aliases:</strong>
                                <span><code>transaction_id</code> is preferred; <code>order_id</code>, <code>sale_id</code>, <code>payment_id</code> and <code>session_id</code> also work</span>
                                <span><code>value</code> and <code>currency</code> for revenue</span>
                                <span><code>itp_bridge</code> or <code>bridge_id</code> for attribution</span>
                            </div>
                            <details class="itp-source-advanced">
                                <summary>Supported adapter presets</summary>
                                <p class="itp-note">The universal URL stays the same. Invalid Traffic tries known safe adapters first, then Generic Standard Fields. Current presets include: Hotmart, Kiwify, Perfect Pay, Braip, Stripe, PayPal, Adyen, ClickBank, BuyGoods, Digistore24, Lemon Squeezy, Paddle, Gumroad, WooCommerce, Shopify, Magento, PrestaShop, ThriveCart, PayKickstart, SamCart, ClickFunnels, JVZoo, WarriorPlus, PartnerStack, Impact, Awin, ShareASale, CJ Affiliate, LinkTrust, MaxBounty, MaxWeb, CrakRevenue, AdCombo, Kajabi, Teachable, Mollie, Checkout.com, Braintree, Square, Worldpay, Klarna, GoCardless, Authorize.net, Payhip, Podia, Thinkific, Systeme.io, Kartra, Stan Store, Tapfiliate, Rewardful, FirstPromoter, Tolt, Reditus, Rakuten Advertising, FlexOffers, Tradedoubler, Webgains, Admitad, eBay Partner Network, Amazon Associates, Eduzz, Monetizze, Ticto, Cartpanda, Yampi, Lastlink, Doppus, Mercado Pago, PagSeguro and Asaas.</p>
                                <p class="itp-note"><strong>Important:</strong> Amazon/eBay and some affiliate networks may be click-only unless they provide a postback, subID report or conversion callback.</p>
                            </details>
                            <details class="itp-source-advanced">
                                <summary>Advanced mapping example</summary>
                                <label>Example full postback with fields<textarea class="itp-code-area itp-copy-field" readonly onclick="this.select()" style="min-height:100px"><?php echo esc_textarea($example_postback_url); ?></textarea></label>
                                <div class="itp-simple-helper-grid itp-required-field-grid">
                                    <div class="itp-simple-helper-box"><strong>Bridge</strong><p>Send <code>itp_bridge</code>, <code>bridge_id</code>, <code>presell_id</code> or <code>bridge_slug</code>.</p></div>
                                    <div class="itp-simple-helper-box"><strong>Transaction ID</strong><p>Preferred: <code>transaction_id</code>. Also accepted: <code>order_id</code>, <code>txn_id</code>, <code>sale_id</code>, <code>payment_id</code>, <code>checkout_id</code>, <code>external_id</code> or <code>session_id</code>. If none is available, thank-you pixel fallback counts one return per bridge session.</p></div>
                                    <div class="itp-simple-helper-box"><strong>Revenue</strong><p>Send <code>value</code> and <code>currency</code>. Without value, it counts conversion but cannot calculate revenue.</p></div>
                                </div>
                                <div class="itp-form-grid two" style="margin-top:12px">
                                    <label>Base endpoint<input readonly onclick="this.select()" class="itp-copy-field" value="<?php echo esc_attr($webhook_url); ?>"></label>
                                    <label>Private token already inside URL<input readonly onclick="this.select()" class="itp-copy-field" value="<?php echo esc_attr($webhook_secret); ?>"></label>
                                </div>
                                <p class="itp-note"><strong>For non-technical users:</strong> copy the full postback URL above. Only open this section if the platform asks you to map field names/macros.</p>
                            </details>
                        </div>
                    </div>

                    <div class="itp-pixel-truth-note">
                        <strong>Important:</strong> pasting a platform pixel is enough for normal ad tracking. Confirmed sales inside Invalid Traffic require the one-line thank-you pixel on a success/return page, or a postback/webhook from the checkout. Standard purchase fields are <code>event=purchase</code>, <code>transaction_id</code>, <code>value</code> and <code>currency</code>.
                    </div>                    </div>
                </div>
            </section>

            <script>
            (function(){
              function itpDemoUrl(card){
                var base = card.getAttribute('data-itp-demo-base') || '';
                if(!base) return '';
                var url = new URL(base, window.location.href);
                var bridge = card.querySelector('[data-itp-demo-bridge]');
                var value = card.querySelector('[data-itp-demo-value]');
                var currency = card.querySelector('[data-itp-demo-currency]');
                var source = card.querySelector('[data-itp-demo-source]');
                var id = 'DEMO-' + (bridge && bridge.value ? bridge.value : 'bridge') + '-' + Date.now();
                url.searchParams.set('itp_bridge', bridge && bridge.value ? bridge.value : '');
                url.searchParams.set('event', 'purchase');
                url.searchParams.set('value', value && value.value ? value.value : '29.90');
                url.searchParams.set('currency', currency && currency.value ? currency.value : 'USD');
                url.searchParams.set('transaction_id', id);
                url.searchParams.set('source', source && source.value ? source.value : 'admin-demo');
                return url.toString();
              }
              function itpSyncDemoAnalytics(card){
                var bridge = card.querySelector('[data-itp-demo-bridge]');
                var link = card.querySelector('[data-itp-demo-analytics-link]');
                if(bridge && link && bridge.value){
                  link.href = '<?php echo esc_js(admin_url('admin.php?page=itp-analytics&id=')); ?>' + encodeURIComponent(bridge.value);
                }
              }
              document.addEventListener('change', function(e){
                var bridge = e.target.closest('[data-itp-demo-bridge]');
                if(bridge){ var card = bridge.closest('[data-itp-demo-base]'); if(card) itpSyncDemoAnalytics(card); }
              });
              document.addEventListener('click', function(e){
                var btn = e.target.closest('[data-itp-send-demo-purchase]');
                if(!btn) return;
                e.preventDefault();
                var card = btn.closest('[data-itp-demo-base]');
                if(!card) return;
                var status = card.querySelector('[data-itp-demo-status]');
                var url = itpDemoUrl(card);
                if(!url){ if(status) status.textContent = 'Missing postback URL.'; return; }
                btn.disabled = true;
                if(status) status.textContent = 'Sending demo purchase...';
                fetch(url, { credentials:'same-origin' })
                  .then(function(res){ return res.json().then(function(json){ return { ok: res.ok, json: json }; }); })
                  .then(function(result){
                    if(result.ok && result.json && result.json.ok){
                      var bridge = card.querySelector('[data-itp-demo-bridge]');
                      var value = card.querySelector('[data-itp-demo-value]');
                      var currency = card.querySelector('[data-itp-demo-currency]');
                      if(status) status.textContent = 'Demo purchase recorded for bridge #' + (bridge ? bridge.value : '') + ' · ' + (currency ? currency.value : '') + ' ' + (value ? value.value : '');
                      itpSyncDemoAnalytics(card);
                    } else {
                      if(status) status.textContent = (result.json && result.json.message) ? result.json.message : 'Demo purchase failed.';
                    }
                  })
                  .catch(function(err){ if(status) status.textContent = err && err.message ? err.message : 'Demo purchase failed.'; })
                  .finally(function(){ btn.disabled = false; });
              });
              document.addEventListener('DOMContentLoaded', function(){
                document.querySelectorAll('[data-itp-demo-base]').forEach(itpSyncDemoAnalytics);
              });
            })();
            </script>
            <div class="itp-sticky-save"><button type="submit" class="itp-btn">Save pixel setup</button></div>
        </form>

        <section class="itp-card itp-pixel-history-card itp-pixel-history-card-compact">
            <details class="itp-pixel-log-details">
                <summary>
                    <div><span class="itp-kicker">Delivery history</span><h2>Conversion delivery log</h2><p>Only advanced postbacks and thank-you helper events appear here. Normal ad pixels can work even when this log is empty.</p></div>
                    <em>Show log</em>
                </summary>
                <div class="itp-table-wrap"><table class="itp-table"><thead><tr><th>Time</th><th>Status</th><th>Counted?</th><th>Source</th><th>Event</th><th>Bridge</th><th>Value</th><th>External ID</th><th>Message</th></tr></thead><tbody>
                <?php foreach ($webhook_history as $row) : $bridge_label = !empty($row['bridge_title']) ? $row['bridge_title'] : (!empty($row['bridge_id']) ? ('Bridge #' . (int)$row['bridge_id']) : 'Global / not matched'); ?>
                    <tr>
                        <td><?php echo esc_html($row['time']); ?></td>
                        <td><span class="itp-status-soft"><?php echo esc_html($row['status']); ?></span></td>
                        <td><?php echo !empty($row['counted']) && $row['counted'] !== '0' ? 'Yes' : 'No'; ?></td>
                        <td><?php echo esc_html($row['source']); ?></td>
                        <td><?php echo esc_html($row['event']); ?></td>
                        <td><?php if (!empty($row['bridge_id']) && get_post_type((int)$row['bridge_id']) === self::CPT) : ?><a href="<?php echo esc_url(admin_url('admin.php?page=itp-analytics&id=' . (int)$row['bridge_id'])); ?>"><?php echo esc_html($bridge_label); ?></a><?php else : ?><?php echo esc_html($bridge_label); ?><?php endif; ?></td>
                        <td><?php echo esc_html(trim(($row['currency'] ?? '') . ' ' . ($row['value'] ?? ''))); ?></td>
                        <td><?php echo esc_html($row['external_id']); ?></td>
                        <td><?php echo esc_html($row['message']); ?></td>
                    </tr>
                <?php endforeach; if (empty($webhook_history)) : ?><tr><td colspan="9">No conversion deliveries yet. This is normal until you connect a checkout postback or use a thank-you helper.</td></tr><?php endif; ?>
            </tbody></table></div>
            </details>
        </section>

        <?php
        $this->admin_shell_end();
    }


    public function page_settings() {
        if (!current_user_can('manage_options')) return;
        $this->handle_actions();
        $this->handle_settings_save();
        $settings = $this->settings();
        $current_plugin_language = $this->sanitize_plugin_language($settings['plugin_language'] ?? 'auto');
        $this->admin_shell_start('Settings', 'Keep it simple. Change brand details, button delay and basic compliance pages. Bridge URLs use the clean root format: domain.com/slug/.');
        if (isset($_GET['policies'])) {
            echo '<div class="itp-notice-ok">Policy pages created or connected. Add these links to your bridge pages when running paid traffic.</div>';
        }
        if (isset($_GET['cleared'])) {
            echo '<div class="itp-notice-ok">Old traffic logs cleaned: ' . esc_html((int)$_GET['cleared']) . ' event(s) removed.</div>';
        }
        ?>
        <form method="post" class="itp-card itp-settings-form">
            <?php wp_nonce_field('itp_save_settings', 'itp_nonce'); ?>
            <div class="itp-final-url">Public bridge URL format: <strong><?php echo esc_html(home_url('/your-slug/')); ?></strong></div>
            <div class="itp-form-grid two">
                <label>Brand name inside admin panel<input name="brand_name" value="<?php echo esc_attr($settings['brand_name']); ?>"></label>
                <label>Default button delay in seconds<input type="number" min="0" max="60" name="button_delay" value="<?php echo esc_attr($settings['button_delay']); ?>"></label>
            </div>
            <h2>Language & localization</h2>
            <p>This changes fixed plugin interface text. Bridge headlines, buttons, VSL copy, card text and CTA messages stay controlled by the text you write inside each bridge.</p>
            <div class="itp-form-grid two">
                <label>Plugin language
                    <select name="plugin_language">
                        <?php foreach (self::plugin_language_choices() as $locale_code => $locale_label) : ?>
                            <option value="<?php echo esc_attr($locale_code); ?>" <?php selected($current_plugin_language, $locale_code); ?>><?php echo esc_html($locale_label); ?></option>
                        <?php endforeach; ?>
                    </select>
                    <small>Default is Auto, which follows the WordPress site/admin language. Built-in core UI translations are active, and external language packs can still be added in /languages.</small>
                </label>
            </div>
            <h2>Risk rules</h2>
            <p>Adjust how the first-party quality layer classifies engagement, fast clicks and repeated visits. These rules do not block users; they label sessions for analysis.</p>
            <div class="itp-form-grid two">
                <label>Engaged after seconds<input type="number" min="3" max="120" name="engaged_seconds" value="<?php echo esc_attr($settings['engaged_seconds']); ?>"></label>
                <label>Engaged after scroll %<input type="number" min="5" max="100" name="engaged_scroll" value="<?php echo esc_attr($settings['engaged_scroll']); ?>"></label>
                <label>Fast click threshold seconds<input type="number" min="1" max="20" name="fast_click_seconds" value="<?php echo esc_attr($settings['fast_click_seconds']); ?>"></label>
                <label>Repeated visit limit<input type="number" min="2" max="100" name="repeat_visit_limit" value="<?php echo esc_attr($settings['repeat_visit_limit']); ?>"></label>
                <label>Repeated visit window minutes<input type="number" min="5" max="1440" name="repeat_window_minutes" value="<?php echo esc_attr($settings['repeat_window_minutes']); ?>"></label>
                <label>Data retention days<input type="number" min="7" max="365" name="retention_days" value="<?php echo esc_attr($settings['retention_days'] ?? '30'); ?>"></label>
            </div>
            <p class="itp-note">Traffic logs older than the retention window are cleaned automatically once per day. Keep this shorter for high-volume paid traffic sites.</p>
            <div class="itp-row-actions"><a class="itp-btn itp-btn-ghost" href="<?php echo esc_url(wp_nonce_url(admin_url('admin.php?page=itp-settings&itp_action=clear_old_logs'), 'itp_clear_old_logs')); ?>">Clear old logs now</a></div>
            <h2>Emergency mode</h2>
            <p>Use this after an AdSense invalid-traffic warning, ad-account landing-page issue or suspicious paid traffic spike. It does not touch destination pages; it makes the bridge analytics stricter and highlights evidence faster.</p>
            <label class="itp-toggle itp-toggle-danger"><input type="checkbox" name="emergency_mode" value="1" <?php checked($settings['emergency_mode'] ?? '0', '1'); ?>><span></span><strong>Enable Emergency Mode</strong><em>Stricter risk labels for fast clicks, repeated visits and weak engagement. No final-page changes.</em></label>
            <label class="itp-toggle"><input type="checkbox" name="powered_by" value="1" <?php checked($settings['powered_by'], '1'); ?>><span></span><strong>Show small powered-by footer</strong><em>Disabled by default. Enable only if you want the plugin attributed on public bridge pages.</em></label>
            <button class="itp-btn" type="submit">Save Settings</button>
        </form>
        <section class="itp-card">
            <div class="itp-card-head"><div><h2>Compliance pages</h2><p>Create simple Contact, Privacy Policy and Terms pages. These links are shown in the bridge footer when connected.</p></div><a class="itp-btn" href="<?php echo esc_url(wp_nonce_url(admin_url('admin.php?page=itp-settings&itp_action=create_policies'), 'itp_create_policies')); ?>">Create/connect pages</a></div>
            <div class="itp-policy-grid">
                <?php foreach (['contact' => 'Contact', 'privacy' => 'Privacy Policy', 'terms' => 'Terms of Use'] as $key => $label) : $pid = (int)($settings[$key . '_page_id'] ?? 0); ?>
                    <div><strong><?php echo esc_html($label); ?></strong><?php if ($pid && get_post($pid)) : ?><em>Connected</em><p><a href="<?php echo esc_url(get_permalink($pid)); ?>" target="_blank">Open</a> · <a href="<?php echo esc_url(get_edit_post_link($pid)); ?>">Edit</a></p><?php else : ?><em class="warn">Not connected</em><?php endif; ?></div>
                <?php endforeach; ?>
            </div>
        </section>
        <?php
        $this->admin_shell_end();
    }

    public function page_license() {
    if (!current_user_can('manage_options')) return;

    $notice = '';
    $notice_class = 'ok';

    if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['itp_license_nonce'])) {
        check_admin_referer('itp_license_action', 'itp_license_nonce');

        $license_action = sanitize_key(wp_unslash($_POST['license_action'] ?? 'activate'));
        $license_key = sanitize_text_field(wp_unslash($_POST['license_key'] ?? ''));

        if ($license_action === 'clear') {
            delete_option('itp_license_state');
            delete_option('itp_license_key');
            delete_option('itp_license_status');
            delete_option('itp_license_data');
            delete_option('itp_license_last_check');
            delete_option('itp_license_last_error');
            delete_transient('itp_license_cached_valid');
            $notice = 'Local license removed from this site.';
            $notice_class = 'warn';
        } elseif ($license_key === '') {
            $notice = 'Please enter your license key.';
            $notice_class = 'danger';
        } else {
            $endpoint = $license_action === 'check' ? 'check.php' : 'activate.php';

            $response = $this->license_remote_request($endpoint, [
                'license_key' => $license_key,
                'site_url' => home_url('/'),
                'domain' => wp_parse_url(home_url('/'), PHP_URL_HOST),
                'plugin_version' => self::VERSION,
                'wp_version' => get_bloginfo('version'),
                'php_version' => PHP_VERSION,
            ]);

            if (!empty($response['success'])) {
                $license = $response['data']['license'] ?? [];
                $domain = $response['data']['domain'] ?? [];

                $remote_license_status = sanitize_text_field($license['status'] ?? 'inactive');
$remote_domain_status  = sanitize_text_field($domain['status'] ?? 'not_activated');

$remote_is_active = in_array($remote_license_status, ['active', 'trialing'], true)
    && $remote_domain_status === 'active';

$state = [
    'license_key' => $license_key,
    'active' => $remote_is_active,
    'status' => $remote_license_status,
    'plan_code' => $remote_is_active ? sanitize_text_field($license['plan_code'] ?? '') : '',
    'billing_interval' => $remote_is_active ? sanitize_text_field($license['billing_interval'] ?? '') : '',
    'max_domains' => $remote_is_active ? (int)($license['max_domains'] ?? 1) : 0,
    'current_period_end' => $remote_is_active ? (int)($license['current_period_end'] ?? 0) : 0,
    'cancel_at_period_end' => (int)($license['cancel_at_period_end'] ?? 0),
    'domain' => sanitize_text_field($domain['domain'] ?? wp_parse_url(home_url('/'), PHP_URL_HOST)),
    'domain_status' => $remote_domain_status,
    'server_code' => sanitize_text_field($response['code'] ?? ''),
    'server_message' => sanitize_text_field($response['message'] ?? ''),
    'last_checked' => time(),
];

                update_option('itp_license_state', $state, false);
                update_option('itp_license_key', $license_key, false);
                update_option('itp_license_status', 'active', false);
                update_option('itp_license_data', $response['data'] ?? [], false);
                update_option('itp_license_last_check', time(), false);
                delete_option('itp_license_last_error');
                set_transient('itp_license_cached_valid', 'yes', 12 * HOUR_IN_SECONDS);

                $notice = $response['message'] ?? 'License activated successfully.';
                $notice_class = 'ok';
            } else {
                $notice = $response['message'] ?? 'License server rejected this request.';
                $notice_class = 'danger';
                update_option('itp_license_status', 'inactive', false);
                update_option('itp_license_last_error', $notice, false);
                delete_transient('itp_license_cached_valid');

                $old = get_option('itp_license_state', []);
                if (is_array($old) && !empty($old)) {
                    $old['active'] = false;
                    $old['server_message'] = $notice;
                    $old['last_checked'] = time();
                    update_option('itp_license_state', $old, false);
                }
            }
        }
    }

    $state = get_option('itp_license_state', []);
    if (!is_array($state)) {
        $state = [];
    }

    $license_status = sanitize_text_field((string)($state['status'] ?? 'inactive'));
$domain_status  = sanitize_text_field((string)($state['domain_status'] ?? 'not_activated'));

$is_active = !empty($state['active'])
    && in_array($license_status, ['active', 'trialing'], true)
    && $domain_status === 'active';

$license_key = (string)($state['license_key'] ?? '');
$masked_key = $license_key ? substr($license_key, 0, 8) . '••••••••••••' . substr($license_key, -4) : 'Not activated';

$period_end = $is_active ? (int)($state['current_period_end'] ?? 0) : 0;
$period_label = $period_end > 0 ? date_i18n('M j, Y', $period_end) : '—';

$display_plan = $is_active && !empty($state['plan_code'])
    ? strtoupper((string)$state['plan_code'])
    : '—';

$display_billing = $is_active && !empty($state['billing_interval'])
    ? (string)$state['billing_interval']
    : '—';

$display_domain_limit = $is_active && !empty($state['max_domains'])
    ? (string)$state['max_domains']
    : '—';

$display_domain = (string)($state['domain'] ?? wp_parse_url(home_url('/'), PHP_URL_HOST));
$display_domain_status = $domain_status ?: 'not_activated';

    $this->admin_shell_start('License', 'Activate this plugin with your Invalid Traffic license key.');
    ?>

    <?php if ($notice !== '') : ?>
        <section class="itp-card itp-license-notice <?php echo esc_attr($notice_class); ?>">
            <strong><?php echo esc_html($notice); ?></strong>
        </section>
    <?php endif; ?>

    <section class="itp-license-hero">
        <span class="itp-kicker">Premium Bridge Builder</span>
        <h2>Invalid Traffic v<?php echo esc_html(self::VERSION); ?></h2>
        <p>A lightweight WordPress bridge layer for paid traffic, affiliate campaigns and publisher safety. It tracks visits, intent, click quality, source, geo/IP signals and forward clicks without requiring Google Ads, Meta, Taboola or GA4 account connections.</p>
        <div class="itp-row-actions">
            <a class="itp-btn" href="<?php echo esc_url(admin_url('admin.php?page=itp-create')); ?>">Create Bridge</a>
            <a class="itp-btn itp-btn-white" href="<?php echo esc_url(admin_url('admin.php?page=itp-analytics')); ?>">Open Analytics</a>
        </div>
    </section>

    <section class="itp-insight-grid three">
        <div class="itp-card">
            <h2>What it does</h2>
            <p>Creates clean no-ad bridge pages, preserves UTMs/click IDs, records first-party events and shows traffic quality before the destination page.</p>
        </div>
        <div class="itp-card">
            <h2>What it tracks</h2>
            <p>Visits, clicks, CTR, time, scroll, source, campaign, referrer, device, browser, country/IP signal and risk reasons.</p>
        </div>
        <div class="itp-card">
            <h2>What it does not claim</h2>
            <p>It is not an ad-platform approval guarantee and does not replace fraud detection platforms connected directly to ad accounts.</p>
        </div>
    </section>

    <section class="itp-card">
        <div class="itp-card-head">
            <div>
                <h2>License activation</h2>
                <p>Paste the license key received after purchase. This site will be registered as one active domain on your license.</p>
            </div>
            <span class="itp-license-pill <?php echo $is_active ? 'active' : 'inactive'; ?>">
                <?php echo $is_active ? 'Active' : 'Not active'; ?>
            </span>
        </div>

        <form method="post" class="itp-license-form">
            <?php wp_nonce_field('itp_license_action', 'itp_license_nonce'); ?>

            <label>
                License key
                <input
                    type="text"
                    name="license_key"
                    value="<?php echo esc_attr($license_key); ?>"
                    placeholder="IT-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX"
                    autocomplete="off"
                >
            </label>

            <div class="itp-row-actions">
                <button class="itp-btn" type="submit" name="license_action" value="activate">Activate license</button>
                <button class="itp-btn itp-btn-ghost" type="submit" name="license_action" value="check">Check status</button>

                <?php if ($license_key) : ?>
                    <button class="itp-btn itp-btn-light" type="submit" name="license_action" value="clear" onclick="return confirm('Remove this local license from this site?');">Remove local key</button>
                <?php endif; ?>
            </div>
        </form>
    </section>

    <section class="itp-card">
        <h2>License status</h2>

        <div class="itp-license-status">
            <strong>Build</strong>
            <span>v<?php echo esc_html(self::VERSION); ?></span>

            <strong>Mode</strong>
            <span><?php echo $is_active ? 'Remote license active' : 'License required'; ?></span>

            <strong>License key</strong>
            <span><?php echo esc_html($masked_key); ?></span>

            <strong>Plan</strong>
<span><?php echo esc_html($display_plan); ?></span>

<strong>Billing</strong>
<span><?php echo esc_html($display_billing); ?></span>

<strong>Domain limit</strong>
<span><?php echo esc_html($display_domain_limit); ?></span>

<strong>This domain</strong>
<span><?php echo esc_html($display_domain); ?></span>

<strong>Domain status</strong>
<span><?php echo esc_html($display_domain_status); ?></span>

<strong>Period end</strong>
<span><?php echo esc_html($period_label); ?></span>

            <strong>Last checked</strong>
            <span><?php echo !empty($state['last_checked']) ? esc_html(date_i18n('M j, Y H:i', (int)$state['last_checked'])) : 'Never'; ?></span>
        </div>

        <?php if (!empty($state['server_message'])) : ?>
            <p class="itp-note"><?php echo esc_html($state['server_message']); ?></p>
        <?php endif; ?>
    </section>

    <?php
    $this->admin_shell_end();
}

private function license_remote_request($endpoint, $payload) {
    $url = 'https://invalid-traffic.com/license/' . ltrim($endpoint, '/');

    $response = wp_remote_post($url, [
        'timeout' => 20,
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'body' => wp_json_encode($payload),
    ]);

    if (is_wp_error($response)) {
        return [
            'success' => false,
            'code' => 'wp_remote_error',
            'message' => $response->get_error_message(),
        ];
    }

    $status = (int)wp_remote_retrieve_response_code($response);
    $body = (string)wp_remote_retrieve_body($response);
    $json = json_decode($body, true);

    if (!is_array($json)) {
        return [
            'success' => false,
            'code' => 'invalid_json',
            'message' => 'Invalid response from license server. HTTP ' . $status,
        ];
    }

    return $json;
}

    private function vsl_font_choices() {
        return [
            'system' => 'system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',
            'Inter' => 'Inter,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',
            'Poppins' => 'Poppins,Inter,system-ui,sans-serif',
            'Montserrat' => 'Montserrat,Inter,system-ui,sans-serif',
            'Roboto' => 'Roboto,Arial,system-ui,sans-serif',
            'Open Sans' => '"Open Sans",Arial,system-ui,sans-serif',
            'Oswald' => 'Oswald,Impact,"Arial Narrow",sans-serif',
            'Bebas Neue' => '"Bebas Neue",Impact,"Arial Narrow",sans-serif',
            'Arial' => 'Arial,Helvetica,sans-serif',
        ];
    }

    private function vsl_font_stack($choice) {
        $choices = $this->vsl_font_choices();
        $choice = (string)$choice;
        return $choices[$choice] ?? $choices['Inter'];
    }

    private function vsl_sanitize_font_choice($value) {
        $value = sanitize_text_field(wp_unslash($value));
        return array_key_exists($value, $this->vsl_font_choices()) ? $value : 'Inter';
    }

    private function vsl_font_options_html($selected) {
        $labels = [
            'Inter' => 'Inter',
            'Poppins' => 'Poppins',
            'Montserrat' => 'Montserrat',
            'Roboto' => 'Roboto',
            'Open Sans' => 'Open Sans',
            'Oswald' => 'Oswald',
            'Bebas Neue' => 'Bebas Neue',
            'Arial' => 'Arial',
            'system' => 'System',
        ];
        $html = '';
        foreach ($labels as $value => $label) {
            $html .= '<option value="' . esc_attr($value) . '" ' . selected($selected, $value, false) . '>' . esc_html($label) . '</option>';
        }
        return $html;
    }

    private function vsl_custom_font_css($id, $slot, $url, $fallback_choice = 'Inter') {
        $url = esc_url($url);
        if (!$url) {
            return ['', $this->vsl_font_stack($fallback_choice)];
        }
        $family = 'ITP VSL ' . preg_replace('/[^A-Za-z0-9_ -]/', '', (string)$slot) . ' ' . (int)$id;
        $stack = '"' . $family . '",' . $this->vsl_font_stack($fallback_choice);
        $css = '<style id="itp-vsl-font-' . esc_attr(sanitize_key($slot)) . '-' . esc_attr((string)$id) . '">@font-face{font-family:"' . esc_html($family) . '";src:url("' . esc_url($url) . '") format("woff2");font-weight:100 1000;font-style:normal;font-display:swap;}</style>';
        return [$css, $stack];
    }

    public function maybe_handle_admin_actions() {
        $this->handle_actions();
    }

    private function handle_actions() {
        if (!current_user_can('manage_options') || empty($_GET['itp_action'])) return;
        $action = sanitize_key($_GET['itp_action']);
        $id = isset($_GET['id']) ? absint($_GET['id']) : 0;
        if ($action === 'export_csv') {
            check_admin_referer('itp_export_csv');
            $this->export_csv($id);
        }
        if ($action === 'export_leads_csv') {
            check_admin_referer('itp_export_leads_csv');
            $this->export_leads_csv($id);
        }
        if ($action === 'create_policies') {
            check_admin_referer('itp_create_policies');
            $this->create_policy_pages();
            wp_safe_redirect(admin_url('admin.php?page=itp-settings&policies=1'));
            exit;
        }
        if ($action === 'clear_old_logs') {
            check_admin_referer('itp_clear_old_logs');
            $deleted = $this->cleanup_old_events(true);
            wp_safe_redirect(admin_url('admin.php?page=itp-settings&cleared=' . (int)$deleted));
            exit;
        }
        if ($action === 'reset_metrics_scope') {
            check_admin_referer('itp_reset_metrics');
            $page = sanitize_key($_GET['page'] ?? 'itp-dashboard');
            $scope = sanitize_key($_GET['itp_scope'] ?? 'all');
            if (!$this->reset_confirmed()) {
                wp_safe_redirect(admin_url('admin.php?page=' . ($page ?: 'itp-dashboard') . '&reset_cancelled=1'));
                exit;
            }
            $deleted = $this->delete_metrics_for_scope($scope);
            wp_safe_redirect(admin_url('admin.php?page=' . ($page ?: 'itp-dashboard') . '&reset_scope=' . rawurlencode($scope) . '&reset_deleted=' . (int)$deleted));
            exit;
        }
        if (!$id || get_post_type($id) !== self::CPT) return;
        check_admin_referer('itp_action_' . $id);
        if ($action === 'toggle') {
            $current = get_post_status($id);
            wp_update_post(['ID' => $id, 'post_status' => $current === 'publish' ? 'draft' : 'publish']);
            wp_safe_redirect(admin_url('admin.php?page=itp-presells'));
            exit;
        }
        if ($action === 'duplicate') {
            $new = $this->duplicate_bridge($id);
            wp_safe_redirect(admin_url('admin.php?page=itp-create&id=' . $new));
            exit;
        }
        if ($action === 'delete') {
            wp_delete_post($id, true);
            wp_safe_redirect(admin_url('admin.php?page=itp-presells'));
            exit;
        }
        if ($action === 'delete_landing') {
            $page_id = isset($_GET['landing_page_id']) ? absint($_GET['landing_page_id']) : (int)get_post_meta($id, '_itp_elementor_landing_page_id', true);
            if (!$page_id) {
                $meta_tmp = $this->bridge_meta($id);
                $page_id = (int)($meta_tmp['elementor_landing_page_id'] ?? 0);
            }
            if ($page_id && get_post_type($page_id) === 'page') {
                $known_ids = $this->landing_page_ids_meta($id);
                $current_known = (int)get_post_meta($id, '_itp_elementor_landing_page_id', true);
                $source_bridge = (int)get_post_meta($page_id, '_itp_source_bridge_id', true);
                $belongs = ($source_bridge === (int)$id) || in_array((int)$page_id, array_map('intval', $known_ids), true) || ((int)$page_id === $current_known);
                if ($belongs) {
                    wp_delete_post($page_id, true);
                    $this->forget_landing_page_for_bridge($id, $page_id);
                }
            }
            $remaining = $this->landing_pages_for_bridge($id);
            $next_current = 0;
            if (!empty($remaining)) {
                $last = end($remaining);
                $next_current = (int)($last['id'] ?? 0);
            }
            $this->set_current_landing_page($id, $next_current, $this->bridge_meta($id));
            wp_safe_redirect(admin_url('admin.php?page=itp-create&id=' . $id . '&landing_deleted=1'));
            exit;
        }
        if ($action === 'set_current_landing') {
            $page_id = isset($_GET['landing_page_id']) ? absint($_GET['landing_page_id']) : 0;
            if ($page_id) {
                $this->set_current_landing_page($id, $page_id, $this->bridge_meta($id));
            }
            wp_safe_redirect(admin_url('admin.php?page=itp-create&id=' . $id . '&landing_current=1'));
            exit;
        }
        if ($action === 'reset_metrics') {
            if (!$this->reset_confirmed()) {
                wp_safe_redirect(admin_url('admin.php?page=itp-analytics&id=' . $id . '&reset_cancelled=1' . $this->analytics_query_suffix()));
                exit;
            }
            global $wpdb;
            $table = $wpdb->prefix . self::EVENT_TABLE;
            $deleted = $wpdb->delete($table, ['presell_id' => $id], ['%d']);
            wp_safe_redirect(admin_url('admin.php?page=itp-analytics&id=' . $id . '&reset=1&reset_deleted=' . (int)$deleted . $this->analytics_query_suffix()));
            exit;
        }
    }

    private function handle_save_bridge() {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST' || empty($_POST['itp_nonce'])) return;
        if (!current_user_can('manage_options')) return;
        check_admin_referer('itp_save_bridge', 'itp_nonce');

        $id = isset($_POST['bridge_id']) ? absint($_POST['bridge_id']) : 0;
        $submitted_title = sanitize_text_field(wp_unslash($_POST['title'] ?? ''));
        $title = $submitted_title !== '' ? $submitted_title : ($id ? ('Untitled campaign #' . $id) : 'Untitled campaign');
        $slug = sanitize_title(wp_unslash($_POST['slug'] ?? $title));
        if (!$slug) $slug = sanitize_title($title ?: 'bridge');
        $postarr = [
            'post_title' => $title,
            'post_name' => $slug,
            'post_type' => self::CPT,
            'post_status' => 'publish',
        ];
        if ($id && get_post_type($id) === self::CPT) {
            $postarr['ID'] = $id;
            $post_id = wp_update_post($postarr, true);
        } else {
            $post_id = wp_insert_post($postarr, true);
        }
        if (is_wp_error($post_id)) {
            wp_die(esc_html($post_id->get_error_message()));
        }
        if ($submitted_title === '' && !$id) {
            $title = 'Untitled campaign #' . (int)$post_id;
            wp_update_post(['ID' => (int)$post_id, 'post_title' => $title]);
        }
        $cards = [];
        $raw_cards = isset($_POST['cards']) && is_array($_POST['cards']) ? $_POST['cards'] : [];
        foreach ($raw_cards as $card) {
            $c = [
                'title' => sanitize_text_field(wp_unslash($card['title'] ?? '')),
                'text' => sanitize_text_field(wp_unslash($card['text'] ?? '')),
                'button' => sanitize_text_field(wp_unslash($card['button'] ?? 'Read more')),
                'url' => esc_url_raw(wp_unslash($card['url'] ?? '')),
                'image' => esc_url_raw(wp_unslash($card['image'] ?? '')),
            ];
            // Only complete cards are saved for the public page. Empty/partial rows are handled in the admin preview/validation.
            if ($c['title'] && $c['url']) {
                $cards[] = $c;
            }
        }
        $posted_template = sanitize_key($_POST['template'] ?? 'blue_cards');

if ($posted_template === 'cards_bridge') {
    $posted_template = 'blue_cards';
}
$color_value = function($key, $fallback) {
    $value = sanitize_hex_color(wp_unslash($_POST[$key] ?? ''));
    return $value ?: $fallback;
};

$bridge_bg_mode = sanitize_key($_POST['bridge_bg_mode'] ?? 'gradient');
if (!in_array($bridge_bg_mode, ['gradient', 'solid', 'image'], true)) {
    $bridge_bg_mode = 'gradient';
}
$bridge_card_layout = sanitize_key($_POST['bridge_card_layout'] ?? 'stacked');
if (!in_array($bridge_card_layout, ['stacked', 'grid', 'links'], true)) {
    $bridge_card_layout = 'stacked';
}
        $meta = [
            'template' => $posted_template,
            'slug' => $slug,
            'category' => sanitize_text_field(wp_unslash($_POST['category'] ?? '')),
            'headline' => sanitize_text_field(wp_unslash($_POST['headline'] ?? '')),
            'logo' => esc_url_raw(wp_unslash($_POST['logo'] ?? '')),
            'short_copy' => sanitize_textarea_field(wp_unslash($_POST['short_copy'] ?? '')),
            'top_cta_url' => esc_url_raw(wp_unslash($_POST['top_cta_url'] ?? '')),
            'top_cta_label' => sanitize_text_field(wp_unslash($_POST['top_cta_label'] ?? 'Continue')),
            'secondary_cta_url' => esc_url_raw(wp_unslash($_POST['secondary_cta_url'] ?? '')),
            'secondary_cta_label' => sanitize_text_field(wp_unslash($_POST['secondary_cta_label'] ?? 'Compare options')),
            'cards' => $cards,
            'footer_note' => sanitize_text_field(wp_unslash($_POST['footer_note'] ?? '')),
            'button_delay' => max(0, min(60, absint($_POST['button_delay'] ?? 0))),
            'background_image' => esc_url_raw(wp_unslash($_POST['background_image'] ?? '')),
            'bridge_show_badge' => !empty($_POST['bridge_show_badge']) ? '1' : '0',
'bridge_show_top_buttons' => !empty($_POST['bridge_show_top_buttons']) ? '1' : '0',
'bridge_bg_mode' => $bridge_bg_mode,
'bridge_bg_color' => $color_value('bridge_bg_color', '#0b0620'),
'bridge_bg_color_2' => $color_value('bridge_bg_color_2', '#261044'),
'bridge_bg_image' => esc_url_raw(wp_unslash($_POST['bridge_bg_image'] ?? '')),
'bridge_card_layout' => $bridge_card_layout,
'bridge_title_color' => $color_value('bridge_title_color', '#ffffff'),
'bridge_text_color' => $color_value('bridge_text_color', '#d8def0'),
'bridge_button_bg' => $color_value('bridge_button_bg', '#24d4d9'),
'bridge_button_text' => $color_value('bridge_button_text', '#041526'),
'bridge_card_bg' => $color_value('bridge_card_bg', '#241638'),
'bridge_card_border' => $color_value('bridge_card_border', '#47305f'),
'bridge_card_title' => $color_value('bridge_card_title', '#ffffff'),
'bridge_card_text' => $color_value('bridge_card_text', '#d8def0'),
            'cookie_title' => sanitize_text_field(wp_unslash($_POST['cookie_title'] ?? 'Cookie Policy')),
            'cookie_copy' => sanitize_textarea_field(wp_unslash($_POST['cookie_copy'] ?? '')),
            'cookie_accept_label' => sanitize_text_field(wp_unslash($_POST['cookie_accept_label'] ?? 'Accept')),
            'cookie_accept_url' => esc_url_raw(wp_unslash($_POST['cookie_accept_url'] ?? '')),
            'cookie_close_label' => sanitize_text_field(wp_unslash($_POST['cookie_close_label'] ?? 'Close')),
            'cookie_close_url' => esc_url_raw(wp_unslash($_POST['cookie_close_url'] ?? '')),
            'cookie_policy_label' => sanitize_text_field(wp_unslash($_POST['cookie_policy_label'] ?? 'Privacy Policy')),
            'cookie_policy_url' => esc_url_raw(wp_unslash($_POST['cookie_policy_url'] ?? '')),
            'cookie_modal_bg' => $color_value('cookie_modal_bg', '#f8fafc'),
            'cookie_modal_text' => $color_value('cookie_modal_text', '#111827'),
            'cookie_accept_bg' => $color_value('cookie_accept_bg', '#1683f7'),
            'cookie_accept_text' => $color_value('cookie_accept_text', '#ffffff'),
            'cookie_link_color' => $color_value('cookie_link_color', '#2777c7'),
            'cookie_overlay_opacity' => max(0, min(90, absint($_POST['cookie_overlay_opacity'] ?? 58))),
            'cookie_blur' => max(0, min(12, absint($_POST['cookie_blur'] ?? 2))),
            'vsl_source_type' => 'wp_media',
            'vsl_video_url' => esc_url_raw(wp_unslash($_POST['vsl_video_url'] ?? '')),
            'vsl_preview_video_url' => esc_url_raw(wp_unslash($_POST['vsl_preview_video_url'] ?? '')),
            'vsl_poster_url' => esc_url_raw(wp_unslash($_POST['vsl_poster_url'] ?? '')),
            'vsl_player_bg' => sanitize_hex_color(wp_unslash($_POST['vsl_player_bg'] ?? '#050816')) ?: '#050816',
            'vsl_player_accent' => sanitize_hex_color(wp_unslash($_POST['vsl_player_accent'] ?? '#ff5a1f')) ?: '#ff5a1f',
            'vsl_ctrl_big_play' => isset($_POST['vsl_ctrl_big_play']) ? '1' : '0',
            'vsl_ctrl_small_play' => isset($_POST['vsl_ctrl_small_play']) ? '1' : '0',
            'vsl_ctrl_allow_pause' => isset($_POST['vsl_ctrl_allow_pause']) ? '1' : '0',
            'vsl_ctrl_progress' => '0',
            'vsl_ctrl_time' => isset($_POST['vsl_ctrl_time']) ? '1' : '0',
            'vsl_ctrl_back10' => isset($_POST['vsl_ctrl_back10']) ? '1' : '0',
            'vsl_ctrl_forward10' => isset($_POST['vsl_ctrl_forward10']) ? '1' : '0',
            'vsl_ctrl_volume' => isset($_POST['vsl_ctrl_volume']) ? '1' : '0',
            'vsl_ctrl_fullscreen' => isset($_POST['vsl_ctrl_fullscreen']) ? '1' : '0',
            'vsl_ctrl_speed' => isset($_POST['vsl_ctrl_speed']) ? '1' : '0',
            'vsl_ctrl_color' => sanitize_hex_color(wp_unslash($_POST['vsl_ctrl_color'] ?? '#ffffff')) ?: '#ffffff',
            'vsl_ctrl_bg' => sanitize_hex_color(wp_unslash($_POST['vsl_ctrl_bg'] ?? '#111827')) ?: '#111827',
            'vsl_embed_code' => $this->sanitize_vsl_embed_code(wp_unslash($_POST['vsl_embed_code'] ?? '')),
            'vsl_headline' => sanitize_text_field(wp_unslash($_POST['vsl_headline'] ?? '')),
            'vsl_subheadline' => sanitize_textarea_field(wp_unslash($_POST['vsl_subheadline'] ?? '')),
            'vsl_speed' => sanitize_text_field(wp_unslash($_POST['vsl_speed'] ?? '1.0')),
            'vsl_smart_autoplay' => isset($_POST['vsl_smart_autoplay']) ? '1' : '0',
            'vsl_overlay_template' => (function($tpl){ $tpl = $tpl === 'large' ? 'full' : $tpl; return in_array($tpl, ['wide','square','play','minimal','full','compact','icon_sound','icon_play','icon_blink','custom'], true) ? sanitize_key($tpl) : 'wide'; })(sanitize_key(wp_unslash($_POST['vsl_overlay_template'] ?? 'wide'))),
            'vsl_overlay_top' => sanitize_text_field(wp_unslash($_POST['vsl_overlay_top'] ?? 'Your video has already started')),
            'vsl_overlay_bottom' => sanitize_text_field(wp_unslash($_POST['vsl_overlay_bottom'] ?? 'Click to hear')),
            'vsl_overlay_color' => sanitize_hex_color(wp_unslash($_POST['vsl_overlay_color'] ?? '#111827')) ?: '#111827',
            'vsl_overlay_opacity' => max(20, min(100, (int)($_POST['vsl_overlay_opacity'] ?? 92))),
            'vsl_overlay_text_color' => sanitize_hex_color(wp_unslash($_POST['vsl_overlay_text_color'] ?? '#ffffff')) ?: '#ffffff',
            'vsl_overlay_font' => $this->vsl_sanitize_font_choice($_POST['vsl_overlay_font'] ?? 'Inter'),
            'vsl_overlay_font_woff2' => esc_url_raw(wp_unslash($_POST['vsl_overlay_font_woff2'] ?? '')),
            'vsl_overlay_image' => esc_url_raw(wp_unslash($_POST['vsl_overlay_image'] ?? '')),
            'vsl_overlay_x' => max(0, min(100, (float)($_POST['vsl_overlay_x'] ?? 50))),
            'vsl_overlay_y' => max(0, min(100, (float)($_POST['vsl_overlay_y'] ?? 50))),
            'vsl_overlay_width' => max(8, min(500, (float)($_POST['vsl_overlay_width'] ?? 42))),
            'vsl_overlay_rotate' => max(-360, min(360, (float)($_POST['vsl_overlay_rotate'] ?? 0))),
            // v0.18.158: save the real mobile Smart Autoplay geometry when present.
            // Older builds accidentally copied the desktop fields into the mobile fields on every save,
            // which could make the final mobile render disagree with the preview/canvas state.
            'vsl_overlay_mobile_x' => max(0, min(100, (float)($_POST['vsl_overlay_mobile_x'] ?? ($_POST['vsl_overlay_x'] ?? 50)))),
            'vsl_overlay_mobile_y' => max(0, min(100, (float)($_POST['vsl_overlay_mobile_y'] ?? ($_POST['vsl_overlay_y'] ?? 50)))),
            'vsl_overlay_mobile_width' => max(8, min(500, (float)($_POST['vsl_overlay_mobile_width'] ?? ($_POST['vsl_overlay_width'] ?? 42)))),
            'vsl_overlay_mobile_rotate' => max(-360, min(360, (float)($_POST['vsl_overlay_mobile_rotate'] ?? ($_POST['vsl_overlay_rotate'] ?? 0)))),
            'vsl_overlay_pulse' => isset($_POST['vsl_overlay_pulse']) ? '1' : '0',
            'vsl_overlay_pulse_speed' => max(0.60, min(3.00, (float)($_POST['vsl_overlay_pulse_speed'] ?? 1.30))),
            'vsl_overlay_pulse_mode' => in_array(($_POST['vsl_overlay_pulse_mode'] ?? 'scale'), ['scale','soft','glow','premium','heartbeat','breathe','bounce','flash','shake'], true) ? sanitize_key($_POST['vsl_overlay_pulse_mode']) : 'scale',
            'vsl_interaction_sound' => isset($_POST['vsl_interaction_sound']) ? '1' : '0',
            'vsl_bg_start' => max(0, min(7200, absint($_POST['vsl_bg_start'] ?? 0))),
            'vsl_bg_end' => max(0, min(7200, absint($_POST['vsl_bg_end'] ?? 0))),
            'vsl_native_controls' => (!empty($_POST['vsl_native_controls']) && $_POST['vsl_native_controls'] === '1') ? '1' : '0',
            'vsl_click_pause' => (!empty($_POST['vsl_ctrl_allow_pause']) || (!empty($_POST['vsl_click_pause']) && $_POST['vsl_click_pause'] === '1')) ? '1' : '0',
            'vsl_progress_on' => isset($_POST['vsl_progress_on']) ? '1' : '0',
            'vsl_progress_use_cta_color' => isset($_POST['vsl_progress_use_cta_color']) ? '1' : '0',
            'vsl_progress_type' => in_array(($_POST['vsl_progress_type'] ?? 'smart'), ['real','smart'], true) ? sanitize_key($_POST['vsl_progress_type']) : 'smart',
            'vsl_progress_color' => sanitize_hex_color(wp_unslash($_POST['vsl_progress_color'] ?? '#ff5a1f')) ?: '#ff5a1f',
            'vsl_progress_height' => max(2, min(30, absint($_POST['vsl_progress_height'] ?? 6))),
            'vsl_cta_on' => isset($_POST['vsl_cta_on']) ? '1' : '0',
            'vsl_cta_text' => sanitize_text_field(wp_unslash($_POST['vsl_cta_text'] ?? 'Continue')),
            'vsl_cta_url' => esc_url_raw(wp_unslash($_POST['vsl_cta_url'] ?? '')),
            'vsl_cta_action' => in_array(($_POST['vsl_cta_action'] ?? 'url'), ['url','lead','lead_redirect'], true) ? sanitize_key($_POST['vsl_cta_action']) : 'url',
            'vsl_lead_title' => sanitize_text_field(wp_unslash($_POST['vsl_lead_title'] ?? 'Get the next step by email')),
            'vsl_lead_copy' => sanitize_textarea_field(wp_unslash($_POST['vsl_lead_copy'] ?? 'Enter your email below to continue.')),
            'vsl_lead_email_placeholder' => sanitize_text_field(wp_unslash($_POST['vsl_lead_email_placeholder'] ?? 'you@example.com')),
            'vsl_lead_button_text' => sanitize_text_field(wp_unslash($_POST['vsl_lead_button_text'] ?? 'Continue by email')),
            'vsl_lead_success' => sanitize_text_field(wp_unslash($_POST['vsl_lead_success'] ?? 'Thanks. Your email was received.')),
            'vsl_lead_name_on' => isset($_POST['vsl_lead_name_on']) ? '1' : '0',
            'vsl_lead_phone_on' => isset($_POST['vsl_lead_phone_on']) ? '1' : '0',
            'vsl_lead_name_placeholder' => sanitize_text_field(wp_unslash($_POST['vsl_lead_name_placeholder'] ?? 'Full name')),
            'vsl_lead_phone_placeholder' => sanitize_text_field(wp_unslash($_POST['vsl_lead_phone_placeholder'] ?? 'Phone number')),
            'vsl_lead_bg' => sanitize_hex_color(wp_unslash($_POST['vsl_lead_bg'] ?? '#050816')) ?: '#050816',
            'vsl_lead_text_color' => sanitize_hex_color(wp_unslash($_POST['vsl_lead_text_color'] ?? '#ffffff')) ?: '#ffffff',
            'vsl_lead_button_bg' => sanitize_hex_color(wp_unslash($_POST['vsl_lead_button_bg'] ?? '#16c653')) ?: '#16c653',
            'vsl_lead_button_text_color' => sanitize_hex_color(wp_unslash($_POST['vsl_lead_button_text_color'] ?? '#02120a')) ?: '#02120a',
            'vsl_lead_title_color' => sanitize_hex_color(wp_unslash($_POST['vsl_lead_title_color'] ?? '#ffffff')) ?: '#ffffff',
            'vsl_lead_close_color' => sanitize_hex_color(wp_unslash($_POST['vsl_lead_close_color'] ?? '#ffffff')) ?: '#ffffff',
            'vsl_lead_close_bg' => sanitize_hex_color(wp_unslash($_POST['vsl_lead_close_bg'] ?? '#060b14')) ?: '#060b14',
            'vsl_lead_bg_image' => esc_url_raw(wp_unslash($_POST['vsl_lead_bg_image'] ?? '')),
            'vsl_lead_radius' => max(0, min(42, absint($_POST['vsl_lead_radius'] ?? 24))),
            'vsl_lead_close_style' => in_array(($_POST['vsl_lead_close_style'] ?? 'round'), ['round','plain'], true) ? sanitize_key($_POST['vsl_lead_close_style']) : 'round',
            'vsl_cta_image' => esc_url_raw(wp_unslash($_POST['vsl_cta_image'] ?? '')),
            'vsl_cta_x' => max(0, min(100, (float)($_POST['vsl_cta_x'] ?? 50))),
            'vsl_cta_y' => max(0, min(100, (float)($_POST['vsl_cta_y'] ?? 82))),
            'vsl_cta_width' => max(8, min(100, (float)($_POST['vsl_cta_width'] ?? 34))),
            'vsl_cta_delay' => max(0, min(7200, absint($_POST['vsl_cta_delay'] ?? 10))),
            'vsl_pitch_time' => max(0, min(7200, absint($_POST['vsl_pitch_time'] ?? 0))),
            'vsl_cta_position' => in_array(($_POST['vsl_cta_position'] ?? 'below'), ['below','inside'], true) ? sanitize_key($_POST['vsl_cta_position']) : 'below',
            'vsl_cta_color' => sanitize_hex_color(wp_unslash($_POST['vsl_cta_color'] ?? '#16a34a')) ?: '#16a34a',
            'vsl_cta_hover_color' => sanitize_hex_color(wp_unslash($_POST['vsl_cta_hover_color'] ?? '#15803d')) ?: '#15803d',
            'vsl_cta_end' => max(0, min(7200, absint($_POST['vsl_cta_end'] ?? 0))),
            'vsl_cta_font' => in_array(($_POST['vsl_cta_font'] ?? 'Inter'), ['Inter','Poppins','Montserrat','Roboto','Open Sans','Oswald','Bebas Neue','Arial','system'], true) ? sanitize_text_field(wp_unslash($_POST['vsl_cta_font'])) : 'Inter',
            'vsl_cta_font_woff2' => esc_url_raw(wp_unslash($_POST['vsl_cta_font_woff2'] ?? '')),
            'vsl_cta_animation' => in_array(($_POST['vsl_cta_animation'] ?? 'premium'), ['premium','heartbeat','breathe','bounce','flash','shake'], true) ? sanitize_key($_POST['vsl_cta_animation']) : 'premium',
            'vsl_cta_weight' => in_array(($_POST['vsl_cta_weight'] ?? '900'), ['400','600','700','900'], true) ? sanitize_text_field(wp_unslash($_POST['vsl_cta_weight'])) : '900',
            'vsl_cta_font_size' => max(10, min(48, absint($_POST['vsl_cta_font_size'] ?? 16))),
            'vsl_cta_radius' => max(0, min(999, absint($_POST['vsl_cta_radius'] ?? 999))),
            'vsl_cta_padding_y' => max(4, min(60, absint($_POST['vsl_cta_padding_y'] ?? 14))),
            'vsl_cta_padding_x' => max(8, min(120, absint($_POST['vsl_cta_padding_x'] ?? 28))),
            'vsl_cta_text_color' => sanitize_hex_color(wp_unslash($_POST['vsl_cta_text_color'] ?? '#ffffff')) ?: '#ffffff',
            'vsl_cta_text_hover_color' => sanitize_hex_color(wp_unslash($_POST['vsl_cta_text_hover_color'] ?? '#ffffff')) ?: '#ffffff',
            'vsl_cta_pulse' => isset($_POST['vsl_cta_pulse']) ? '1' : '0',
            'vsl_cta_pulse_speed' => max(0.60, min(3.00, (float)($_POST['vsl_cta_pulse_speed'] ?? 1.45))),
            'vsl_cta_shadow' => isset($_POST['vsl_cta_shadow']) ? '1' : '0',
            'vsl_cta_autoscroll' => isset($_POST['vsl_cta_autoscroll']) ? '1' : '0',
            'vsl_cta_remember' => isset($_POST['vsl_cta_remember']) ? '1' : '0',
            'vsl_pause_on' => isset($_POST['vsl_pause_on']) ? '1' : '0',
            'vsl_pause_name' => sanitize_text_field(wp_unslash($_POST['vsl_pause_name'] ?? 'Thumb Recovery')),
            'vsl_pause_image' => esc_url_raw(wp_unslash($_POST['vsl_pause_image'] ?? '')),
            'vsl_pause_start' => max(0, min(7200, absint($_POST['vsl_pause_start'] ?? 0))),
            'vsl_pause_end' => max(0, min(7200, absint($_POST['vsl_pause_end'] ?? 0))),
            'vsl_pause_trigger' => in_array(($_POST['vsl_pause_trigger'] ?? 'pause'), ['pause','end','both'], true) ? sanitize_key($_POST['vsl_pause_trigger']) : 'pause',
            'vsl_pause_button_on' => isset($_POST['vsl_pause_button_on']) ? '1' : '0',
            'vsl_pause_button_text' => sanitize_text_field(wp_unslash($_POST['vsl_pause_button_text'] ?? 'Continue')),
            'vsl_pause_button_url' => esc_url_raw(wp_unslash($_POST['vsl_pause_button_url'] ?? '')),
            'vsl_pause_button_size' => in_array(($_POST['vsl_pause_button_size'] ?? 'medium'), ['small','medium','large'], true) ? sanitize_key($_POST['vsl_pause_button_size']) : 'medium',
            'vsl_pause_button_position' => in_array(($_POST['vsl_pause_button_position'] ?? 'bottom'), ['top','center','bottom'], true) ? sanitize_key($_POST['vsl_pause_button_position']) : 'bottom',
            'vsl_pause_button_text_color' => sanitize_hex_color(wp_unslash($_POST['vsl_pause_button_text_color'] ?? '#ffffff')) ?: '#ffffff',
            'vsl_pause_button_color' => sanitize_hex_color(wp_unslash($_POST['vsl_pause_button_color'] ?? '#2563eb')) ?: '#2563eb',
            'vsl_pause_button_hover_color' => sanitize_hex_color(wp_unslash($_POST['vsl_pause_button_hover_color'] ?? '#1d4ed8')) ?: '#1d4ed8',
            'vsl_pause_font' => $this->vsl_sanitize_font_choice($_POST['vsl_pause_font'] ?? 'Inter'),
            'vsl_pause_font_woff2' => esc_url_raw(wp_unslash($_POST['vsl_pause_font_woff2'] ?? '')),
            'vsl_hook_on' => isset($_POST['vsl_hook_on']) ? '1' : '0',
            'vsl_hook_name' => sanitize_text_field(wp_unslash($_POST['vsl_hook_name'] ?? 'Mini Hook')),
            'vsl_hook_text' => sanitize_text_field(wp_unslash($_POST['vsl_hook_text'] ?? '')),
            'vsl_hook_font' => $this->vsl_sanitize_font_choice($_POST['vsl_hook_font'] ?? 'Inter'),
            'vsl_hook_font_woff2' => esc_url_raw(wp_unslash($_POST['vsl_hook_font_woff2'] ?? '')),
            'vsl_hook_image' => esc_url_raw(wp_unslash($_POST['vsl_hook_image'] ?? '')),
            'vsl_hook_start' => max(0, min(7200, absint($_POST['vsl_hook_start'] ?? 0))),
            'vsl_hook_end' => max(0, min(7200, absint($_POST['vsl_hook_end'] ?? 15))),
            'vsl_hook_position' => in_array(($_POST['vsl_hook_position'] ?? 'bottom_left'), ['bottom_left','bottom_right','top_right','top_left'], true) ? sanitize_key($_POST['vsl_hook_position']) : 'bottom_left',
            'vsl_hook_opacity' => max(10, min(100, absint($_POST['vsl_hook_opacity'] ?? 100))),
            'vsl_hook_x' => max(0, min(100, (float)($_POST['vsl_hook_x'] ?? 4))),
            'vsl_hook_y' => max(0, min(100, (float)($_POST['vsl_hook_y'] ?? 82))),
            'vsl_hook_width' => max(8, min(100, (float)($_POST['vsl_hook_width'] ?? 32))),
            'vsl_resume_on' => isset($_POST['vsl_resume_on']) ? '1' : '0',
            'vsl_resume_message' => sanitize_textarea_field(wp_unslash($_POST['vsl_resume_message'] ?? '')),
            'vsl_resume_continue_text' => sanitize_text_field(wp_unslash($_POST['vsl_resume_continue_text'] ?? '')),
            'vsl_resume_restart_text' => sanitize_text_field(wp_unslash($_POST['vsl_resume_restart_text'] ?? '')),
            'vsl_resume_text_color' => sanitize_hex_color($_POST['vsl_resume_text_color'] ?? '#ffffff') ?: '#ffffff',
            'vsl_resume_bg' => sanitize_hex_color($_POST['vsl_resume_bg'] ?? '#581c87') ?: '#581c87',
            'vsl_resume_font' => $this->vsl_sanitize_font_choice($_POST['vsl_resume_font'] ?? 'Inter'),
            'vsl_resume_font_woff2' => esc_url_raw(wp_unslash($_POST['vsl_resume_font_woff2'] ?? '')),
            'landing_niche' => sanitize_key($_POST['landing_niche'] ?? 'custom'),
            'landing_template' => sanitize_key($_POST['landing_template'] ?? 'vsl_sales'),
            'landing_headline' => sanitize_text_field(wp_unslash($_POST['landing_headline'] ?? '')),
            'landing_copy' => sanitize_textarea_field(wp_unslash($_POST['landing_copy'] ?? '')),
            'landing_button_text' => sanitize_text_field(wp_unslash($_POST['landing_button_text'] ?? 'Continue')),
            'landing_button_url' => esc_url_raw(wp_unslash($_POST['landing_button_url'] ?? '')),
            'elementor_landing_template' => sanitize_key(wp_unslash($_POST['elementor_landing_template'] ?? '')),
            'elementor_landing_title' => sanitize_text_field(wp_unslash($_POST['elementor_landing_title'] ?? '')),
            'elementor_landing_slug' => sanitize_title(wp_unslash($_POST['elementor_landing_slug'] ?? '')),
            'elementor_landing_position' => in_array(($_POST['elementor_landing_position'] ?? 'after_first'), ['top','after_first','bottom'], true) ? sanitize_key($_POST['elementor_landing_position']) : 'after_first',
            'elementor_landing_autocreate' => isset($_POST['elementor_landing_autocreate']) ? '1' : '0',
            'elementor_landing_mode' => in_array(($_POST['elementor_landing_mode'] ?? ''), ['create','keep','regenerate','copy'], true) ? sanitize_key($_POST['elementor_landing_mode']) : '',
            'elementor_landing_page_id' => absint($_POST['elementor_landing_page_id'] ?? 0),
            'meta_description' => sanitize_text_field(wp_unslash($_POST['meta_description'] ?? '')),
            'custom_head' => current_user_can('unfiltered_html') ? wp_unslash($_POST['custom_head'] ?? '') : sanitize_textarea_field(wp_unslash($_POST['custom_head'] ?? '')),
            'custom_footer' => current_user_can('unfiltered_html') ? wp_unslash($_POST['custom_footer'] ?? '') : sanitize_textarea_field(wp_unslash($_POST['custom_footer'] ?? '')),
            'custom_pixels' => $this->sanitize_custom_pixels_from_post(),
            'conversion_events' => $this->sanitize_conversion_events_from_post(),
        ];
        if (($meta['template'] ?? '') === 'vsl_landing') {
            $created_landing_id = $this->maybe_create_elementor_landing_page($post_id, $meta);
            if ($created_landing_id) {
                $meta['elementor_landing_page_id'] = (int)$created_landing_id;
                $actual_landing_slug = (string)get_post_field('post_name', $created_landing_id);
                if ($actual_landing_slug !== '') {
                    $meta['elementor_landing_slug'] = $actual_landing_slug;
                }
            }
            // After a save, return the automation action to the safe default so Copy/Regenerate cannot repeat accidentally on the next save.
            $meta['elementor_landing_mode'] = 'keep';
        }
        update_post_meta($post_id, '_itp_meta', $meta);
        $active_tab = sanitize_key(wp_unslash($_POST['itp_active_vsl_tab'] ?? 'video'));
        $allowed_tabs = ['video','style','autoplay','turbo','cta','pause','hook','resume','pixels','embed'];
        if (!in_array($active_tab, $allowed_tabs, true)) $active_tab = 'video';
        wp_safe_redirect(admin_url('admin.php?page=itp-create&id=' . $post_id . '&saved=1&itp_tab=' . rawurlencode($active_tab)));
        exit;
    }

    private function handle_settings_save() {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST' || empty($_POST['itp_nonce'])) return;
        if (!current_user_can('manage_options')) return;
        check_admin_referer('itp_save_settings', 'itp_nonce');
        $old = $this->settings();
        $settings = $old;
        $settings['base_slug'] = '';
        $settings['brand_name'] = sanitize_text_field(wp_unslash($_POST['brand_name'] ?? 'Invalid Traffic'));
        $settings['plugin_language'] = $this->sanitize_plugin_language(wp_unslash($_POST['plugin_language'] ?? 'auto'));
        $settings['button_delay'] = (string)max(0, min(60, absint($_POST['button_delay'] ?? 0)));
        $settings['engaged_seconds'] = (string)max(3, min(120, absint($_POST['engaged_seconds'] ?? 10)));
        $settings['engaged_scroll'] = (string)max(5, min(100, absint($_POST['engaged_scroll'] ?? 30)));
        $settings['fast_click_seconds'] = (string)max(1, min(20, absint($_POST['fast_click_seconds'] ?? 2)));
        $settings['repeat_visit_limit'] = (string)max(2, min(100, absint($_POST['repeat_visit_limit'] ?? 8)));
        $settings['repeat_window_minutes'] = (string)max(5, min(1440, absint($_POST['repeat_window_minutes'] ?? 60)));
        $settings['retention_days'] = (string)max(7, min(365, absint($_POST['retention_days'] ?? 30)));
        $settings['emergency_mode'] = isset($_POST['emergency_mode']) ? '1' : '0';
        $settings['powered_by'] = isset($_POST['powered_by']) ? '1' : '0';
        update_option(self::OPT, $settings, false);
        if (($old['base_slug'] ?? '') !== $settings['base_slug']) {
            update_option('itp_flush_rewrite', '1', false);
        }
        wp_safe_redirect(admin_url('admin.php?page=itp-settings&saved=1'));
        exit;
    }

    private function bridge_meta($id) {
        $meta = get_post_meta($id, '_itp_meta', true);
        if (!is_array($meta)) $meta = [];
        $merged = array_merge($this->empty_bridge_meta(), $meta);
        $merged['slug'] = get_post_field('post_name', $id) ?: $merged['slug'];
        return $merged;
    }

    private function empty_bridge_meta() {
        return [
            'title' => '',
            'template' => 'blue_cards',
            'slug' => '',
            'category' => '',
            'headline' => 'Choose where to continue',
            'logo' => '',
            'short_copy' => 'Select the option that matches what you came to see.',
            'top_cta_url' => '',
            'top_cta_label' => 'Continue',
            'secondary_cta_url' => '',
            'secondary_cta_label' => 'Compare options',
            'cards' => $this->sample_cards(),
            'footer_note' => '',
            'button_delay' => '',
            'background_image' => '',
            'bridge_show_badge' => '0',
            'bridge_show_top_buttons' => '1',
'bridge_bg_mode' => 'gradient',
'bridge_bg_color' => '#0b0620',
'bridge_bg_color_2' => '#261044',
'bridge_bg_image' => '',
'bridge_card_layout' => 'stacked',
'bridge_title_color' => '#ffffff',
'bridge_text_color' => '#d8def0',
'bridge_button_bg' => '#24d4d9',
'bridge_button_text' => '#041526',
'bridge_card_bg' => '#241638',
'bridge_card_border' => '#47305f',
'bridge_card_title' => '#ffffff',
'bridge_card_text' => '#d8def0',
            'cookie_title' => 'Cookie Policy',
            'cookie_copy' => 'This page uses cookies to personalize content, analyze visits and improve your experience. By clicking Accept, you agree to continue.',
            'cookie_accept_label' => 'Accept',
            'cookie_accept_url' => '',
            'cookie_close_label' => 'Close',
            'cookie_close_url' => '',
            'cookie_policy_label' => 'Privacy Policy',
            'cookie_policy_url' => '',
            'cookie_modal_bg' => '#f8fafc',
            'cookie_modal_text' => '#111827',
            'cookie_accept_bg' => '#1683f7',
            'cookie_accept_text' => '#ffffff',
            'cookie_link_color' => '#2777c7',
            'cookie_overlay_opacity' => 58,
            'cookie_blur' => 2,
            'cookie_modal_bg' => '#f8fafc',
            'cookie_modal_text' => '#111827',
            'cookie_accept_bg' => '#1683f7',
            'cookie_accept_text' => '#ffffff',
            'cookie_link_color' => '#2777c7',
            'cookie_overlay_opacity' => 58,
            'cookie_blur' => 2,
            'vsl_source_type' => 'wp_media',
            'vsl_video_url' => '',
            'vsl_preview_video_url' => '',
            'vsl_poster_url' => '',
            'vsl_player_bg' => '#050816',
            'vsl_player_accent' => '#ff5a1f',
            'vsl_ctrl_big_play' => '0',
            'vsl_ctrl_small_play' => '0',
            'vsl_ctrl_allow_pause' => '0',
            'vsl_ctrl_progress' => '0',
            'vsl_ctrl_time' => '0',
            'vsl_ctrl_back10' => '0',
            'vsl_ctrl_forward10' => '0',
            'vsl_ctrl_volume' => '0',
            'vsl_ctrl_fullscreen' => '0',
            'vsl_ctrl_speed' => '0',
            'vsl_ctrl_color' => '#ffffff',
            'vsl_ctrl_bg' => '#111827',
            'vsl_embed_code' => '',
            'vsl_headline' => 'Watch this presentation before you continue',
            'vsl_subheadline' => 'This video bridge tracks play, CTA and traffic-quality signals before the next step.',
            'vsl_speed' => '1.0',
            'vsl_smart_autoplay' => '1',
            'vsl_overlay_template' => 'wide',
            'vsl_overlay_top' => 'Your video has already started',
            'vsl_overlay_bottom' => 'Click to hear',
            'vsl_overlay_color' => '#111827',
            'vsl_overlay_opacity' => 92,
            'vsl_overlay_text_color' => '#ffffff',
            'vsl_overlay_font' => 'Inter',
            'vsl_overlay_font_woff2' => '',
            'vsl_overlay_image' => '',
            'vsl_overlay_x' => 50,
            'vsl_overlay_y' => 50,
            'vsl_overlay_width' => 42,
            'vsl_overlay_rotate' => 0,
            'vsl_overlay_mobile_x' => 50,
            'vsl_overlay_mobile_y' => 50,
            'vsl_overlay_mobile_width' => 42,
            'vsl_overlay_mobile_rotate' => 0,
            'vsl_overlay_pulse' => '0',
            'vsl_overlay_pulse_speed' => '1.30',
            'vsl_overlay_pulse_mode' => 'scale',
            'vsl_interaction_sound' => '0',
            'vsl_bg_start' => 0,
            'vsl_bg_end' => 15,
            'vsl_native_controls' => '0',
            'vsl_click_pause' => '0',
            'vsl_progress_on' => '1',
            'vsl_progress_use_cta_color' => '0',
            'vsl_progress_type' => 'smart',
            'vsl_progress_color' => '#ff5a1f',
            'vsl_progress_height' => 6,
            'vsl_cta_on' => '0',
            'vsl_cta_text' => 'Continue',
            'vsl_cta_url' => '',
            'vsl_cta_action' => 'url',
            'vsl_lead_title' => 'Get the next step by email',
            'vsl_lead_copy' => 'Enter your email below to continue.',
            'vsl_lead_email_placeholder' => 'you@example.com',
            'vsl_lead_button_text' => 'Continue by email',
            'vsl_lead_success' => 'Thanks. Your email was received.',
            'vsl_lead_name_on' => '0',
            'vsl_lead_phone_on' => '0',
            'vsl_lead_name_placeholder' => 'Full name',
            'vsl_lead_phone_placeholder' => 'Phone number',
            'vsl_lead_bg' => '#050816',
            'vsl_lead_text_color' => '#ffffff',
            'vsl_lead_button_bg' => '#16c653',
            'vsl_lead_button_text_color' => '#02120a',
            'vsl_lead_title_color' => '#ffffff',
            'vsl_lead_close_color' => '#ffffff',
            'vsl_lead_close_bg' => '#060b14',
            'vsl_lead_bg_image' => '',
            'vsl_lead_radius' => 24,
            'vsl_lead_close_style' => 'round',
            'vsl_cta_image' => '',
            'vsl_cta_x' => 50,
            'vsl_cta_y' => 82,
            'vsl_cta_width' => 34,
            'vsl_cta_delay' => 10,
            'vsl_pitch_time' => 0,
            'vsl_cta_position' => 'below',
            'vsl_cta_color' => '#16a34a',
            'vsl_cta_hover_color' => '#15803d',
            'vsl_cta_end' => 0,
            'vsl_cta_font' => 'Inter',
            'vsl_cta_font_woff2' => '',
            'vsl_cta_animation' => 'premium',
            'vsl_cta_weight' => '900',
            'vsl_cta_font_size' => 16,
            'vsl_cta_radius' => 999,
            'vsl_cta_padding_y' => 14,
            'vsl_cta_padding_x' => 28,
            'vsl_cta_text_color' => '#ffffff',
            'vsl_cta_text_hover_color' => '#ffffff',
            'vsl_cta_pulse' => '1',
            'vsl_cta_pulse_speed' => '1.45',
            'vsl_cta_shadow' => '1',
            'vsl_cta_autoscroll' => '0',
            'vsl_cta_remember' => '0',
            'vsl_pause_on' => '0',
            'vsl_pause_name' => 'Thumb Recovery',
            'vsl_pause_image' => '',
            'vsl_pause_start' => 0,
            'vsl_pause_end' => 0,
            'vsl_pause_trigger' => 'pause',
            'vsl_pause_button_on' => '0',
            'vsl_pause_button_text' => 'Continue',
            'vsl_pause_button_url' => '',
            'vsl_pause_button_size' => 'medium',
            'vsl_pause_button_position' => 'bottom',
            'vsl_pause_button_text_color' => '#ffffff',
            'vsl_pause_button_color' => '#2563eb',
            'vsl_pause_button_hover_color' => '#1d4ed8',
            'vsl_pause_font' => 'Inter',
            'vsl_pause_font_woff2' => '',
            'vsl_hook_on' => '0',
            'vsl_hook_name' => 'Mini Hook',
            'vsl_hook_text' => 'Wait — this next part matters...',
            'vsl_hook_font' => 'Inter',
            'vsl_hook_font_woff2' => '',
            'vsl_hook_image' => '',
            'vsl_hook_start' => 0,
            'vsl_hook_end' => 15,
            'vsl_hook_position' => 'bottom_left',
            'vsl_hook_opacity' => 100,
            'vsl_hook_x' => 4,
            'vsl_hook_y' => 82,
            'vsl_hook_width' => 32,
            'vsl_resume_on' => '0',
            'vsl_resume_message' => 'You already started watching this video',
            'vsl_resume_continue_text' => 'Continue watching?',
            'vsl_resume_restart_text' => 'Start from the beginning?',
            'vsl_resume_text_color' => '#ffffff',
            'vsl_resume_bg' => '#581c87',
            'vsl_resume_font' => 'Inter',
            'vsl_resume_font_woff2' => '',
            'landing_niche' => 'custom',
            'landing_template' => 'vsl_sales',
            'landing_headline' => 'A ready-to-edit campaign page',
            'landing_copy' => 'Use this clean page as a first layer for paid traffic, VSL campaigns, affiliate offers or lead capture.',
            'landing_button_text' => 'Continue',
            'landing_button_url' => '',
            'elementor_landing_template' => '',
            'elementor_landing_title' => '',
            'elementor_landing_slug' => '',
            'elementor_landing_position' => 'after_first',
            'elementor_landing_autocreate' => '1',
            'elementor_landing_mode' => '',
            'elementor_landing_page_id' => 0,
            'meta_description' => '',
            'custom_head' => '',
            'custom_footer' => '',
            'custom_pixels' => [],
            'conversion_events' => [],
        ];
    }

    private function demo_meta() {
        return [
            'title' => 'Demo Bridge',
            'template' => 'blue_cards',
            'slug' => 'demo-bridge',
            'category' => 'Native Ads',
            'headline' => 'Choose the story you want to read next',
            'short_copy' => 'Pick the option that matches what you came to see. No ads are shown on this first step.',
            'top_cta_url' => home_url('/'),
            'top_cta_label' => 'Read more',
            'background_image' => '',
            'cookie_title' => 'Cookie Policy',
            'cookie_copy' => 'This page uses cookies to personalize content, analyze visits and improve your experience. By clicking Accept, you agree to continue.',
            'cookie_accept_label' => 'Accept',
            'cookie_accept_url' => '',
            'cookie_close_label' => 'Close',
            'cookie_close_url' => '',
            'cookie_policy_label' => 'Privacy Policy',
            'cookie_policy_url' => '',
            'cards' => $this->sample_cards(),
        ];
    }

    private function demo_cookie_meta() {
        return [
            'title' => 'Cookie Modal Demo',
            'template' => 'cookie_modal',
            'slug' => 'cookie-modal-demo',
            'category' => 'Cookie Bridge',
            'headline' => 'Cookie Policy',
            'top_cta_url' => home_url('/'),
            'background_image' => home_url('/'),
            'footer_note' => '',
            'cookie_title' => 'Cookie Policy',
            'cookie_copy' => 'This page uses cookies to personalize content, analyze visits and improve your experience. By clicking Accept, you agree to continue.',
            'cookie_accept_label' => 'Accept',
            'cookie_accept_url' => '',
            'cookie_close_label' => 'Close',
            'cookie_close_url' => '',
            'cookie_policy_label' => 'Privacy Policy',
            'cookie_policy_url' => '',
            'cards' => [],
        ];
    }

    private function sample_cards() {
        $image = $this->brand_logo_url();
        return [[
            'title' => 'Product 1',
            'text' => 'Short product copy with one clear benefit and a simple reason to continue.',
            'button' => 'View product',
            'url' => home_url('/'),
            'image' => $image,
        ], [
            'title' => 'Product 2',
            'text' => 'Second destination preview for an offer, article, guide or comparison page.',
            'button' => 'See details',
            'url' => home_url('/'),
            'image' => $image,
        ], [
            'title' => 'Product 3',
            'text' => 'Third sample card so the bridge opens with a complete premium-looking preview.',
            'button' => 'Continue',
            'url' => home_url('/'),
            'image' => $image,
        ]];
    }

    private function empty_card() {
        return ['title' => '', 'text' => '', 'button' => 'Read more', 'url' => '', 'image' => ''];
    }

    private function templates() {
        return [
            'blue_cards' => ['name' => 'Blue Cards', 'tag' => 'Universal', 'desc' => 'Dark blue bridge with premium cards. Good for traffic arbitrage and paid traffic hubs.'],
            'news_cards' => ['name' => 'News Cards', 'tag' => 'Native ads', 'desc' => 'Article/news-list style with image, headline, summary and button.'],
            'link_hub' => ['name' => 'Link Hub', 'tag' => 'Bio links', 'desc' => 'Linktree-style page with avatar/logo and stacked buttons.'],
            'offer_grid' => ['name' => 'Offer Grid', 'tag' => 'Affiliates', 'desc' => 'Product/affiliate grid with stronger CTA buttons.'],
            'review_clean' => ['name' => 'Review Clean', 'tag' => 'Review', 'desc' => 'Editorial review-style page with clean cards and white background.'],
            'segment_choice' => ['name' => 'Segment Choice', 'tag' => '2 paths', 'desc' => 'Two-path bridge for segmentation before sending users to the final page.'],
            'cookie_modal' => ['name' => 'Cookie Modal', 'tag' => 'Cookie bridge', 'desc' => 'Landing-page screenshot with a cookie-style popup. Accept, close and inline policy link can forward to the destination.'],
            'vsl_video' => ['name' => 'VSL / Video', 'tag' => 'Player studio', 'desc' => 'Native WordPress Media video with smart autoplay, CTA delay, pause image, mini hook, embed and video analytics.'],
            'vsl_landing' => ['name' => 'VSL + Landing Page', 'tag' => 'Video + page', 'desc' => 'Create a VSL player and place it inside a ready landing page template.'],
            'landing_page' => ['name' => 'Landing Page Only', 'tag' => 'Templates', 'desc' => 'Use a ready page template with tracking. Add or edit copy later in the page/template flow.'],
        ];
    }

    private function template_label($key) {
        $templates = $this->templates();
        return $templates[$key]['name'] ?? 'Blue Cards';
    }

    private function cta_presets() {
        return ['Continue', 'Read more', 'Learn more', 'See details', 'View offer', 'Compare options', 'Start now', 'Get access', 'Open page', 'Shop now'];
    }

    private function preview_url($slug) {
        $settings = $this->settings();
        $base = trim((string)$settings['base_slug'], '/');
        $path = $base ? '/' . $base . '/' . trim($slug, '/') . '/' : '/' . trim($slug, '/') . '/';
        return home_url($path);
    }

    private function duplicate_bridge($id) {
        $post = get_post($id);
        $meta = $this->bridge_meta($id);
        $new_slug = wp_unique_post_slug($post->post_name . '-copy', 0, 'publish', self::CPT, 0);
        $new_id = wp_insert_post([
            'post_type' => self::CPT,
            'post_status' => 'publish',
            'post_title' => $post->post_title . ' Copy',
            'post_name' => $new_slug,
        ]);
        $meta['slug'] = $new_slug;
        update_post_meta($new_id, '_itp_meta', $meta);
        return $new_id;
    }

    public function force_no_ads_on_bridge_pages() {
        if (!is_singular(self::CPT)) {
            return;
        }

        if (!defined('DONOTCACHEPAGE')) {
            define('DONOTCACHEPAGE', true);
        }
        if (!defined('DONOTCACHEOBJECT')) {
            define('DONOTCACHEOBJECT', true);
        }
        if (!defined('DONOTCACHEDB')) {
            define('DONOTCACHEDB', true);
        }

        if (!headers_sent()) {
            header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
            header('Pragma: no-cache');
            header('Expires: 0');
            header('Vary: Cookie');
            header('X-Invalid-Traffic-Bridge: no-ads');
        }

        ob_start(function ($html) {
            return $this->strip_adsense_from_bridge_html($html);
        });
    }

    public function strip_ad_scripts_on_bridge_pages($tag, $handle, $src) {
        if (!is_singular(self::CPT)) {
            return $tag;
        }

        return $this->is_ad_script_src($src) ? '' : $tag;
    }

    private function is_ad_script_src($src) {
        $src = (string) $src;
        return (
            stripos($src, 'pagead2.googlesyndication.com') !== false ||
            stripos($src, 'googlesyndication.com') !== false ||
            stripos($src, 'googletagservices.com') !== false ||
            stripos($src, 'doubleclick.net') !== false ||
            stripos($src, 'securepubads.g.doubleclick.net') !== false
        );
    }

    private function strip_adsense_from_bridge_html($html) {
        if ($html === '') {
            return $html;
        }

        $patterns = [
            '#<script[^>]+pagead2\\.googlesyndication\\.com/pagead/js/adsbygoogle\\.js[^>]*></script>#i',
            '#<script[^>]+googlesyndication\\.com[^>]*></script>#i',
            '#<script[^>]+googletagservices\\.com[^>]*></script>#i',
            '#<script[^>]+doubleclick\\.net[^>]*></script>#i',
            '#<script[^>]+securepubads\\.g\\.doubleclick\\.net[^>]*></script>#i',
            '#<script[^>]*>\\s*\\(adsbygoogle\\s*=\\s*window\\.adsbygoogle\\s*\\|\\|\\s*\\[\\]\\)\\.push\\([^<]*?</script>#is',
            '#<ins[^>]*adsbygoogle[^>]*>.*?</ins>#is',
            '#<amp-ad[^>]*>.*?</amp-ad>#is',
            '#<iframe[^>]+(?:doubleclick\\.net|googlesyndication\\.com)[^>]*>.*?</iframe>#is',
        ];

        $out = preg_replace($patterns, '', (string) $html);

        $safety_css = '<style id="itp-bridge-no-ads">.itp-bridge-body .adsbygoogle,.itp-bridge-body .google-auto-placed,.itp-bridge-body [id^="google_ads_iframe"],.itp-bridge-body iframe[src*="doubleclick.net"],.itp-bridge-body iframe[src*="googlesyndication.com"],.itp-bridge-body amp-ad{display:none!important;visibility:hidden!important;opacity:0!important;width:0!important;height:0!important;max-width:0!important;max-height:0!important;overflow:hidden!important;pointer-events:none!important;}</style>';

        if (stripos($out, '</head>') !== false && stripos($out, 'id="itp-bridge-no-ads"') === false) {
            $out = preg_replace('#</head>#i', $safety_css . "\n</head>", $out, 1);
        }

        return $out;
    }

    private function privacy_policy_url() {
        $settings = $this->settings();
        $pid = (int)($settings['privacy_page_id'] ?? 0);
        if ($pid && get_post_status($pid)) {
            return get_permalink($pid);
        }

        $wp_privacy = (int)get_option('wp_page_for_privacy_policy');
        if ($wp_privacy && get_post_status($wp_privacy)) {
            return get_permalink($wp_privacy);
        }

        return home_url('/privacy-policy/');
    }

    public function render_bridge_template() {
        if (!is_singular(self::CPT)) return;
        global $post;
        if (!$post || $post->post_status !== 'publish') {
            status_header(404);
            return;
        }
        $id = $post->ID;
        $meta = $this->bridge_meta($id);
        $settings = $this->settings();
        $delay = $meta['button_delay'] !== '' ? absint($meta['button_delay']) : absint($settings['button_delay']);
        $template = sanitize_html_class($meta['template'] ?: 'blue_cards');
        if (($meta['template'] ?? '') === 'cookie_modal') {
            $this->render_cookie_bridge_template($id, $meta, $settings, $delay);
        }
        if (($meta['template'] ?? '') === 'vsl_video') {
            $this->render_vsl_bridge_template($id, $meta, $settings, false);
        }
        if (($meta['template'] ?? '') === 'vsl_landing') {
            $this->render_vsl_bridge_template($id, $meta, $settings, true);
        }
        if (($meta['template'] ?? '') === 'landing_page') {
            $this->render_landing_page_template($id, $meta, $settings);
        }
        $bridge_bg_mode = sanitize_html_class($meta['bridge_bg_mode'] ?? 'gradient');
        if (!in_array($bridge_bg_mode, ['gradient', 'solid', 'image'], true)) {
            $bridge_bg_mode = 'gradient';
        }
        $bridge_card_layout = sanitize_html_class($meta['bridge_card_layout'] ?? 'stacked');
        if (!in_array($bridge_card_layout, ['stacked', 'grid', 'links'], true)) {
            $bridge_card_layout = 'stacked';
        }
        $bridge_style_vars = [
            '--itp-bridge-bg-1:' . ($meta['bridge_bg_color'] ?: '#0b0620'),
            '--itp-bridge-bg-2:' . ($meta['bridge_bg_color_2'] ?: '#261044'),
            '--itp-bridge-title:' . ($meta['bridge_title_color'] ?: '#ffffff'),
            '--itp-bridge-text:' . ($meta['bridge_text_color'] ?: '#d8def0'),
            '--itp-bridge-btn-bg:' . ($meta['bridge_button_bg'] ?: '#24d4d9'),
            '--itp-bridge-btn-text:' . ($meta['bridge_button_text'] ?: '#041526'),
            '--itp-bridge-card-bg:' . ($meta['bridge_card_bg'] ?: '#241638'),
            '--itp-bridge-card-border:' . ($meta['bridge_card_border'] ?: '#47305f'),
            '--itp-bridge-card-title:' . ($meta['bridge_card_title'] ?: $meta['bridge_title_color'] ?: '#ffffff'),
            '--itp-bridge-card-text:' . ($meta['bridge_card_text'] ?: $meta['bridge_text_color'] ?: '#d8def0'),
        ];
        if (!empty($meta['bridge_bg_image'])) {
            $bridge_style_vars[] = '--itp-bridge-bg-image:url(' . esc_url($meta['bridge_bg_image']) . ')';
        }
        $bridge_inline_style = implode(';', $bridge_style_vars);
        nocache_headers();
        ?><!doctype html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex,nofollow">
<title><?php echo esc_html($meta['headline'] ?: get_the_title($id)); ?></title>
<?php if (!empty($meta['meta_description'])) : ?><meta name="description" content="<?php echo esc_attr($meta['meta_description']); ?>"><?php endif; ?>
<?php echo $this->tracking_head_markup($meta); ?>
<?php wp_head(); ?>
<link rel="stylesheet" href="<?php echo esc_url(plugin_dir_url(__FILE__) . 'assets/bridge.css?ver=' . self::VERSION); ?>">
</head>
<body class="itp-bridge-body itp-template-<?php echo esc_attr($template); ?> itp-bg-mode-<?php echo esc_attr($bridge_bg_mode); ?> itp-card-layout-<?php echo esc_attr($bridge_card_layout); ?>" style="<?php echo esc_attr($bridge_inline_style); ?>" data-itp-delay="<?php echo esc_attr($delay); ?>">
<div class="itp-delay-pill" data-itp-delay-pill hidden>Continue available in <span><?php echo esc_html($delay); ?></span> seconds...</div>
<main class="itp-bridge-page">
    <?php if (($meta['bridge_show_badge'] ?? '0') === '1' && !empty($meta['logo'])) : ?>
<header class="itp-bridge-brand">
    <img src="<?php echo esc_url($meta['logo']); ?>" alt="">
</header>
<?php endif; ?>
    <section class="itp-bridge-hero">
        <?php if (trim((string)$meta['headline']) !== '') : ?><h1><?php echo esc_html($meta['headline']); ?></h1><?php endif; ?>
        <?php if ($meta['short_copy']) : ?><p><?php echo esc_html($meta['short_copy']); ?></p><?php endif; ?>
        <?php if (($meta['bridge_show_top_buttons'] ?? '0') === '1' && (!empty($meta['top_cta_url']) || !empty($meta['secondary_cta_url']))) : ?>
        <div class="itp-bridge-actions">
            <?php if ($meta['top_cta_url']) : ?><a class="itp-bridge-btn" data-itp-destination="<?php echo esc_url($meta['top_cta_url']); ?>" href="<?php echo esc_url($meta['top_cta_url']); ?>"><?php echo esc_html($meta['top_cta_label'] ?: 'Continue'); ?></a><?php endif; ?>
            <?php if ($meta['secondary_cta_url']) : ?><a class="itp-bridge-btn secondary" data-itp-destination="<?php echo esc_url($meta['secondary_cta_url']); ?>" href="<?php echo esc_url($meta['secondary_cta_url']); ?>"><?php echo esc_html($meta['secondary_cta_label'] ?: 'Compare options'); ?></a><?php endif; ?>
        </div>
        <?php endif; ?>
    </section>
    <?php if (!empty($meta['cards'])) : ?>
    <section class="itp-public-card-grid count-<?php echo esc_attr(count($meta['cards'])); ?>">
        <?php foreach ($meta['cards'] as $card) : if (empty($card['url']) && empty($card['title'])) continue; ?>
            <a class="itp-public-card <?php echo !empty($card['image']) ? 'has-image' : 'no-image'; ?>" data-itp-destination="<?php echo esc_url($card['url']); ?>" href="<?php echo esc_url($card['url'] ?: '#'); ?>">
                <?php if (!empty($card['image'])) : ?><img src="<?php echo esc_url($card['image']); ?>" alt=""><?php endif; ?>
                <strong><?php echo esc_html($card['title'] ?: 'Continue'); ?></strong>
                <?php if (!empty($card['text'])) : ?><span><?php echo esc_html($card['text']); ?></span><?php endif; ?>
                <em><?php echo esc_html($card['button'] ?: 'Read more'); ?> →</em>
            </a>
        <?php endforeach; ?>
    </section>
    <?php endif; ?>
    <footer class="itp-bridge-footer">
        <?php if ($meta['footer_note']) : ?><p><?php echo esc_html($meta['footer_note']); ?></p><?php endif; ?>
        <nav><?php echo wp_kses_post($this->policy_links_html()); ?></nav>
        <?php if ($settings['powered_by'] === '1') : ?><small>Powered by Invalid Traffic</small><?php endif; ?>
    </footer>
</main>
<script>window.ITP_TRACK=<?php echo wp_json_encode(['ajax' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('itp_track_' . $id), 'id' => $id, 'delay' => $delay]); ?>;</script>
<?php echo $this->tracking_runtime_script($meta, $id); ?>
<script src="<?php echo esc_url(plugin_dir_url(__FILE__) . 'assets/it.js?ver=' . self::VERSION); ?>" defer></script>
<?php echo $this->tracking_footer_markup($meta); ?>
<?php wp_footer(); ?>
</body></html><?php
        exit;
    }

    private function sanitize_vsl_embed_code($code) {
        $code = (string)$code;
        if (current_user_can('unfiltered_html')) {
            return $code;
        }
        return wp_kses_post($code);
    }

    private function vsl_runtime_config($id, $meta) {
        return [
            'id' => (int)$id,
            'speed' => (float)($meta['vsl_speed'] ?: 1),
            'smartAutoplay' => (($meta['vsl_smart_autoplay'] ?? '1') === '1'),
            'interactionSound' => (($meta['vsl_interaction_sound'] ?? '0') === '1'),
            'controlsOn' => (($meta['vsl_native_controls'] ?? '0') === '1'),
            'clickPause' => (($meta['vsl_ctrl_allow_pause'] ?? '0') === '1'),
            'customControls' => [
                'bigPlay' => false,
                'bigPlay' => (($meta['vsl_ctrl_big_play'] ?? '0') === '1'),
                'smallPlay' => (($meta['vsl_ctrl_small_play'] ?? '0') === '1'),
                'allowPause' => (($meta['vsl_ctrl_allow_pause'] ?? '0') === '1'),
                'progress' => false,
                'time' => (($meta['vsl_ctrl_time'] ?? '0') === '1'),
                'back10' => (($meta['vsl_ctrl_back10'] ?? '0') === '1'),
                'forward10' => (($meta['vsl_ctrl_forward10'] ?? '0') === '1'),
                'volume' => (($meta['vsl_ctrl_volume'] ?? '0') === '1'),
                'fullscreen' => (($meta['vsl_ctrl_fullscreen'] ?? '0') === '1'),
                'speed' => (($meta['vsl_ctrl_speed'] ?? '0') === '1'),
            ],
            'bgStart' => (int)($meta['vsl_bg_start'] ?? 0),
            'bgEnd' => (int)($meta['vsl_bg_end'] ?? 0),
            'progressOn' => (($meta['vsl_progress_on'] ?? '1') === '1'),
            'progressType' => $meta['vsl_progress_type'] ?: 'smart',
            'ctaOn' => (($meta['vsl_cta_on'] ?? '0') === '1'),
            'ctaDelay' => (int)($meta['vsl_cta_delay'] ?? 0),
            'ctaEnd' => (int)($meta['vsl_cta_end'] ?? 0),
            'pitchTime' => (int)($meta['vsl_pitch_time'] ?? 0),
            'ctaAutoScroll' => (($meta['vsl_cta_autoscroll'] ?? '0') === '1'),
            'ctaRemember' => (($meta['vsl_cta_remember'] ?? '0') === '1'),
            'pauseOn' => (($meta['vsl_pause_on'] ?? '0') === '1'),
            'pauseStart' => (int)($meta['vsl_pause_start'] ?? 0),
            'pauseEnd' => (int)($meta['vsl_pause_end'] ?? 0),
            'pauseTrigger' => $meta['vsl_pause_trigger'] ?? 'pause',
            'hookOn' => (($meta['vsl_hook_on'] ?? '0') === '1'),
            'hookStart' => (int)($meta['vsl_hook_start'] ?? 0),
            'hookEnd' => (int)($meta['vsl_hook_end'] ?? 0),
            'resumeOn' => (($meta['vsl_resume_on'] ?? '0') === '1'),
            'resumeMessage' => (string)($meta['vsl_resume_message'] ?? 'You already started watching this video'),
            'resumeContinueText' => (string)($meta['vsl_resume_continue_text'] ?? 'Continue watching?'),
            'resumeRestartText' => (string)($meta['vsl_resume_restart_text'] ?? 'Start from the beginning?'),
            'resumeTextColor' => (string)($meta['vsl_resume_text_color'] ?? '#ffffff'),
            'resumeBg' => (string)($meta['vsl_resume_bg'] ?? '#581c87'),
        ];
    }


    private function vsl_player_js_url($post_or_slug) {
        $post = null;
        if (is_numeric($post_or_slug)) {
            $post = get_post((int)$post_or_slug);
            $slug = $post ? $post->post_name : '';
        } else {
            $slug = sanitize_title((string)$post_or_slug);
            $post = get_page_by_path($slug, OBJECT, self::CPT);
        }
        if (!$slug) $slug = 'your-vsl';
        $url = home_url(user_trailingslashit('it-vsl-player/' . $slug . '/player.js'));
        $ver = self::VERSION;
        if ($post && !empty($post->ID)) {
            $modified = get_post_modified_time('U', true, $post->ID);
            if ($modified) $ver .= '-' . $modified;
        }
        return add_query_arg('v', rawurlencode($ver), $url);
    }

    public function render_vsl_player_js_endpoint() {
        $slug = sanitize_title((string)get_query_var('itp_vsl_player_js'));
        if (!$slug) {
            $path = isset($_SERVER['REQUEST_URI']) ? trim((string)wp_parse_url(wp_unslash($_SERVER['REQUEST_URI']), PHP_URL_PATH), '/') : '';
            if ($path && preg_match('#(?:^|/)it-vsl-player/([^/]+)/player\.js$#', $path, $m)) {
                $slug = sanitize_title($m[1]);
            }
        }
        if (!$slug) return;

        $bridge = get_page_by_path($slug, OBJECT, self::CPT);
        if (!$bridge || $bridge->post_status !== 'publish') {
            status_header(404);
            header('Content-Type: application/javascript; charset=utf-8');
            echo "console.warn('Invalid Traffic player not found.');";
            exit;
        }

        $embed_url = $this->vsl_embed_url($bridge->ID);
        $bridge_meta = $this->bridge_meta($bridge->ID);
        $preload_video_url = !empty($bridge_meta['vsl_video_url']) ? esc_url_raw($bridge_meta['vsl_video_url']) : '';
        $vsl_aspect_w = 9;
        $vsl_aspect_h = 16;
        $vsl_aspect_url = !empty($bridge_meta['vsl_video_url']) ? $bridge_meta['vsl_video_url'] : '';
        if ($vsl_aspect_url && function_exists('attachment_url_to_postid')) {
            $vsl_aspect_att = attachment_url_to_postid($vsl_aspect_url);
            if ($vsl_aspect_att) {
                $vsl_aspect_meta = wp_get_attachment_metadata($vsl_aspect_att);
                $maybe_w = isset($vsl_aspect_meta['width']) ? (int)$vsl_aspect_meta['width'] : 0;
                $maybe_h = isset($vsl_aspect_meta['height']) ? (int)$vsl_aspect_meta['height'] : 0;
                if ($maybe_w > 0 && $maybe_h > 0) {
                    $vsl_aspect_w = $maybe_w;
                    $vsl_aspect_h = $maybe_h;
                }
            }
        }
        $vsl_has_below_cta = (($bridge_meta['vsl_cta_on'] ?? '0') === '1' && ($bridge_meta['vsl_cta_position'] ?? 'below') !== 'inside');
        nocache_headers();
        status_header(200);
        header('Content-Type: application/javascript; charset=utf-8');
        ?>!function(){
  var script=document.currentScript;
  var slug=<?php echo wp_json_encode($slug); ?>;
  var bridgeId=<?php echo (int)$bridge->ID; ?>;
  var src=<?php echo wp_json_encode($embed_url); ?>;
  var preloadVideo=<?php echo wp_json_encode($preload_video_url); ?>;
  var aspectW=<?php echo (int)$vsl_aspect_w; ?>;
  var aspectH=<?php echo (int)$vsl_aspect_h; ?>;
  var hasBelowCta=<?php echo $vsl_has_below_cta ? 'true' : 'false'; ?>;
  var interactionSound=<?php echo (($bridge_meta['vsl_interaction_sound'] ?? '0') === '1') ? 'true' : 'false'; ?>;
  var tag=null;
  function css(el,txt){try{el.style.cssText=(el.style.cssText?el.style.cssText+';':'')+txt;}catch(e){}}
  tag=document.getElementById('itp-vsl-'+slug)||document.querySelector('it-vsl-player[data-player="'+slug+'"]')||document.querySelector('it-vsl-player[id="'+slug+'"]')||document.querySelector('it-vsl-player');
  if(!tag){
    tag=document.createElement('it-vsl-player');
    tag.id='itp-vsl-'+slug;
    if(script&&script.parentNode) script.parentNode.insertBefore(tag,script);
  }
  var explicitMax=tag.getAttribute('max-width')||tag.dataset.maxWidth||'';
  function aspectMaxWidth(){
    var aw=parseInt(aspectW||9,10), ah=parseInt(aspectH||16,10);
    if(!aw||!ah){aw=9;ah=16;}
    var ratio=aw/Math.max(1,ah);
    if(ratio<0.82) return '400px';
    if(ratio>1.22) return '100%';
    return '620px';
  }
  var max=explicitMax||aspectMaxWidth();
  css(tag,'display:block;margin:0 auto;width:100%;max-width:'+max+';background:transparent;line-height:0;');
  try{
    if(preloadVideo){
      var pv=document.createElement('link');
      pv.rel='preload'; pv.as='video'; pv.href=preloadVideo; pv.fetchPriority='high';
      document.head.appendChild(pv);
    }
  }catch(e){}
  try{
    var u=new URL(src,window.location.href);
    var p=new URLSearchParams(window.location.search||'');
    p.forEach(function(v,k){if(!u.searchParams.has(k))u.searchParams.set(k,v);});
    u.searchParams.set('itp_parent',window.location.href);
    u.searchParams.set('itp_embed_id',tag.id||('itp-vsl-'+slug));
    src=u.toString();
    var origin=u.origin;
    ['preconnect','dns-prefetch'].forEach(function(rel){
      var l=document.createElement('link'); l.rel=rel; l.href=origin; document.head.appendChild(l);
    });
  }catch(e){}
  var iframe=document.createElement('iframe');
  iframe.src=src;
  iframe.loading='eager';
  try{ iframe.setAttribute('fetchpriority','high'); }catch(e){}
  iframe.allow='autoplay; fullscreen; picture-in-picture';
  iframe.setAttribute('allowfullscreen','');
  iframe.setAttribute('frameborder','0');
  var lastIframeHeight=0;
  var belowCtaVisible=false;
  function estimatedHeight(){
    var w = 0;
    try{ w = Math.round((tag.getBoundingClientRect && tag.getBoundingClientRect().width) || tag.offsetWidth || 0); }catch(e){}
    if(!w) w = 420;
    var aw = parseInt(aspectW||9,10), ah = parseInt(aspectH||16,10);
    if(!aw || !ah){ aw = 9; ah = 16; }
    var ratio = aw / Math.max(1, ah);
    var maxPlayer = ratio < 0.82 ? 400 : (ratio > 1.22 ? w : 620);
    var base = ratio > 1.22 ? w : Math.min(w, maxPlayer);
    // 0.18.95: reserve only the video. The CTA area is added only after the child player says it is visible.
    var reserve = belowCtaVisible ? 92 : 0;
    var h = Math.round(base * ah / Math.max(1, aw)) + reserve;
    return Math.max(160, Math.min(1400, h));
  }
  function setHeight(h, force){
    h=parseInt(h||0,10);
    if(!h||h<100||h>3000) return;
    if(!force && lastIframeHeight){
      var delta=Math.abs(h-lastIframeHeight);
      if(delta<10) return;
      // Avoid tiny downward measurements from browser/video metadata recalculations.
      if(h<lastIframeHeight && (lastIframeHeight-h)<48) return;
    }
    lastIframeHeight=h;
    iframe.style.height=h+'px';
  }
  css(iframe,'display:block;width:100%;height:'+estimatedHeight()+'px;border:0;overflow:hidden;background:transparent;margin:0 auto;');
  tag.innerHTML='';
  tag.appendChild(iframe);
  if(interactionSound && !tag.__itpInteractionRelay148){
    tag.__itpInteractionRelay148='1';
    var relayDone=false;
    var relayInteraction=function(ev){
      if(relayDone) return;
      relayDone=true;
      try{
        if(iframe && iframe.contentWindow){
          iframe.contentWindow.postMessage({type:'itp-vsl-interaction-start', id:tag.id||bridgeId, bridgeId:bridgeId, event:(ev&&ev.type)||'interaction'}, '*');
        }
      }catch(e){}
      ['click','pointerdown','touchstart','keydown','wheel','scroll'].forEach(function(type){
        try{ window.removeEventListener(type, relayInteraction, true); }catch(e){}
      });
    };
    ['click','pointerdown','touchstart','keydown','wheel','scroll'].forEach(function(type){
      try{ window.addEventListener(type, relayInteraction, {capture:true, passive:true}); }catch(e){ try{ window.addEventListener(type, relayInteraction, true); }catch(err){} }
    });
  }
  window.addEventListener('message',function(ev){
    var d=ev.data||{};
    if(!d||d.type!=='itp-vsl-resize') return;
    if(d.id&&d.id!==tag.id&&d.id!==bridgeId) return;
    if(typeof d.ctaVisible!=='undefined') belowCtaVisible=!!d.ctaVisible;
    setHeight(d.height, !!d.force || d.reason==='cta' || d.reason==='boot');
  });
  function sameOriginResize(force){
    try{
      var doc=iframe.contentDocument||iframe.contentWindow.document;
      if(!doc) return;
      var player=doc.querySelector('[data-itp-vsl]');
      var rect=player?player.getBoundingClientRect():null;
      var h=rect?Math.ceil((rect.height||0)+6):Math.max(doc.body.scrollHeight||0,doc.documentElement.scrollHeight||0);
      setHeight(h, !!force);
    }catch(e){}
  }
  iframe.addEventListener('load',function(){
    // Avoid the old 30x polling loop. It caused visible Elementor/section recalculations.
    setTimeout(function(){ sameOriginResize(true); },140);
    setTimeout(function(){ sameOriginResize(false); },700);
  });
  var resizeTimer=null;
  window.addEventListener('resize', function(){
    clearTimeout(resizeTimer);
    resizeTimer=setTimeout(function(){ setHeight(estimatedHeight(), true); sameOriginResize(false); },120);
  });
}();<?php
        exit;
    }


    public function render_vsl_embed_endpoint() {
        $slug = sanitize_title((string)get_query_var('itp_vsl_embed'));
        if (!$slug) {
            $path = isset($_SERVER['REQUEST_URI']) ? trim((string)wp_parse_url(wp_unslash($_SERVER['REQUEST_URI']), PHP_URL_PATH), '/') : '';
            if ($path && preg_match('#(?:^|/)it-vsl-embed/([^/]+)/?$#', $path, $m)) {
                $slug = sanitize_title($m[1]);
            }
        }
        if (!$slug) {
            return;
        }
        $bridge = get_page_by_path($slug, OBJECT, self::CPT);
        if (!$bridge || $bridge->post_status !== 'publish') {
            status_header(404);
            nocache_headers();
            echo '<!doctype html><html><head><meta name="robots" content="noindex,nofollow"><meta name="viewport" content="width=device-width, initial-scale=1"></head><body style="margin:0;background:#070a13;color:#fff;font-family:system-ui;padding:24px">VSL player not found.</body></html>';
            exit;
        }
        $meta = $this->bridge_meta($bridge->ID);
        $is_preview_player = false; // 0.18.87: VSL / Video is player-only. No public preview page/copy card.
        $clean_embed_url = $this->vsl_embed_url($bridge->ID);
        $preview_player_js_url = $this->vsl_player_js_url($bridge->ID);
        $preview_embed_js = '<it-vsl-player id="itp-vsl-' . esc_attr($slug) . '" style="display:block;margin:0 auto;width:100%;"></it-vsl-player> <script type="text/javascript">var s=document.createElement("script");s.src="' . esc_url($preview_player_js_url) . '";s.async=true;document.head.appendChild(s);</script>';
        $preview_embed_iframe = '<iframe src="' . esc_url($clean_embed_url) . '" width="100%" height="760" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" loading="eager" style="display:block;width:100%;margin:0 auto;border:0;overflow:hidden;background:transparent;"></iframe>';
        if (!in_array(($meta['template'] ?? ''), ['vsl_video','vsl_landing'], true)) {
            status_header(404);
            nocache_headers();
            echo '<!doctype html><html><head><meta name="robots" content="noindex,nofollow"><meta name="viewport" content="width=device-width, initial-scale=1"></head><body style="margin:0;background:#070a13;color:#fff;font-family:system-ui;padding:24px">This bridge is not a VSL player.</body></html>';
            exit;
        }
        nocache_headers();
        status_header(200);
        header('X-Robots-Tag: noindex, nofollow', true);
        $title = $meta['vsl_headline'] ?: get_the_title($bridge->ID);
        ?><!doctype html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex,nofollow">
<title><?php echo esc_html($title); ?></title>
<?php echo $this->tracking_head_markup($meta); ?>
<link rel="stylesheet" href="<?php echo esc_url(plugin_dir_url(__FILE__) . 'assets/bridge.css?ver=' . self::VERSION); ?>">
<style>
html,body{margin:0!important;padding:0!important;min-height:0!important;height:auto!important;background:transparent!important;overflow:hidden!important}
.itp-vsl-player-only-body{display:block!important;width:100%!important;background:transparent!important;color:#fff!important}
.itp-vsl-embed-body{margin:0!important;padding:0!important;background:transparent!important;min-height:0!important;height:auto!important}
.itp-vsl-embed-shell{display:block!important;width:100%!important;max-width:100%!important;margin:0 auto!important;padding:0!important;background:transparent!important;min-height:0!important;height:auto!important;line-height:0!important}
.itp-vsl-embed-shell .itp-vsl-player{background:transparent!important;margin:0 auto!important;line-height:0!important}
.itp-vsl-embed-shell .itp-vsl-video-frame{margin:0 auto!important}
</style>
<?php if ($is_preview_player) : ?>
<style>
.itp-vsl-preview-copy-wrap{max-width:980px;margin:22px auto 34px;padding:0 18px;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;color:#e5f6ff}
.itp-vsl-preview-copy-card{border:1px solid rgba(34,211,238,.26);border-radius:24px;background:radial-gradient(circle at 0% 0%,rgba(34,211,238,.14),transparent 34%),linear-gradient(135deg,rgba(8,18,34,.96),rgba(12,28,50,.94));box-shadow:0 24px 70px rgba(2,6,23,.34),inset 0 1px 0 rgba(255,255,255,.07);padding:20px}
.itp-vsl-preview-copy-head{display:flex;justify-content:space-between;gap:16px;align-items:flex-start;margin-bottom:14px}
.itp-vsl-preview-copy-head h2{margin:0;color:#fff;font-size:22px;letter-spacing:-.04em}.itp-vsl-preview-copy-head p{margin:6px 0 0;color:#9fb1c8;font-size:14px;line-height:1.5}
.itp-vsl-preview-copy-actions{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end}
.itp-vsl-preview-copy-btn{appearance:none;border:1px solid rgba(148,163,184,.24);border-radius:999px;background:rgba(15,23,42,.72);color:#dbeafe;text-decoration:none;font-weight:900;font-size:13px;padding:12px 15px;cursor:pointer}
.itp-vsl-preview-copy-btn.primary{background:linear-gradient(135deg,#18d8ff,#2dd4bf);color:#03121f;border-color:rgba(255,255,255,.34);box-shadow:0 16px 34px rgba(24,216,255,.22)}
.itp-vsl-preview-code-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}.itp-vsl-preview-code-grid label{display:grid;gap:8px;color:#cbd5e1;font-weight:900;font-size:13px}
.itp-vsl-preview-code-grid textarea{min-height:118px;max-height:180px;resize:vertical;border:1px solid rgba(148,163,184,.22);border-radius:16px;background:rgba(2,6,23,.62);color:#dff8ff;padding:13px;font-size:12px;line-height:1.45;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono",monospace}
.itp-vsl-preview-toast{display:none;margin-top:12px;color:#67e8f9;font-weight:900;font-size:13px}.itp-vsl-preview-toast.is-on{display:block}
@media(max-width:760px){.itp-vsl-preview-copy-head{display:block}.itp-vsl-preview-copy-actions{margin-top:14px;justify-content:flex-start}.itp-vsl-preview-code-grid{grid-template-columns:1fr}.itp-vsl-preview-copy-card{padding:16px}}
</style>
<?php endif; ?>
</head>
<body class="itp-vsl-embed-body itp-vsl-player-only-body">
<div class="itp-vsl-embed-shell">
    <?php echo $this->vsl_player_html($bridge->ID, $meta); ?>
</div>
<?php if ($is_preview_player) : ?>
<div class="itp-vsl-preview-copy-wrap">
    <div class="itp-vsl-preview-copy-card">
        <div class="itp-vsl-preview-copy-head">
            <div>
                <h2>Embed / Copy</h2>
                <p>This is the clean player-only output. Copy JS or iFrame and paste it into your sales page, Elementor HTML block, WordPress Custom HTML, or funnel builder.</p>
            </div>
            <div class="itp-vsl-preview-copy-actions">
                <a class="itp-vsl-preview-copy-btn" href="<?php echo esc_url(admin_url('admin.php?page=itp-create&id=' . $bridge->ID)); ?>">Edit player</a>
                <button type="button" class="itp-vsl-preview-copy-btn primary" data-itp-preview-copy="js">Copy JS</button>
                <button type="button" class="itp-vsl-preview-copy-btn" data-itp-preview-copy="iframe">Copy iFrame</button>
            </div>
        </div>
        <div class="itp-vsl-preview-code-grid">
            <label>JavaScript embed<textarea readonly data-itp-preview-code="js"><?php echo esc_textarea($preview_embed_js); ?></textarea></label>
            <label>iFrame embed<textarea readonly data-itp-preview-code="iframe"><?php echo esc_textarea($preview_embed_iframe); ?></textarea></label>
        </div>
        <div class="itp-vsl-preview-toast" data-itp-preview-toast>Copied.</div>
    </div>
</div>
<script>
(function(){
  function copy(type){
    var field=document.querySelector('[data-itp-preview-code="'+type+'"]');
    var toast=document.querySelector('[data-itp-preview-toast]');
    if(!field) return;
    field.focus(); field.select();
    var text=field.value || '';
    var done=function(){ if(toast){ toast.textContent=(type==='js'?'JavaScript embed':'iFrame embed')+' copied.'; toast.classList.add('is-on'); setTimeout(function(){toast.classList.remove('is-on');},2200); } };
    if(navigator.clipboard && navigator.clipboard.writeText){ navigator.clipboard.writeText(text).then(done).catch(function(){ try{document.execCommand('copy'); done();}catch(e){} }); }
    else { try{document.execCommand('copy'); done();}catch(e){} }
  }
  document.addEventListener('click',function(e){ var btn=e.target.closest('[data-itp-preview-copy]'); if(!btn) return; e.preventDefault(); copy(btn.getAttribute('data-itp-preview-copy')); });
})();
</script>
<?php endif; ?>
<script>window.ITP_TRACK=<?php echo wp_json_encode(['ajax' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('itp_track_' . $bridge->ID), 'id' => $bridge->ID, 'delay' => 0]); ?>;</script>
<?php echo $this->tracking_runtime_script($meta, $bridge->ID); ?>
<script>window.ITP_VSL=<?php echo wp_json_encode($this->vsl_runtime_config($bridge->ID, $meta)); ?>;</script>
<script src="<?php echo esc_url(plugin_dir_url(__FILE__) . 'assets/it.js?ver=' . self::VERSION); ?>" defer></script>
<?php echo $this->tracking_footer_markup($meta); ?>
</body></html><?php
        exit;
    }


    private function render_vsl_embed_endpoint_by_id($id) {
        $bridge = get_post((int)$id);
        if (!$bridge || $bridge->post_status !== 'publish') {
            status_header(404);
            exit;
        }
        $meta = $this->bridge_meta($bridge->ID);
        nocache_headers();
        status_header(200);
        header('X-Robots-Tag: noindex, nofollow', true);
        $title = $meta['vsl_headline'] ?: get_the_title($bridge->ID);
        ?><!doctype html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex,nofollow">
<title><?php echo esc_html($title); ?></title>
<?php echo $this->tracking_head_markup($meta); ?>
<link rel="stylesheet" href="<?php echo esc_url(plugin_dir_url(__FILE__) . 'assets/bridge.css?ver=' . self::VERSION); ?>">
<style>html,body{margin:0!important;padding:0!important;background:transparent!important;min-height:0!important;height:auto!important;overflow:hidden!important}.itp-vsl-embed-shell{display:block!important;width:100%!important;margin:0 auto!important;padding:0!important;background:transparent!important;line-height:0!important}</style>
</head>
<body class="itp-vsl-embed-body itp-vsl-player-only-body">
<div class="itp-vsl-embed-shell"><?php echo $this->vsl_player_html($bridge->ID, $meta); ?></div>
<script>window.ITP_TRACK=<?php echo wp_json_encode(['ajax' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('itp_track_' . $bridge->ID), 'id' => $bridge->ID, 'delay' => 0]); ?>;</script>
<?php echo $this->tracking_runtime_script($meta, $bridge->ID); ?>
<script>window.ITP_VSL=<?php echo wp_json_encode($this->vsl_runtime_config($bridge->ID, $meta)); ?>;</script>
<script src="<?php echo esc_url(plugin_dir_url(__FILE__) . 'assets/it.js?ver=' . self::VERSION); ?>" defer></script>
<?php echo $this->tracking_footer_markup($meta); ?>
</body></html><?php
        exit;
    }

    private function render_vsl_bridge_template($id, $meta, $settings, $with_landing = false) {
        if (!$with_landing) {
            $this->render_vsl_embed_endpoint_by_id($id);
        }
        nocache_headers();
        $title = $with_landing ? ($meta['landing_headline'] ?: $meta['vsl_headline'] ?: get_the_title($id)) : ($meta['vsl_headline'] ?: get_the_title($id));
        ?><!doctype html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex,nofollow">
<title><?php echo esc_html($title); ?></title>
<?php if (!empty($meta['meta_description'])) : ?><meta name="description" content="<?php echo esc_attr($meta['meta_description']); ?>"><?php endif; ?>
<?php echo $this->tracking_head_markup($meta); ?>
<?php wp_head(); ?>
<link rel="stylesheet" href="<?php echo esc_url(plugin_dir_url(__FILE__) . 'assets/bridge.css?ver=' . self::VERSION); ?>">
</head>
<body class="itp-bridge-body itp-template-<?php echo esc_attr($meta['template'] ?? 'vsl_video'); ?>">
<main class="<?php echo $with_landing ? 'itp-vsl-landing-page' : 'itp-vsl-public-page'; ?>">
    <?php if ($with_landing) : ?>
        <section class="itp-native-landing-hero">
            <span><?php echo esc_html($this->landing_niche_label($meta['landing_niche'] ?? 'custom')); ?></span>
            <h1><?php echo esc_html($meta['landing_headline'] ?: 'A ready-to-edit campaign page'); ?></h1>
            <?php if (!empty($meta['landing_copy'])) : ?><p><?php echo esc_html($meta['landing_copy']); ?></p><?php endif; ?>
        </section>
    <?php endif; ?>

    <section class="itp-vsl-public-wrap">
        <?php if (!$with_landing) : ?>
            <header class="itp-vsl-public-head">
                <span>Video presentation</span>
                <h1><?php echo esc_html($meta['vsl_headline'] ?: 'Watch this presentation before you continue'); ?></h1>
                <?php if (!empty($meta['vsl_subheadline'])) : ?><p><?php echo esc_html($meta['vsl_subheadline']); ?></p><?php endif; ?>
            </header>
        <?php endif; ?>
        <?php echo $this->vsl_player_html($id, $meta); ?>
    </section>

    <?php if ($with_landing) : ?>
        <?php echo $this->native_landing_sections_html($meta); ?>
    <?php endif; ?>

    <footer class="itp-bridge-footer itp-vsl-footer">
        <?php if ($meta['footer_note']) : ?><p><?php echo esc_html($meta['footer_note']); ?></p><?php endif; ?>
        <nav><?php echo wp_kses_post($this->policy_links_html()); ?></nav>
        <?php if ($settings['powered_by'] === '1') : ?><small>Powered by Invalid Traffic</small><?php endif; ?>
    </footer>
</main>
<script>window.ITP_TRACK=<?php echo wp_json_encode(['ajax' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('itp_track_' . $id), 'id' => $id, 'delay' => 0]); ?>;</script>
<?php echo $this->tracking_runtime_script($meta, $id); ?>
<script>window.ITP_VSL=<?php echo wp_json_encode($this->vsl_runtime_config($id, $meta)); ?>;</script>
<script src="<?php echo esc_url(plugin_dir_url(__FILE__) . 'assets/it.js?ver=' . self::VERSION); ?>" defer></script>
<?php echo $this->tracking_footer_markup($meta); ?>
<?php wp_footer(); ?>
</body></html><?php
        exit;
    }

    private function vsl_smartplay_split_lines($text, $max = 3) {
        $text = trim((string)$text);
        if ($text === '') return [];
        $manual = preg_split('/\r\n|\r|\n/', $text);
        $manual = array_values(array_filter(array_map('trim', $manual), static function($v){ return $v !== ''; }));
        if (count($manual) > 1) return array_slice($manual, 0, max(1, (int)$max));
        $words = preg_split('/\s+/', $text);
        $words = array_values(array_filter(array_map('trim', $words), static function($v){ return $v !== ''; }));
        $max = max(1, (int)$max);
        if (count($words) <= $max) return $words;
        $out = array_slice($words, 0, $max - 1);
        $out[] = implode(' ', array_slice($words, $max - 1));
        return $out;
    }

    private function vsl_smartplay_text_svg($text, $x, $y, $size, $opts = []) {
        $text = (string)$text;
        if (trim($text) === '') return '';
        $weight = isset($opts['weight']) ? (int)$opts['weight'] : 950;
        $letter = isset($opts['letter']) ? (string)$opts['letter'] : '-0.035em';
        $line_height = isset($opts['line_height']) ? (float)$opts['line_height'] : ((float)$size * 1.12);
        $lines = isset($opts['lines']) && is_array($opts['lines']) ? $opts['lines'] : [$text];
        $lines = array_values(array_filter(array_map('strval', $lines), static function($v){ return trim($v) !== ''; }));
        if (!$lines) return '';
        $style = 'font-family:var(--itp-overlay-font,Inter,system-ui,sans-serif);font-size:' . (float)$size . 'px;font-weight:' . $weight . ';letter-spacing:' . esc_attr($letter) . ';fill:currentColor;text-anchor:middle;dominant-baseline:middle;line-height:1;';
        if (count($lines) === 1) {
            return '<text x="' . esc_attr((string)$x) . '" y="' . esc_attr((string)$y) . '" style="' . esc_attr($style) . '">' . esc_html($lines[0]) . '</text>';
        }
        $start_y = (float)$y - ((count($lines) - 1) * $line_height / 2);
        $out = '<text x="' . esc_attr((string)$x) . '" y="' . esc_attr((string)$y) . '" style="' . esc_attr($style) . '">';
        foreach ($lines as $i => $line) {
            $out .= '<tspan x="' . esc_attr((string)$x) . '" y="' . esc_attr((string)($start_y + ($i * $line_height))) . '">' . esc_html($line) . '</tspan>';
        }
        $out .= '</text>';
        return $out;
    }

    private function vsl_smartplay_template_default_width($template) {
        $template = $template === 'large' ? 'full' : (string)$template;
        $map = [
            'wide' => 42,
            'square' => 40,
            'play' => 40,
            'full' => 100,
            'compact' => 52,
            'icon_sound' => 24,
            'icon_play' => 24,
            'icon_blink' => 24,
            'custom' => 34,
        ];
        return $map[$template] ?? 42;
    }

    private function vsl_smartplay_safe_public_width($template, $saved_width) {
        $template = $template === 'large' ? 'full' : (string)$template;
        $saved = (float)$saved_width;
        $default = $this->vsl_smartplay_template_default_width($template);
        if ($saved <= 0) return $default;
        // v0.18.150: the public/iframe renderer must respect the exact size edited in
        // Smart Autoplay. Do not normalize 58%, 92%, 100% etc. anymore; those values
        // are now intentional when the user resized the grouped overlay.
        return max(8, min(500, $saved));
    }

    private function vsl_smartplay_overlay_svg($template, $top, $bottom, $icon_url, $bg, $fg, $opacity = 100) {
        $template = $template === 'large' ? 'full' : (string)$template;
        $bg = $bg ?: '#f79a27';
        $fg = $fg ?: '#ffffff';
        $specs = [
            'wide' => ['vb' => '0 0 486 270', 'w' => 486, 'h' => 270, 'r' => 9, 'stroke' => 'rgba(255,255,255,.62)', 'sw' => 2.2],
            'square' => ['vb' => '0 0 320 360', 'w' => 320, 'h' => 360, 'r' => 14, 'stroke' => 'rgba(255,255,255,.56)', 'sw' => 3],
            'play' => ['vb' => '0 0 320 320', 'w' => 320, 'h' => 320, 'r' => 2, 'stroke' => 'rgba(255,255,255,.34)', 'sw' => 2],
            'full' => ['vb' => '0 0 400 711', 'w' => 400, 'h' => 711, 'r' => 7, 'stroke' => 'rgba(255,255,255,.36)', 'sw' => 2],
            'compact' => ['vb' => '0 0 506 152', 'w' => 506, 'h' => 152, 'r' => 12, 'stroke' => 'rgba(255,255,255,.42)', 'sw' => 0],
            'icon_sound' => ['vb' => '0 0 220 220', 'w' => 220, 'h' => 220, 'r' => 16, 'stroke' => 'rgba(255,255,255,.34)', 'sw' => 0],
            'icon_play' => ['vb' => '0 0 220 220', 'w' => 220, 'h' => 220, 'r' => 16, 'stroke' => 'rgba(255,255,255,.34)', 'sw' => 0],
            'icon_blink' => ['vb' => '0 0 220 220', 'w' => 220, 'h' => 220, 'r' => 16, 'stroke' => 'rgba(255,255,255,.34)', 'sw' => 0],
        ];
        $spec = $specs[$template] ?? $specs['wide'];
        $stroke = $spec['sw'] ? 'stroke:' . esc_attr($spec['stroke']) . ';stroke-width:' . esc_attr((string)$spec['sw']) . ';' : '';
        $fill_opacity = max(0.2, min(1, ((int)$opacity) / 100));
        $svg = '<svg class="itp-smartplay-final-svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="' . esc_attr($spec['vb']) . '" preserveAspectRatio="xMidYMid meet" aria-hidden="true" role="img" style="display:block;width:100%;height:100%;overflow:visible;color:' . esc_attr($fg) . ';">';
        $svg .= '<rect x="0" y="0" width="' . esc_attr((string)$spec['w']) . '" height="' . esc_attr((string)$spec['h']) . '" rx="' . esc_attr((string)$spec['r']) . '" ry="' . esc_attr((string)$spec['r']) . '" fill-opacity="' . esc_attr($fill_opacity) . '" style="fill:' . esc_attr($bg) . ';' . $stroke . '"/>';
        $image = static function($x, $y, $w, $h) use ($icon_url) {
            if (!$icon_url) return '';
            $u = esc_url($icon_url);
            return '<image href="' . $u . '" xlink:href="' . $u . '" x="' . esc_attr((string)$x) . '" y="' . esc_attr((string)$y) . '" width="' . esc_attr((string)$w) . '" height="' . esc_attr((string)$h) . '" preserveAspectRatio="xMidYMid meet"/>';
        };
        if ($template === 'wide') {
            $svg .= $this->vsl_smartplay_text_svg($top, 243, 54, 26, ['weight' => 950]);
            $svg .= $image(165, 82, 156, 109);
            $svg .= $this->vsl_smartplay_text_svg($bottom, 243, 220, 27, ['weight' => 950]);
        } elseif ($template === 'square') {
            $svg .= $this->vsl_smartplay_text_svg($bottom, 160, 105, 45, ['lines' => $this->vsl_smartplay_split_lines($bottom, 3), 'line_height' => 51, 'weight' => 950]);
            $svg .= $image(93, 222, 134, 96);
        } elseif ($template === 'play') {
            $svg .= $image(68, 48, 184, 184);
            $svg .= $this->vsl_smartplay_text_svg(strtoupper((string)$bottom), 160, 272, 20, ['weight' => 950, 'letter' => '0.02em']);
        } elseif ($template === 'full') {
            $svg .= $this->vsl_smartplay_text_svg($top, 200, 307, 30, ['lines' => $this->vsl_smartplay_split_lines($top, 3), 'line_height' => 34, 'weight' => 950]);
            $svg .= $image(146, 354, 108, 80);
            $svg .= $this->vsl_smartplay_text_svg($bottom, 200, 482, 28, ['weight' => 950]);
        } elseif ($template === 'compact') {
            $svg .= $image(219, 23, 68, 50);
            $svg .= $this->vsl_smartplay_text_svg($bottom, 253, 105, 34, ['weight' => 950]);
        } elseif (in_array($template, ['icon_sound', 'icon_play', 'icon_blink'], true)) {
            $iw = ($template === 'icon_play') ? 142 : 118;
            $ih = ($template === 'icon_play') ? 142 : 100;
            $svg .= $image(110 - ($iw / 2), 110 - ($ih / 2), $iw, $ih);
        }
        $svg .= '</svg>';
        return $svg;
    }

    private function vsl_player_html($id, $meta) {
        $video_url = esc_url($meta['vsl_video_url'] ?? '');
        $teaser_url = esc_url($meta['vsl_preview_video_url'] ?? '');
        $poster = esc_url($meta['vsl_poster_url'] ?? '');
        $embed_code = (string)($meta['vsl_embed_code'] ?? '');
        $cta_url = esc_url($meta['vsl_cta_url'] ?? '');
        $cta_action = in_array(($meta['vsl_cta_action'] ?? 'url'), ['url','lead','lead_redirect'], true) ? ($meta['vsl_cta_action'] ?? 'url') : 'url';
        $cta_lead_mode = in_array($cta_action, ['lead','lead_redirect'], true);
        $cta_text = $meta['vsl_cta_text'] ?: 'Continue';
        $cta_on = (($meta['vsl_cta_on'] ?? '0') === '1');
        $inside = ($meta['vsl_cta_position'] ?? 'below') === 'inside';
        $hook_class = sanitize_html_class($meta['vsl_hook_position'] ?: 'bottom_left');
        $smart_autoplay = (($meta['vsl_smart_autoplay'] ?? '1') === '1');
        $initial_video = ($smart_autoplay && $teaser_url) ? $teaser_url : $video_url;
        // 0.18.87: smart autoplay always starts as a muted pre-click teaser.
        // If a separate teaser/background exists, it uses that source. Otherwise the main video itself loops muted until click.
        $video_attrs = ($smart_autoplay && $video_url) ? ' autoplay muted loop' : (((($meta['vsl_native_controls'] ?? '0') === '1') ? ' controls' : ''));
        $video_control_attrs = (($meta['vsl_native_controls'] ?? '0') === '1') ? ' controlsList="nodownload"' : '';
        $progress_color = (($meta['vsl_progress_use_cta_color'] ?? '0') === '1') ? ($meta['vsl_ctrl_bg'] ?? '#111827') : ($meta['vsl_progress_color'] ?? '#ff5a1f');
        $progress_height = max(2, min(30, (int)($meta['vsl_progress_height'] ?? 6)));
        $player_style = '--itp-vsl-player-bg:#050816;--itp-vsl-player-accent:' . esc_attr($progress_color ?: '#ff5a1f') . ';--itp-vsl-control-color:' . esc_attr($meta['vsl_ctrl_color'] ?? '#ffffff') . ';--itp-vsl-control-bg:' . esc_attr($meta['vsl_ctrl_bg'] ?? '#050816') . ';--itp-vsl-progress-h:' . esc_attr($progress_height) . 'px;';
        $custom_controls_on = in_array('1', [
            '0',
            (string)($meta['vsl_ctrl_small_play'] ?? '0'),
            (string)($meta['vsl_ctrl_time'] ?? '0'),
            (string)($meta['vsl_ctrl_volume'] ?? '0'),
            (string)($meta['vsl_ctrl_fullscreen'] ?? '0'),
            (string)($meta['vsl_ctrl_speed'] ?? '0'),
        ], true);
        $overlay_template_raw = ($meta['vsl_overlay_template'] ?? 'wide') === 'large' ? 'full' : ($meta['vsl_overlay_template'] ?? 'wide');
        $overlay_template = sanitize_html_class($overlay_template_raw ?: 'wide');
        $overlay_pulse_mode = sanitize_html_class($meta['vsl_overlay_pulse_mode'] ?? 'scale');
        $overlay_opacity_val = max(20, min(100, (int)($meta['vsl_overlay_opacity'] ?? 92)));
        $overlay_pulse = ((($meta['vsl_overlay_pulse'] ?? '0') === '1') && (($meta['vsl_overlay_template'] ?? 'wide') !== 'full')) ? ' pulse pulse-' . $overlay_pulse_mode . ' effect-' . $overlay_pulse_mode : '';
        $cta_font_choice = (string)($meta['vsl_cta_font'] ?? 'Inter');
        $cta_font_stacks = [
            'system' => 'system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',
            'Inter' => 'Inter,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',
            'Poppins' => 'Poppins,Inter,system-ui,sans-serif',
            'Montserrat' => 'Montserrat,Inter,system-ui,sans-serif',
            'Roboto' => 'Roboto,Arial,system-ui,sans-serif',
            'Open Sans' => '"Open Sans",Arial,system-ui,sans-serif',
            'Oswald' => 'Oswald,Impact,Arial Narrow,sans-serif',
            'Bebas Neue' => '"Bebas Neue",Impact,Arial Narrow,sans-serif',
            'Arial' => 'Arial,Helvetica,sans-serif',
        ];
        $cta_font = $cta_font_stacks[$cta_font_choice] ?? $cta_font_stacks['Inter'];
        $cta_font_face_css = '';
        $cta_font_woff2 = esc_url($meta['vsl_cta_font_woff2'] ?? '');
        if ($cta_font_woff2) {
            $cta_custom_family = 'ITP VSL CTA ' . (int)$id;
            $cta_font = '"' . $cta_custom_family . '",Inter,system-ui,sans-serif';
            $cta_font_face_css = '<style id="itp-vsl-cta-font-' . esc_attr((string)$id) . '">@font-face{font-family:"' . esc_html($cta_custom_family) . '";src:url("' . esc_url($cta_font_woff2) . '") format("woff2");font-weight:100 1000;font-style:normal;font-display:swap;}</style>';
        }
        $cta_style = '--itp-cta:' . esc_attr($meta['vsl_cta_color'] ?? '#16a34a') . ';--itp-cta-hover:' . esc_attr($meta['vsl_cta_hover_color'] ?? '#15803d') . ';--itp-cta-text:' . esc_attr($meta['vsl_cta_text_color'] ?? '#fff') . ';--itp-cta-text-hover:' . esc_attr($meta['vsl_cta_text_hover_color'] ?? '#fff') . ';--itp-cta-radius:' . esc_attr((int)($meta['vsl_cta_radius'] ?? 999)) . 'px;--itp-cta-py:' . esc_attr((int)($meta['vsl_cta_padding_y'] ?? 14)) . 'px;--itp-cta-px:' . esc_attr((int)($meta['vsl_cta_padding_x'] ?? 28)) . 'px;--itp-cta-size:' . esc_attr((int)($meta['vsl_cta_font_size'] ?? 16)) . 'px;--itp-cta-weight:' . esc_attr($meta['vsl_cta_weight'] ?? '900') . ';--itp-cta-font:' . esc_attr($cta_font) . ';--itp-cta-pulse-duration:' . esc_attr($meta['vsl_cta_pulse_speed'] ?? '1.45') . 's;';
        $lead_style = '--itp-lead-bg:' . esc_attr($meta['vsl_lead_bg'] ?? '#050816') . ';--itp-lead-title:' . esc_attr($meta['vsl_lead_title_color'] ?? '#ffffff') . ';--itp-lead-text:' . esc_attr($meta['vsl_lead_text_color'] ?? '#ffffff') . ';--itp-lead-btn:' . esc_attr($meta['vsl_lead_button_bg'] ?? '#16c653') . ';--itp-lead-btn-text:' . esc_attr($meta['vsl_lead_button_text_color'] ?? '#02120a') . ';--itp-lead-close:' . esc_attr($meta['vsl_lead_close_color'] ?? '#ffffff') . ';--itp-lead-close-bg:' . esc_attr($meta['vsl_lead_close_bg'] ?? '#060b14') . ';--itp-lead-radius:' . esc_attr((int)($meta['vsl_lead_radius'] ?? 24)) . 'px;--itp-lead-bg-image:url(' . esc_url($meta['vsl_lead_bg_image'] ?? '') . ');';
        $cta_animation = in_array(($meta['vsl_cta_animation'] ?? 'premium'), ['premium','heartbeat','breathe','bounce','flash','shake'], true) ? sanitize_html_class($meta['vsl_cta_animation']) : 'premium';
        $cta_classes = trim((($meta['vsl_cta_pulse'] ?? '1') === '1' ? 'pulse effect-' . $cta_animation . ' ' : '') . (($meta['vsl_cta_shadow'] ?? '1') === '1' ? 'shadow' : ''));
        [$overlay_font_face_css, $overlay_font] = $this->vsl_custom_font_css($id, 'overlay', $meta['vsl_overlay_font_woff2'] ?? '', $meta['vsl_overlay_font'] ?? 'Inter');
        if (empty($meta['vsl_overlay_font_woff2'])) $overlay_font = $this->vsl_font_stack($meta['vsl_overlay_font'] ?? 'Inter');
        [$hook_font_face_css, $hook_font] = $this->vsl_custom_font_css($id, 'hook', $meta['vsl_hook_font_woff2'] ?? '', $meta['vsl_hook_font'] ?? 'Inter');
        if (empty($meta['vsl_hook_font_woff2'])) $hook_font = $this->vsl_font_stack($meta['vsl_hook_font'] ?? 'Inter');
        [$resume_font_face_css, $resume_font] = $this->vsl_custom_font_css($id, 'resume', $meta['vsl_resume_font_woff2'] ?? '', $meta['vsl_resume_font'] ?? 'Inter');
        if (empty($meta['vsl_resume_font_woff2'])) $resume_font = $this->vsl_font_stack($meta['vsl_resume_font'] ?? 'Inter');
        [$pause_font_face_css, $pause_font] = $this->vsl_custom_font_css($id, 'pause', $meta['vsl_pause_font_woff2'] ?? '', $meta['vsl_pause_font'] ?? 'Inter');
        if (empty($meta['vsl_pause_font_woff2'])) $pause_font = $this->vsl_font_stack($meta['vsl_pause_font'] ?? 'Inter');
        $vsl_extra_font_faces_css = $overlay_font_face_css . $hook_font_face_css . $resume_font_face_css . $pause_font_face_css;
        $pause_button_style = '--itp-pause-btn:' . esc_attr($meta['vsl_pause_button_color'] ?? '#2563eb') . ';--itp-pause-btn-hover:' . esc_attr($meta['vsl_pause_button_hover_color'] ?? '#1d4ed8') . ';--itp-pause-btn-text:' . esc_attr($meta['vsl_pause_button_text_color'] ?? '#fff') . ';--itp-pause-font:' . esc_attr($pause_font) . ';';
        $overlay_public_width = $this->vsl_smartplay_safe_public_width($overlay_template, $meta['vsl_overlay_width'] ?? 42);
        $overlay_x_val = (float)($meta['vsl_overlay_x'] ?? 50);
        $overlay_y_val = (float)($meta['vsl_overlay_y'] ?? 50);
        $overlay_w_val = $overlay_public_width;
        $overlay_r_val = (float)($meta['vsl_overlay_rotate'] ?? 0);
        $overlay_mobile_x_val = (float)($meta['vsl_overlay_mobile_x'] ?? $overlay_x_val);
        $overlay_mobile_y_val = (float)($meta['vsl_overlay_mobile_y'] ?? $overlay_y_val);
        $overlay_mobile_w_val = $this->vsl_smartplay_safe_public_width($overlay_template, $meta['vsl_overlay_mobile_width'] ?? $overlay_w_val);
        $overlay_mobile_r_val = (float)($meta['vsl_overlay_mobile_rotate'] ?? $overlay_r_val);
        $overlay_pos_style = '--itp-vsl-x:' . esc_attr($overlay_x_val) . '%;--itp-vsl-y:' . esc_attr($overlay_y_val) . '%;--itp-vsl-w:' . esc_attr($overlay_w_val) . '%;--itp-vsl-r:' . esc_attr($overlay_r_val) . 'deg;--itp-design-x:' . esc_attr($overlay_x_val) . '%;--itp-design-y:' . esc_attr($overlay_y_val) . '%;--itp-design-w:' . esc_attr($overlay_w_val) . '%;--itp-design-r:' . esc_attr($overlay_r_val) . 'deg;--itp-vsl-mobile-x:' . esc_attr($overlay_mobile_x_val) . '%;--itp-vsl-mobile-y:' . esc_attr($overlay_mobile_y_val) . '%;--itp-vsl-mobile-w:' . esc_attr($overlay_mobile_w_val) . '%;--itp-vsl-mobile-r:' . esc_attr($overlay_mobile_r_val) . 'deg;--itp-overlay-pulse-duration:' . esc_attr((float)($meta['vsl_overlay_pulse_speed'] ?? 1.30)) . 's;--itp-icon-blink-duration:' . esc_attr(max(0.4, ((float)($meta['vsl_overlay_pulse_speed'] ?? 1.30)) * 1.35)) . 's;--itp-overlay-font:' . esc_attr($overlay_font) . ';--itp-overlay-opacity:' . esc_attr($overlay_opacity_val / 100) . ';--itp-overlay-opacity-pct:' . esc_attr($overlay_opacity_val) . '%;';
        // v0.18.157: the public visual group gets the exact saved geometry inline.
        // The outer button is only the click/canvas layer; the inner group is the visible Smartplay object.
        if (in_array($overlay_template, ['full', 'large'], true)) {
            $overlay_inner_pos_style = 'left:0!important;top:0!important;width:100%!important;height:100%!important;max-width:100%!important;max-height:100%!important;transform:none!important;aspect-ratio:auto!important;';
        } else {
            $overlay_inner_pos_style = 'left:' . esc_attr($overlay_x_val) . '%!important;top:' . esc_attr($overlay_y_val) . '%!important;width:' . esc_attr($overlay_w_val) . '%!important;height:auto!important;max-width:none!important;max-height:none!important;transform:translate(-50%,-50%) rotate(' . esc_attr($overlay_r_val) . 'deg)!important;';
        }
        $hook_pos_style = '--itp-vsl-x:' . esc_attr((float)($meta['vsl_hook_x'] ?? 4)) . '%;--itp-vsl-y:' . esc_attr((float)($meta['vsl_hook_y'] ?? 82)) . '%;--itp-vsl-w:' . esc_attr((float)($meta['vsl_hook_width'] ?? 32)) . '%;--itp-hook-font:' . esc_attr($hook_font) . ';';
        $cta_pos_style = '--itp-vsl-x:' . esc_attr((float)($meta['vsl_cta_x'] ?? 50)) . '%;--itp-vsl-y:' . esc_attr((float)($meta['vsl_cta_y'] ?? 82)) . '%;--itp-vsl-w:' . esc_attr((float)($meta['vsl_cta_width'] ?? 34)) . '%;';
        $initial_src = $initial_video ?: $video_url;
        $below_cta = ($cta_on && !$inside && ($cta_text || !empty($meta['vsl_cta_image'])));
        $cta_href = $cta_lead_mode ? '#itp-vsl-lead-' . (int)$id : ($cta_url ?: '#');
        $cta_anchor_attrs = $cta_lead_mode ? 'data-itp-vsl-lead-open aria-haspopup="dialog"' : 'data-itp-destination="' . esc_url($cta_url) . '" data-itp-open-target="_top" target="_top" rel="noopener"';
        $ratio_class = '';
        $aspect_style = '';
        $aspect_probe_url = $video_url ?: $initial_src;
        if ($aspect_probe_url && function_exists('attachment_url_to_postid')) {
            $att_id = attachment_url_to_postid($aspect_probe_url);
            if ($att_id) {
                $att_meta = wp_get_attachment_metadata($att_id);
                $vw = isset($att_meta['width']) ? (int)$att_meta['width'] : 0;
                $vh = isset($att_meta['height']) ? (int)$att_meta['height'] : 0;
                if ($vw > 0 && $vh > 0) {
                    $aspect_style = '--itp-vsl-aspect:' . $vw . ' / ' . $vh . ';';
                    $r = $vw / max(1, $vh);
                    if ($r < 0.82) $ratio_class = ' itp-ratio-vertical';
                    elseif ($r > 1.22) $ratio_class = ' itp-ratio-horizontal';
                    else $ratio_class = ' itp-ratio-square';
                }
            }
        }
        ob_start();
        ?>
        <?php echo $cta_font_face_css . $vsl_extra_font_faces_css; ?>
        <div class="itp-vsl-player<?php echo esc_attr($ratio_class . ($below_cta ? ' has-below-cta' : '')); ?>" style="<?php echo esc_attr($player_style . $aspect_style); ?>" data-itp-vsl data-itp-pitch="<?php echo esc_attr((int)$meta['vsl_pitch_time']); ?>">
            <div class="itp-vsl-video-area" data-itp-vsl-video-area>
            <div class="itp-vsl-video-frame <?php echo $embed_code && !$video_url ? 'has-external-embed' : ''; ?>">
                <?php if ($video_url || $initial_video) : ?>
                    <video class="itp-vsl-video" playsinline preload="auto"<?php echo $video_attrs . $video_control_attrs; ?> <?php echo $poster ? 'poster="' . esc_url($poster) . '"' : ''; ?> <?php echo ($smart_autoplay && $video_url) ? 'data-itp-main-video="' . esc_url($video_url) . '"' : ''; ?> <?php echo ($smart_autoplay && $teaser_url && $video_url) ? 'data-itp-teaser-video="' . esc_url($teaser_url) . '"' : ''; ?>>
                        <source src="<?php echo esc_url($initial_src); ?>" type="video/mp4">
                    </video>
                <?php elseif ($embed_code) : ?>
                    <div class="itp-vsl-external-embed"><?php echo $embed_code; ?></div>
                <?php else : ?>
                    <div class="itp-vsl-empty"><strong>Add a video from WordPress Media</strong><span>The VSL player is ready, but no video URL is connected yet.</span></div>
                <?php endif; ?>
                <?php if (($meta['vsl_ctrl_big_play'] ?? '0') === '1') : ?><button type="button" class="itp-vsl-big-play" data-itp-vsl-big-play aria-label="Play or pause">▶</button><?php endif; ?>
                <?php if (($meta['vsl_ctrl_back10'] ?? '0') === '1') : ?><button type="button" class="itp-vsl-skip itp-vsl-skip-back" data-itp-vsl-back10 aria-label="Back 10 seconds">↶<b>10</b></button><?php endif; ?>
                <?php if (($meta['vsl_ctrl_forward10'] ?? '0') === '1') : ?><button type="button" class="itp-vsl-skip itp-vsl-skip-forward" data-itp-vsl-forward10 aria-label="Forward 10 seconds"><b>10</b>↷</button><?php endif; ?>
                <?php if ($custom_controls_on) : ?>
                    <div class="itp-vsl-custom-controls" data-itp-vsl-custom-controls>
                        <?php if (($meta['vsl_ctrl_small_play'] ?? '0') === '1') : ?><button type="button" data-itp-vsl-small-play aria-label="Play or pause">▶</button><?php endif; ?>
                        <?php if (($meta['vsl_ctrl_volume'] ?? '0') === '1') : ?><button type="button" data-itp-vsl-volume aria-label="Mute or unmute">🔊</button><?php endif; ?>
                        <?php if (($meta['vsl_ctrl_time'] ?? '0') === '1') : ?><span class="itp-vsl-control-time" data-itp-vsl-time>0:00</span><?php endif; ?>
                        <?php if (($meta['vsl_ctrl_speed'] ?? '0') === '1') : ?><button type="button" data-itp-vsl-speed aria-label="Video speed">1×</button><?php endif; ?>
                        <?php if (($meta['vsl_ctrl_fullscreen'] ?? '0') === '1') : ?><button type="button" data-itp-vsl-fullscreen aria-label="Fullscreen">⛶</button><?php endif; ?>
                    </div>
                <?php endif; ?>
                <?php if (($meta['vsl_progress_on'] ?? '1') === '1') : ?><div class="itp-vsl-progress" style="height:<?php echo esc_attr($progress_height); ?>px"><i style="background:<?php echo esc_attr($progress_color); ?>"></i></div><?php endif; ?>
                <?php if (($meta['vsl_smart_autoplay'] ?? '1') === '1') : ?>
                    <?php
                        $public_overlay_icon_key = ($overlay_template === 'play' || $overlay_template === 'icon_play') ? 'play' : (($overlay_template === 'compact' || $overlay_template === 'icon_blink') ? 'blink' : 'sound');
                        $public_overlay_icon_url = plugins_url('assets/invalid-traffic-smartplay-' . $public_overlay_icon_key . '.svg', __FILE__);
                    ?>
                    <button type="button" class="itp-vsl-overlay itp-smartplay-object itp-sp180 template-<?php echo esc_attr($overlay_template); ?><?php echo ($overlay_template === 'custom' && !empty($meta['vsl_overlay_image'])) ? ' has-custom-image' : ''; ?><?php echo esc_attr($overlay_pulse); ?>" style="background:<?php echo esc_attr($meta['vsl_overlay_color']); ?>;color:<?php echo esc_attr($meta['vsl_overlay_text_color']); ?>;--itp-overlay-bg:<?php echo esc_attr($meta['vsl_overlay_color']); ?>;--itp-overlay-fg:<?php echo esc_attr($meta['vsl_overlay_text_color']); ?>;<?php echo esc_attr($overlay_pos_style); ?>" data-itp-vsl-overlay data-itp-freepos data-itp-sp180>
                        <span class="itp-smartplay-inner" style="<?php echo esc_attr($overlay_inner_pos_style); ?>" data-itp-sp-x="<?php echo esc_attr($overlay_x_val); ?>" data-itp-sp-y="<?php echo esc_attr($overlay_y_val); ?>" data-itp-sp-w="<?php echo esc_attr($overlay_w_val); ?>" data-itp-sp-r="<?php echo esc_attr($overlay_r_val); ?>">
                            <?php if ($overlay_template === 'custom' && !empty($meta['vsl_overlay_image'])) : ?>
                                <img class="itp-vsl-custom-overlay-image" src="<?php echo esc_url($meta['vsl_overlay_image']); ?>" alt="">
                            <?php elseif (in_array($overlay_template, ['full', 'large'], true)) : ?>
                                <span class="itp-vsl-overlay-top"><?php echo esc_html((string)($meta['vsl_overlay_top'] ?? '')); ?></span>
                                <img class="itp-vsl-itbrand-icon itp-vsl-itbrand-icon-<?php echo esc_attr($public_overlay_icon_key); ?>" src="<?php echo esc_url($public_overlay_icon_url); ?>" alt="" aria-hidden="true">
                                <span class="itp-vsl-overlay-bottom"><?php echo esc_html((string)($meta['vsl_overlay_bottom'] ?? '')); ?></span>
                            <?php else : ?>
                                <?php echo $this->vsl_smartplay_overlay_svg($overlay_template, (string)($meta['vsl_overlay_top'] ?? ''), (string)($meta['vsl_overlay_bottom'] ?? ''), $public_overlay_icon_url, (string)($meta['vsl_overlay_color'] ?? '#f79a27'), (string)($meta['vsl_overlay_text_color'] ?? '#ffffff'), (int)($meta['vsl_overlay_opacity'] ?? 92)); ?>
                            <?php endif; ?>
                        </span>
                    </button>
                <?php endif; ?>
                <?php if (($meta['vsl_pause_on'] ?? '0') === '1' && !empty($meta['vsl_pause_image'])) : ?>
                    <div class="itp-vsl-pause-card pos-<?php echo esc_attr($meta['vsl_pause_button_position'] ?? 'bottom'); ?>" data-itp-vsl-pause-card hidden>
                        <button type="button" class="itp-vsl-pause-bg" data-itp-vsl-resume-area><img src="<?php echo esc_url($meta['vsl_pause_image']); ?>" alt=""></button>
                        <?php if (($meta['vsl_pause_button_on'] ?? '0') === '1') : ?><a class="itp-vsl-pause-button size-<?php echo esc_attr($meta['vsl_pause_button_size'] ?? 'medium'); ?>" style="<?php echo esc_attr($pause_button_style); ?>" href="<?php echo esc_url($meta['vsl_pause_button_url'] ?: '#'); ?>" data-itp-destination="<?php echo esc_url($meta['vsl_pause_button_url']); ?>" data-itp-open-target="_top" target="_top" rel="noopener"><?php echo esc_html($meta['vsl_pause_button_text'] ?: 'Continue'); ?></a><?php endif; ?>
                    </div>
                <?php endif; ?>
                <?php if (($meta['vsl_hook_on'] ?? '0') === '1' && (!empty($meta['vsl_hook_text']) || !empty($meta['vsl_hook_image']))) : ?><div class="itp-vsl-hook <?php echo esc_attr($hook_class); ?>" style="opacity:<?php echo esc_attr(((int)($meta['vsl_hook_opacity'] ?? 100)) / 100); ?>;<?php echo esc_attr($hook_pos_style); ?>" data-itp-vsl-hook data-itp-freepos hidden><?php if (!empty($meta['vsl_hook_image'])) : ?><img src="<?php echo esc_url($meta['vsl_hook_image']); ?>" alt=""><?php endif; ?><span><?php echo esc_html($meta['vsl_hook_text']); ?></span></div><?php endif; ?>
                <?php if (($meta['vsl_resume_on'] ?? '0') === '1') : ?>
                    <div class="itp-vsl-resume-panel" data-itp-vsl-resume-panel style="<?php echo esc_attr('--itp-resume-bg:' . ($meta['vsl_resume_bg'] ?? '#581c87') . ';--itp-resume-text:' . ($meta['vsl_resume_text_color'] ?? '#ffffff') . ';--itp-resume-font:' . $this->vsl_font_stack($meta['vsl_resume_font'] ?? 'Inter') . ';'); ?>" hidden>
                        <strong><?php echo esc_html($meta['vsl_resume_message'] ?: 'You already started watching this video'); ?></strong>
                        <button type="button" data-itp-vsl-resume-continue><span>▶</span><?php echo esc_html($meta['vsl_resume_continue_text'] ?: 'Continue watching?'); ?></button>
                        <button type="button" data-itp-vsl-resume-restart><span>↻</span><?php echo esc_html($meta['vsl_resume_restart_text'] ?: 'Start from the beginning?'); ?></button>
                    </div>
                    <div class="itp-vsl-resume-badge" data-itp-vsl-resume-badge hidden>Continue watching</div>
                <?php endif; ?>
                <?php if ($cta_on && $inside && ($cta_text || !empty($meta['vsl_cta_image']))) : ?><a class="itp-vsl-cta inside <?php echo esc_attr($cta_classes); ?> <?php echo !empty($meta['vsl_cta_image']) ? 'has-image' : ''; ?>" style="<?php echo esc_attr($cta_style . $cta_pos_style); ?>" href="<?php echo esc_url($cta_href); ?>" data-itp-vsl-cta <?php echo $cta_anchor_attrs; ?> data-itp-freepos hidden><?php if (!empty($meta['vsl_cta_image'])) : ?><img src="<?php echo esc_url($meta['vsl_cta_image']); ?>" alt="<?php echo esc_attr($cta_text); ?>"><?php else : ?><?php echo esc_html($cta_text); ?><?php endif; ?></a><?php endif; ?>
            </div>
            </div>
            <?php if ($below_cta) : ?><div class="itp-vsl-below-cta-area" data-itp-vsl-below-cta-area hidden><a class="itp-vsl-cta below <?php echo esc_attr($cta_classes); ?> <?php echo !empty($meta['vsl_cta_image']) ? 'has-image' : ''; ?>" style="<?php echo esc_attr($cta_style); ?>" href="<?php echo esc_url($cta_href); ?>" data-itp-vsl-cta <?php echo $cta_anchor_attrs; ?> hidden><?php if (!empty($meta['vsl_cta_image'])) : ?><img src="<?php echo esc_url($meta['vsl_cta_image']); ?>" alt="<?php echo esc_attr($cta_text); ?>"><?php else : ?><?php echo esc_html($cta_text); ?><?php endif; ?></a></div><?php endif; ?>
            <?php if ($cta_lead_mode) : ?>
                <div id="itp-vsl-lead-<?php echo (int)$id; ?>" class="itp-vsl-lead-modal" data-itp-vsl-lead-modal hidden>
                    <div class="itp-vsl-lead-backdrop" data-itp-vsl-lead-close></div>
                    <form class="itp-vsl-lead-box <?php echo (($meta['vsl_lead_close_style'] ?? 'round') === 'plain') ? 'itp-lead-close-plain' : 'itp-lead-close-round'; ?>" style="<?php echo esc_attr($lead_style); ?>" data-itp-vsl-lead-form data-itp-redirect="<?php echo esc_url($cta_action === 'lead_redirect' ? $cta_url : ''); ?>">
                        <button type="button" class="itp-vsl-lead-close" data-itp-vsl-lead-close aria-label="Close">×</button>
                        <h3><?php echo esc_html($meta['vsl_lead_title'] ?? 'Get the next step by email'); ?></h3>
                        <p><?php echo esc_html($meta['vsl_lead_copy'] ?? 'Enter your email below to continue.'); ?></p>
                        <?php if (($meta['vsl_lead_name_on'] ?? '0') === '1') : ?><input type="text" name="name" placeholder="<?php echo esc_attr($meta['vsl_lead_name_placeholder'] ?? 'Full name'); ?>"><?php endif; ?>
                        <input type="email" name="email" required placeholder="<?php echo esc_attr($meta['vsl_lead_email_placeholder'] ?? 'you@example.com'); ?>">
                        <?php if (($meta['vsl_lead_phone_on'] ?? '0') === '1') : ?><input type="tel" name="phone" placeholder="<?php echo esc_attr($meta['vsl_lead_phone_placeholder'] ?? 'Phone number'); ?>"><?php endif; ?>
                        <button type="submit" class="itp-vsl-lead-submit"><?php echo esc_html($meta['vsl_lead_button_text'] ?? 'Continue by email'); ?></button>
                        <div class="itp-vsl-lead-message" data-itp-vsl-lead-message hidden><?php echo esc_html($meta['vsl_lead_success'] ?? 'Thanks. Your email was received.'); ?></div>
                    </form>
                </div>
            <?php endif; ?>
        </div>
        <?php
        return ob_get_clean();
    }

    private function render_landing_page_template($id, $meta, $settings) {
        nocache_headers();
        $title = $meta['landing_headline'] ?: get_the_title($id);
        ?><!doctype html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex,nofollow">
<title><?php echo esc_html($title); ?></title>
<?php if (!empty($meta['meta_description'])) : ?><meta name="description" content="<?php echo esc_attr($meta['meta_description']); ?>"><?php endif; ?>
<?php echo $this->tracking_head_markup($meta); ?>
<?php wp_head(); ?>
<link rel="stylesheet" href="<?php echo esc_url(plugin_dir_url(__FILE__) . 'assets/bridge.css?ver=' . self::VERSION); ?>">
</head>
<body class="itp-bridge-body itp-template-landing_page">
<main class="itp-vsl-landing-page">
    <section class="itp-native-landing-hero">
        <span><?php echo esc_html($this->landing_niche_label($meta['landing_niche'] ?? 'custom')); ?></span>
        <h1><?php echo esc_html($meta['landing_headline'] ?: 'A ready-to-edit campaign page'); ?></h1>
        <?php if (!empty($meta['landing_copy'])) : ?><p><?php echo esc_html($meta['landing_copy']); ?></p><?php endif; ?>
        <?php if (!empty($meta['landing_button_url'])) : ?><a class="itp-native-landing-btn" href="<?php echo esc_url($meta['landing_button_url']); ?>" data-itp-destination="<?php echo esc_url($meta['landing_button_url']); ?>"><?php echo esc_html($meta['landing_button_text'] ?: 'Continue'); ?></a><?php endif; ?>
    </section>
    <?php echo $this->native_landing_sections_html($meta); ?>
    <footer class="itp-bridge-footer itp-vsl-footer">
        <?php if ($meta['footer_note']) : ?><p><?php echo esc_html($meta['footer_note']); ?></p><?php endif; ?>
        <nav><?php echo wp_kses_post($this->policy_links_html()); ?></nav>
        <?php if ($settings['powered_by'] === '1') : ?><small>Powered by Invalid Traffic</small><?php endif; ?>
    </footer>
</main>
<script>window.ITP_TRACK=<?php echo wp_json_encode(['ajax' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('itp_track_' . $id), 'id' => $id, 'delay' => 0]); ?>;</script>
<?php echo $this->tracking_runtime_script($meta, $id); ?>
<script src="<?php echo esc_url(plugin_dir_url(__FILE__) . 'assets/it.js?ver=' . self::VERSION); ?>" defer></script>
<?php echo $this->tracking_footer_markup($meta); ?>
<?php wp_footer(); ?>
</body></html><?php
        exit;
    }

    private function native_landing_sections_html($meta) {
        $button_url = esc_url($meta['landing_button_url'] ?? '');
        $button_text = esc_html($meta['landing_button_text'] ?: 'Continue');
        ob_start();
        ?>
        <section class="itp-native-section itp-native-benefits">
            <div><strong>01</strong><h2>Fast campaign page</h2><p>Start with a clean structure instead of a blank page.</p></div>
            <div><strong>02</strong><h2>Traffic signal layer</h2><p>UTMs, source, click IDs, device and engagement stay connected to this page.</p></div>
            <div><strong>03</strong><h2>Edit the message</h2><p>Use this as a template and adjust the copy for your offer or niche.</p></div>
        </section>
        <section class="itp-native-section itp-native-proof"><h2>Built for paid traffic workflows</h2><p>Use this page for presell, VSL, affiliate, course, finance, lead capture or campaign segmentation.</p><?php if ($button_url) : ?><a class="itp-native-landing-btn" data-itp-destination="<?php echo esc_url($button_url); ?>" href="<?php echo esc_url($button_url); ?>"><?php echo $button_text; ?></a><?php endif; ?></section>
        <?php
        return ob_get_clean();
    }

    private function landing_niche_label($key) {
        $map = ['custom'=>'Custom','affiliate'=>'Affiliate Offer','digital_product'=>'Digital Product','course'=>'Online Course','finance'=>'Finance','insurance'=>'Insurance','agency'=>'Agency','clinic'=>'Clinic','lead_capture'=>'Lead Capture'];
        return $map[$key] ?? 'Custom';
    }

    private function primary_destination($meta) {
        if (!empty($meta['top_cta_url'])) return $meta['top_cta_url'];
        foreach ((array)($meta['cards'] ?? []) as $card) {
            if (!empty($card['url'])) return $card['url'];
        }
        if (!empty($meta['secondary_cta_url'])) return $meta['secondary_cta_url'];
        return home_url('/');
    }

    private function is_direct_image_url($url) {
        $path = (string)wp_parse_url($url, PHP_URL_PATH);
        return (bool)preg_match('/\.(?:jpg|jpeg|png|gif|webp|svg)$/i', $path);
    }

    private function cookie_preview_image_url($meta, $destination) {
        $src = !empty($meta['background_image']) ? $meta['background_image'] : $destination;
        if (!$src) return '';
        if ($this->is_direct_image_url($src)) return $src;
        return 'https://s.wordpress.com/mshots/v1/' . rawurlencode($src) . '?w=1600';
    }

    private function render_cookie_bridge_template($id, $meta, $settings, $delay) {
        nocache_headers();
        $destination = $this->primary_destination($meta);
        $bg = $this->cookie_preview_image_url($meta, $destination);
        $title = $meta['cookie_title'] ?: 'Continue to the page';
        $copy = $meta['cookie_copy'] ?: 'You are about to open the selected page. This clean entry step helps verify traffic quality before continuing.';
        $accept = $meta['cookie_accept_label'] ?: 'Continue';
        $close = $meta['cookie_close_label'] ?: 'Back';
        $policy_label = $meta['cookie_policy_label'] ?: 'Privacy Policy';
        $accept_url = !empty($meta['cookie_accept_url']) ? $meta['cookie_accept_url'] : $destination;
        $close_url = !empty($meta['cookie_close_url']) ? $meta['cookie_close_url'] : home_url('/');
        $policy_url = !empty($meta['cookie_policy_url']) ? $meta['cookie_policy_url'] : $this->privacy_policy_url();
        ?><!doctype html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex,nofollow">
<title><?php echo esc_html($meta['headline'] ?: get_the_title($id)); ?></title>
<?php if (!empty($meta['meta_description'])) : ?><meta name="description" content="<?php echo esc_attr($meta['meta_description']); ?>"><?php endif; ?>
<?php echo $this->tracking_head_markup($meta); ?>
<?php wp_head(); ?>
<link rel="stylesheet" href="<?php echo esc_url(plugin_dir_url(__FILE__) . 'assets/bridge.css?ver=' . self::VERSION); ?>">
</head>
<?php
$cookie_style_vars = [
    '--itp-cookie-modal-bg:' . ($meta['cookie_modal_bg'] ?: '#f8fafc'),
    '--itp-cookie-modal-text:' . ($meta['cookie_modal_text'] ?: '#111827'),
    '--itp-cookie-accept-bg:' . ($meta['cookie_accept_bg'] ?: '#1683f7'),
    '--itp-cookie-accept-text:' . ($meta['cookie_accept_text'] ?: '#ffffff'),
    '--itp-cookie-link:' . ($meta['cookie_link_color'] ?: '#2777c7'),
    '--itp-cookie-overlay:' . (max(0, min(90, (int)($meta['cookie_overlay_opacity'] ?? 58))) / 100),
    '--itp-cookie-blur:' . max(0, min(12, (int)($meta['cookie_blur'] ?? 2))) . 'px',
];
?>
<body class="itp-bridge-body itp-template-cookie_modal" style="<?php echo esc_attr(implode(';', $cookie_style_vars)); ?>" data-itp-delay="<?php echo esc_attr($delay); ?>">
<div class="itp-delay-pill" data-itp-delay-pill hidden>Continue available in <span><?php echo esc_html($delay); ?></span> seconds...</div>
<main class="itp-cookie-page">
    <div class="itp-cookie-bg <?php echo $bg ? 'has-preview' : 'no-preview'; ?>" <?php echo $bg ? 'style="background-image:url(' . esc_url($bg) . ')"' : ''; ?>>
        <?php if ($bg) : ?><img class="itp-cookie-bg-img" src="<?php echo esc_url($bg); ?>" alt="" loading="eager"><?php endif; ?>
        <?php if (!$bg) : ?><div class="itp-cookie-fake-page"><i></i><i></i><i></i><b></b><b></b><b></b></div><?php endif; ?>
    </div>
    <div class="itp-cookie-dim"></div>
    <section class="itp-cookie-modal" aria-label="Cookie notice">
        <a class="itp-cookie-close" href="<?php echo esc_url($close_url); ?>"><?php echo esc_html($close); ?></a>
        <h1><?php echo esc_html($title); ?></h1>
        <p><?php echo esc_html($copy); ?> <?php if ($policy_label) : ?><a href="<?php echo esc_url($policy_url); ?>" target="_blank" rel="noopener"><?php echo esc_html($policy_label); ?></a><?php endif; ?></p>
        <a class="itp-cookie-accept" data-itp-destination="<?php echo esc_url($accept_url); ?>" href="<?php echo esc_url($accept_url); ?>"><?php echo esc_html($accept); ?></a>
    </section>
    <footer class="itp-cookie-footer">
        <?php if ($meta['footer_note']) : ?><p><?php echo esc_html($meta['footer_note']); ?></p><?php endif; ?>
        <nav><?php echo wp_kses_post($this->policy_links_html()); ?></nav>
        <?php if ($settings['powered_by'] === '1') : ?><small>Powered by Invalid Traffic</small><?php endif; ?>
    </footer>
</main>
<script>window.ITP_TRACK=<?php echo wp_json_encode(['ajax' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('itp_track_' . $id), 'id' => $id, 'delay' => $delay]); ?>;</script>
<?php echo $this->tracking_runtime_script($meta, $id); ?>
<script src="<?php echo esc_url(plugin_dir_url(__FILE__) . 'assets/it.js?ver=' . self::VERSION); ?>" defer></script>
<?php echo $this->tracking_footer_markup($meta); ?>
<?php wp_footer(); ?>
</body></html><?php
        exit;
    }

    private function policy_links_html() {
        $s = $this->settings();
        $links = [];
        foreach (['contact_page_id' => 'Contact', 'privacy_page_id' => 'Privacy Policy', 'terms_page_id' => 'Terms of Use'] as $key => $label) {
            $pid = (int)($s[$key] ?? 0);
            if ($pid && get_post_status($pid)) {
                $links[] = '<a href="' . esc_url(get_permalink($pid)) . '" target="_blank" rel="noopener">' . esc_html($label) . '</a>';
            }
        }
        return implode('<span>·</span>', $links);
    }

    public function submit_lead() {
        $presell_id = absint($_POST['presell_id'] ?? 0);
        if (!$presell_id || get_post_type($presell_id) !== self::CPT) {
            wp_send_json_error(['message' => 'Invalid bridge.'], 400);
        }
        $nonce = sanitize_text_field(wp_unslash($_POST['nonce'] ?? ''));
        if (!wp_verify_nonce($nonce, 'itp_track_' . $presell_id)) {
            wp_send_json_error(['message' => 'Invalid nonce.'], 403);
        }
        $email = sanitize_email(wp_unslash($_POST['email'] ?? ''));
        if (!$email || !is_email($email)) {
            wp_send_json_error(['message' => 'Please enter a valid email.'], 400);
        }
        global $wpdb;
        $table = $wpdb->prefix . self::LEAD_TABLE;
        $wpdb->insert($table, [
            'presell_id' => $presell_id,
            'session_id' => sanitize_text_field(wp_unslash($_POST['session_id'] ?? '')),
            'visitor_id' => sanitize_text_field(wp_unslash($_POST['visitor_id'] ?? '')),
            'email' => $email,
            'name' => sanitize_text_field(wp_unslash($_POST['name'] ?? '')),
            'phone' => sanitize_text_field(wp_unslash($_POST['phone'] ?? '')),
            'source' => sanitize_text_field(wp_unslash($_POST['source'] ?? '')),
            'medium' => sanitize_text_field(wp_unslash($_POST['medium'] ?? '')),
            'campaign' => sanitize_text_field(wp_unslash($_POST['campaign'] ?? '')),
            'referrer' => esc_url_raw(wp_unslash($_POST['referrer'] ?? '')),
            'landing_url' => esc_url_raw(wp_unslash($_POST['landing_url'] ?? '')),
            'destination' => esc_url_raw(wp_unslash($_POST['destination'] ?? '')),
            'created_at' => current_time('mysql', true),
        ]);
        if (!$wpdb->insert_id) {
            wp_send_json_error(['message' => 'Could not save lead.'], 500);
        }
        wp_send_json_success(['message' => 'Lead saved.']);
    }


    private function pixel_webhook_history_rows($settings = null) {
        $settings = is_array($settings) ? $settings : $this->global_pixel_settings();
        $rows = isset($settings['webhook_history']) && is_array($settings['webhook_history']) ? $settings['webhook_history'] : [];
        $out = [];
        foreach ($rows as $row) {
            if (!is_array($row)) continue;
            $out[] = [
                'time' => sanitize_text_field((string)($row['time'] ?? '')),
                'status' => sanitize_text_field((string)($row['status'] ?? 'received')),
                'counted' => !empty($row['counted']) && $row['counted'] !== '0' ? '1' : '0',
                'source' => sanitize_text_field((string)($row['source'] ?? 'webhook')),
                'event' => sanitize_text_field((string)($row['event'] ?? '')),
                'bridge_id' => absint($row['bridge_id'] ?? 0),
                'bridge_title' => sanitize_text_field((string)($row['bridge_title'] ?? '')),
                'value' => sanitize_text_field((string)($row['value'] ?? '')),
                'currency' => sanitize_text_field((string)($row['currency'] ?? '')),
                'external_id' => sanitize_text_field((string)($row['external_id'] ?? '')),
                'message' => sanitize_text_field((string)($row['message'] ?? '')),
            ];
        }
        return array_slice($out, 0, 80);
    }

    private function add_pixel_webhook_history($entry) {
        $settings = $this->global_pixel_settings();
        $rows = isset($settings['webhook_history']) && is_array($settings['webhook_history']) ? $settings['webhook_history'] : [];
        $bridge_id = absint($entry['bridge_id'] ?? 0);
        $item = [
            'time' => current_time('mysql', true),
            'status' => sanitize_text_field((string)($entry['status'] ?? 'received')),
            'counted' => !empty($entry['counted']) && $entry['counted'] !== '0' ? '1' : '0',
            'source' => sanitize_text_field((string)($entry['source'] ?? 'webhook')),
            'event' => sanitize_text_field((string)($entry['event'] ?? '')),
            'bridge_id' => $bridge_id,
            'bridge_title' => sanitize_text_field((string)($entry['bridge_title'] ?? ($bridge_id ? get_the_title($bridge_id) : ''))),
            'value' => sanitize_text_field((string)($entry['value'] ?? '')),
            'currency' => sanitize_text_field((string)($entry['currency'] ?? '')),
            'external_id' => sanitize_text_field((string)($entry['external_id'] ?? '')),
            'message' => sanitize_text_field((string)($entry['message'] ?? '')),
        ];
        array_unshift($rows, $item);
        $settings['webhook_history'] = array_slice($rows, 0, 80);
        update_option(self::PIXEL_OPT, $settings, false);
        return $item;
    }

    private function conversion_payload_scalar($data, $keys) {
        $data = is_array($data) ? $data : [];
        foreach ((array)$keys as $key) {
            if (isset($data[$key]) && !is_array($data[$key]) && !is_object($data[$key]) && trim((string)$data[$key]) !== '') {
                return (string)$data[$key];
            }
        }
        foreach ($data as $k => $v) {
            if (is_array($v)) {
                $found = $this->conversion_payload_scalar($v, $keys);
                if ($found !== '') return $found;
            }
        }
        return '';
    }

    private function conversion_payload_top_scalar($data, $keys) {
        $data = is_array($data) ? $data : [];
        foreach ((array)$keys as $key) {
            if (isset($data[$key]) && !is_array($data[$key]) && !is_object($data[$key]) && trim((string)$data[$key]) !== '') {
                return (string)$data[$key];
            }
        }
        return '';
    }

    private function conversion_payload_path($data, $path) {
        $cur = is_array($data) ? $data : [];
        foreach (explode('.', (string)$path) as $part) {
            if ($part === '') continue;
            if (!is_array($cur) || !array_key_exists($part, $cur)) return '';
            $cur = $cur[$part];
        }
        if (is_array($cur) || is_object($cur)) return '';
        return trim((string)$cur);
    }

    private function conversion_payload_first_path($data, $paths) {
        foreach ((array)$paths as $path) {
            $value = $this->conversion_payload_path($data, $path);
            if ($value !== '') return $value;
        }
        return '';
    }

    private function conversion_set_if_missing(&$norm, $key, $value) {
        if (is_array($value) || is_object($value)) return;
        $value = trim((string)$value);
        if ($value === '') return;
        if (!isset($norm[$key]) || trim((string)$norm[$key]) === '') {
            $norm[$key] = $value;
        }
    }

    private function conversion_platform_registry() {
        return [
            'hotmart' => ['hotmart'],
            'kiwify' => ['kiwify'],
            'perfect_pay' => ['perfectpay','perfect pay'],
            'braip' => ['braip'],
            'stripe' => ['stripe'],
            'paypal' => ['paypal','pay_pal'],
            'adyen' => ['adyen'],
            'clickbank' => ['clickbank','click bank'],
            'buygoods' => ['buygoods','buy goods'],
            'digistore24' => ['digistore24','digistore'],
            'lemonsqueezy' => ['lemonsqueezy','lemon_squeezy','lemon squeezy'],
            'paddle' => ['paddle'],
            'gumroad' => ['gumroad'],
            'woocommerce' => ['woocommerce','woo commerce','woo'],
            'shopify' => ['shopify'],
            'magento' => ['magento'],
            'prestashop' => ['prestashop','presta shop'],
            'thrivecart' => ['thrivecart','thrive cart'],
            'paykickstart' => ['paykickstart','pay kickstart'],
            'samcart' => ['samcart','sam cart'],
            'clickfunnels' => ['clickfunnels','click funnels'],
            'jvzoo' => ['jvzoo','jv zoo'],
            'warriorplus' => ['warriorplus','warrior plus'],
            'partnerstack' => ['partnerstack','partner stack'],
            'impact' => ['impact','impact.com','impact radius'],
            'awin' => ['awin'],
            'shareasale' => ['shareasale','share a sale'],
            'cj_affiliate' => ['cj','commission junction','cj affiliate'],
            'linktrust' => ['linktrust','link trust'],
            'maxbounty' => ['maxbounty','max bounty'],
            'maxweb' => ['maxweb','max web'],
            'crakrevenue' => ['crakrevenue','crak revenue'],
            'adcombo' => ['adcombo','ad combo'],
            'kajabi' => ['kajabi'],
            'teachable' => ['teachable'],
            'mollie' => ['mollie'],
            'checkout_com' => ['checkout.com','checkoutcom','checkout'],
            'braintree' => ['braintree'],
            'square' => ['square'],
            'worldpay' => ['worldpay','world pay'],
            'klarna' => ['klarna'],
            'gocardless' => ['gocardless','go cardless'],
            'authorize_net' => ['authorize.net','authorizenet','authorize net'],
            'payhip' => ['payhip'],
            'podia' => ['podia'],
            'thinkific' => ['thinkific'],
            'systeme' => ['systeme.io','systemeio','systeme'],
            'kartra' => ['kartra'],
            'stan_store' => ['stan store','stanstore'],
            'tapfiliate' => ['tapfiliate'],
            'rewardful' => ['rewardful'],
            'firstpromoter' => ['firstpromoter','first promoter'],
            'tolt' => ['tolt'],
            'reditus' => ['reditus'],
            'rakuten' => ['rakuten','rakuten advertising'],
            'flexoffers' => ['flexoffers','flex offers'],
            'tradedoubler' => ['tradedoubler','trade doubler'],
            'webgains' => ['webgains','web gains'],
            'admitad' => ['admitad'],
            'ebay_partner_network' => ['ebay','ebay partner network'],
            'amazon_associates' => ['amazon','amazon associates'],
            'eduzz' => ['eduzz'],
            'monetizze' => ['monetizze'],
            'ticto' => ['ticto'],
            'cartpanda' => ['cartpanda','cart panda'],
            'yampi' => ['yampi'],
            'lastlink' => ['lastlink','last link'],
            'doppus' => ['doppus'],
            'mercado_pago' => ['mercado pago','mercadopago'],
            'pagseguro' => ['pagseguro','pag seguro','pagbank'],
            'asaas' => ['asaas'],
        ];
    }

    private function conversion_detect_platform($data, $origin = 'webhook') {
        $data = is_array($data) ? $data : [];
        $hints = [];
        foreach (['source','platform','provider','gateway','processor','payment_processor','network','affiliate_network','vendor','integration'] as $key) {
            if (isset($data[$key]) && !is_array($data[$key]) && !is_object($data[$key])) $hints[] = (string)$data[$key];
        }
        foreach (['event','event_name','type','topic','topic_name','kind','name'] as $key) {
            if (isset($data[$key]) && !is_array($data[$key]) && !is_object($data[$key])) $hints[] = (string)$data[$key];
        }
        $raw = strtolower(implode(' ', $hints));
        foreach ($this->conversion_platform_registry() as $source => $aliases) {
            foreach ($aliases as $alias) {
                if ($alias !== '' && strpos($raw, strtolower($alias)) !== false) return $source;
            }
        }
        if (isset($data['data']) && is_array($data['data']) && isset($data['data']['purchase'])) return 'hotmart';
        if ($this->conversion_payload_path($data, 'data.object.object') === 'checkout.session') return 'stripe';
        if (strpos(strtolower((string)$this->conversion_payload_top_scalar($data, ['type'])), 'stripe') !== false) return 'stripe';
        if ($this->conversion_payload_path($data, 'resource.amount.value') !== '' || strpos(strtolower((string)$this->conversion_payload_top_scalar($data, ['event_type'])), 'payment.') === 0) return 'paypal';
        if ($this->conversion_payload_path($data, 'data.attributes.total') !== '' && ($this->conversion_payload_path($data, 'meta.event_name') !== '' || $this->conversion_payload_path($data, 'data.type') !== '')) return 'lemonsqueezy';
        if ($this->conversion_payload_path($data, 'data.details.totals.total') !== '' || strpos(strtolower((string)$this->conversion_payload_top_scalar($data, ['event_type'])), 'transaction.') === 0) return 'paddle';
        if ($this->conversion_payload_path($data, 'amount.value') !== '' && $this->conversion_payload_top_scalar($data, ['pspReference','merchantReference','eventCode']) !== '') return 'adyen';
        if ($this->conversion_payload_path($data, 'order.amount.value') !== '' || strpos(strtolower((string)$this->conversion_payload_top_scalar($data, ['resource'])), 'payment') !== false) return 'mollie';
        if ($this->conversion_payload_top_scalar($data, ['order_number','financial_status','total_price']) !== '') return 'shopify';
        if ($this->conversion_payload_top_scalar($data, ['order_key','billing','shipping']) !== '' && $this->conversion_payload_top_scalar($data, ['total','currency']) !== '') return 'woocommerce';
        if ($this->conversion_payload_top_scalar($data, ['increment_id','grand_total','order_currency_code']) !== '') return 'magento';
        return '';
    }

    private function conversion_normalize_money_value($value, $source = '') {
        $value = trim((string)$value);
        if ($value === '') return '';
        $value = preg_replace('/[^0-9.,-]/', '', $value);
        if ($value === '') return '';
        $source = strtolower((string)$source);
        if (in_array($source, ['stripe','shopify','lemonsqueezy','paddle','square','adyen','checkout_com','braintree'], true)) {
            $clean = str_replace(',', '.', $value);
            if (is_numeric($clean) && abs((float)$clean) >= 1000 && strpos($clean, '.') === false) {
                return (string)((float)$clean / 100);
            }
        }
        return $value;
    }

    private function normalize_conversion_payload($data, $origin = 'webhook') {
        $data = is_array($data) ? $data : [];
        $norm = $data;
        $event_raw = $this->conversion_payload_top_scalar($data, ['event_name','event','type','event_type','topic','status','eventCode','kind']);
        $event_upper = strtoupper(trim((string)$event_raw));
        $adapter = '';

        $detected_source = $this->conversion_detect_platform($data, $origin);
        if ($detected_source !== '') {
            $norm['source'] = $norm['source'] ?? $detected_source;
            $norm['provider'] = $norm['provider'] ?? $detected_source;
            $adapter = $detected_source;
        }

        // Hotmart Webhook v2 adapter. Keep only purchase fields needed by Invalid Traffic.
        if (isset($data['data']) && is_array($data['data']) && (isset($data['data']['purchase']) || strpos($event_upper, 'PURCHASE') !== false)) {
            $purchase = isset($data['data']['purchase']) && is_array($data['data']['purchase']) ? $data['data']['purchase'] : [];
            $norm['source'] = 'hotmart';
            $norm['provider'] = 'hotmart';
            $norm['adapter'] = 'hotmart_v2';
            $adapter = 'hotmart_v2';
            $norm['provider_event'] = $event_raw ?: ($norm['provider_event'] ?? '');
            $this->conversion_set_if_missing($norm, 'status', $purchase['status'] ?? '');
            if (!empty($data['data']['product']['name'])) $norm['product_name'] = sanitize_text_field((string)$data['data']['product']['name']);
            $this->conversion_set_if_missing($norm, 'transaction_id', $purchase['transaction'] ?? '');
            $this->conversion_set_if_missing($norm, 'value', $this->conversion_payload_first_path($data, ['data.purchase.price.value','data.purchase.full_price.value','data.purchase.original_offer_price.value']));
            $this->conversion_set_if_missing($norm, 'currency', $this->conversion_payload_first_path($data, ['data.purchase.price.currency_value','data.purchase.full_price.currency_value','data.purchase.original_offer_price.currency_value']));
            if (strpos($event_upper, 'PURCHASE_APPROVED') !== false || strpos($event_upper, 'PURCHASE_COMPLETED') !== false || strtoupper((string)($purchase['status'] ?? '')) === 'APPROVED') {
                $norm['event_name'] = 'purchase';
            }
            if (!empty($purchase['sckPaymentLink'])) $norm['tracking_id'] = sanitize_text_field((string)$purchase['sckPaymentLink']);
        }

        // Stripe Checkout adapter for raw webhook payloads. Browser success-page pixel still works separately.
        $stripe_type = strtolower((string)$this->conversion_payload_top_scalar($data, ['type']));
        $stripe_object = strtolower((string)$this->conversion_payload_path($data, 'data.object.object'));
        if (strpos($stripe_type, 'checkout.session.completed') !== false || strpos($stripe_type, 'payment_intent.succeeded') !== false || $stripe_object === 'checkout.session' || $stripe_object === 'payment_intent') {
            $norm['source'] = 'stripe';
            $norm['provider'] = 'stripe';
            $norm['adapter'] = 'stripe_checkout';
            $adapter = 'stripe_checkout';
            $norm['provider_event'] = $stripe_type ?: ($norm['provider_event'] ?? '');
            $this->conversion_set_if_missing($norm, 'transaction_id', $this->conversion_payload_first_path($data, ['data.object.id','id']));
            $this->conversion_set_if_missing($norm, 'value', $this->conversion_normalize_money_value($this->conversion_payload_first_path($data, ['data.object.amount_total','data.object.amount_received','data.object.amount','amount_total']), 'stripe'));
            $this->conversion_set_if_missing($norm, 'currency', strtoupper($this->conversion_payload_first_path($data, ['data.object.currency','currency'])));
            $norm['event_name'] = 'purchase';
        }

        // PayPal REST webhooks.
        if (($detected_source === 'paypal') || $this->conversion_payload_path($data, 'resource.amount.value') !== '') {
            $norm['source'] = 'paypal';
            $norm['provider'] = 'paypal';
            $norm['adapter'] = 'paypal_webhook';
            $adapter = 'paypal_webhook';
            $this->conversion_set_if_missing($norm, 'provider_event', $this->conversion_payload_top_scalar($data, ['event_type','type','event']));
            $this->conversion_set_if_missing($norm, 'transaction_id', $this->conversion_payload_first_path($data, ['resource.id','resource.supplementary_data.related_ids.order_id','id']));
            $this->conversion_set_if_missing($norm, 'value', $this->conversion_payload_first_path($data, ['resource.amount.value','resource.seller_receivable_breakdown.gross_amount.value','amount.value']));
            $this->conversion_set_if_missing($norm, 'currency', $this->conversion_payload_first_path($data, ['resource.amount.currency_code','resource.seller_receivable_breakdown.gross_amount.currency_code','amount.currency_code']));
            if (strpos(strtolower((string)($norm['provider_event'] ?? '')), 'completed') !== false || strtoupper((string)$this->conversion_payload_path($data, 'resource.status')) === 'COMPLETED') $norm['event_name'] = 'purchase';
        }

        // Lemon Squeezy.
        if ($detected_source === 'lemonsqueezy') {
            $norm['source'] = 'lemonsqueezy';
            $norm['provider'] = 'lemonsqueezy';
            $norm['adapter'] = 'lemonsqueezy_order';
            $adapter = 'lemonsqueezy_order';
            $this->conversion_set_if_missing($norm, 'provider_event', $this->conversion_payload_first_path($data, ['meta.event_name','event_name','type']));
            $this->conversion_set_if_missing($norm, 'transaction_id', $this->conversion_payload_first_path($data, ['data.id','data.attributes.identifier','data.attributes.order_number','id']));
            $this->conversion_set_if_missing($norm, 'value', $this->conversion_normalize_money_value($this->conversion_payload_first_path($data, ['data.attributes.total','data.attributes.total_usd','data.attributes.subtotal','data.attributes.order_total']), 'lemonsqueezy'));
            $this->conversion_set_if_missing($norm, 'currency', $this->conversion_payload_first_path($data, ['data.attributes.currency','currency']));
            $this->conversion_set_if_missing($norm, 'product_name', $this->conversion_payload_first_path($data, ['data.attributes.first_order_item.product_name','data.attributes.product_name']));
            if (strpos(strtolower((string)($norm['provider_event'] ?? '')), 'order') !== false) $norm['event_name'] = 'purchase';
        }

        // Paddle Billing / Classic.
        if ($detected_source === 'paddle') {
            $norm['source'] = 'paddle';
            $norm['provider'] = 'paddle';
            $norm['adapter'] = 'paddle_transaction';
            $adapter = 'paddle_transaction';
            $this->conversion_set_if_missing($norm, 'provider_event', $this->conversion_payload_top_scalar($data, ['event_type','alert_name','type','event']));
            $this->conversion_set_if_missing($norm, 'transaction_id', $this->conversion_payload_first_path($data, ['data.id','checkout_id','order_id','p_order_id','transaction_id']));
            $this->conversion_set_if_missing($norm, 'value', $this->conversion_normalize_money_value($this->conversion_payload_first_path($data, ['data.details.totals.total','data.details.totals.subtotal','sale_gross','amount','earnings']), 'paddle'));
            $this->conversion_set_if_missing($norm, 'currency', $this->conversion_payload_first_path($data, ['data.currency_code','data.details.currency_code','currency','currency_code']));
            if (strpos(strtolower((string)($norm['provider_event'] ?? '')), 'completed') !== false || strpos(strtolower((string)($norm['provider_event'] ?? '')), 'payment_succeeded') !== false) $norm['event_name'] = 'purchase';
        }

        // Adyen, Mollie, Checkout.com, Braintree, Square, GoCardless, Authorize.net and EU PSPs.
        $psp = in_array($detected_source, ['adyen','mollie','checkout_com','braintree','square','worldpay','klarna','gocardless','authorize_net','mercado_pago','pagseguro','asaas'], true) ? $detected_source : '';
        if ($psp !== '') {
            $norm['source'] = $psp;
            $norm['provider'] = $psp;
            $norm['adapter'] = $psp . '_payment';
            $adapter = $psp . '_payment';
            $this->conversion_set_if_missing($norm, 'provider_event', $this->conversion_payload_top_scalar($data, ['event','event_type','type','status','eventCode','action']));
            $this->conversion_set_if_missing($norm, 'transaction_id', $this->conversion_payload_first_path($data, ['payment.id','resource.id','data.id','pspReference','merchantReference','id','transaction_id','payment_id','order_id']));
            $this->conversion_set_if_missing($norm, 'value', $this->conversion_normalize_money_value($this->conversion_payload_first_path($data, ['amount.value','payment.amount.value','resource.amount.value','data.amount','amount','transaction_amount','total','value']), $psp));
            $this->conversion_set_if_missing($norm, 'currency', $this->conversion_payload_first_path($data, ['amount.currency','payment.amount.currency','resource.amount.currency','resource.amount.currency_code','currency','currency_code']));
            if (strpos(strtolower((string)($norm['provider_event'] ?? '')), 'success') !== false || strpos(strtolower((string)($norm['provider_event'] ?? '')), 'completed') !== false || strtoupper((string)($norm['provider_event'] ?? '')) === 'AUTHORISATION') $norm['event_name'] = 'purchase';
        }

        // E-commerce platforms.
        $commerce = in_array($detected_source, ['woocommerce','shopify','magento','prestashop'], true) ? $detected_source : '';
        if ($commerce !== '') {
            $norm['source'] = $commerce;
            $norm['provider'] = $commerce;
            $norm['adapter'] = $commerce . '_order';
            $adapter = $commerce . '_order';
            $this->conversion_set_if_missing($norm, 'provider_event', $this->conversion_payload_top_scalar($data, ['event','topic','status','financial_status','order_status']));
            $this->conversion_set_if_missing($norm, 'transaction_id', $this->conversion_payload_first_path($data, ['id','order_id','number','order_number','name','increment_id','entity_id','reference','order_key']));
            $this->conversion_set_if_missing($norm, 'value', $this->conversion_payload_first_path($data, ['total_price','current_total_price','total','grand_total','total_paid','amount_paid','order_total']));
            $this->conversion_set_if_missing($norm, 'currency', $this->conversion_payload_first_path($data, ['currency','currency_code','presentment_currency','order_currency_code']));
            $status = strtolower((string)($norm['provider_event'] ?? ''));
            if (preg_match('/paid|completed|processing|fulfilled|authorized/', $status)) $norm['event_name'] = 'purchase';
        }

        // Creator/affiliate/checkouts that commonly expose flat standard fields.
        $flat_networks = ['kiwify','perfect_pay','braip','clickbank','buygoods','digistore24','gumroad','thrivecart','paykickstart','samcart','clickfunnels','jvzoo','warriorplus','partnerstack','impact','awin','shareasale','cj_affiliate','linktrust','maxbounty','maxweb','crakrevenue','adcombo','kajabi','teachable','payhip','podia','thinkific','systeme','kartra','stan_store','tapfiliate','rewardful','firstpromoter','tolt','reditus','rakuten','flexoffers','tradedoubler','webgains','admitad','eduzz','monetizze','ticto','cartpanda','yampi','lastlink','doppus','ebay_partner_network','amazon_associates'];
        if (in_array($detected_source, $flat_networks, true)) {
            $norm['source'] = $detected_source;
            $norm['provider'] = $detected_source;
            $norm['adapter'] = $detected_source . '_standard_fields';
            $adapter = $detected_source . '_standard_fields';
        }

        // Safe standard fields and controlled aliases. This is intentionally not a wild JSON crawler.
        if (empty($norm['event_name']) && $event_raw !== '') $norm['event_name'] = $event_raw;
        if (empty($norm['provider_event']) && $event_raw !== '') $norm['provider_event'] = $event_raw;
        if (empty($norm['transaction_id'])) {
            $tx = $this->conversion_payload_top_scalar($data, ['transaction_id','transactionId','order_id','orderId','orderID','order_number','orderNumber','txn_id','txnId','sale_id','saleId','payment_id','paymentId','checkout_id','checkoutId','external_id','externalId','session_id','sessionId','receipt','invoice_id','invoiceId','subscription_id','subscriptionId','transaction','id']);
            if ($tx === '') $tx = $this->conversion_payload_first_path($data, ['data.purchase.transaction','data.object.id','resource.id','data.id','payment.id','order.id']);
            if ($tx !== '') $norm['transaction_id'] = $tx;
        }
        if (!isset($norm['value']) || $norm['value'] === '') {
            $val = $this->conversion_payload_top_scalar($data, ['value','amount','price','total','revenue','amount_total','total_amount','gross_amount','purchase_value','sale_amount','sale_value','order_total','total_price','commission','payout']);
            if ($val === '') $val = $this->conversion_payload_first_path($data, ['purchase.price.value','order.total.value','payment.amount.value','resource.amount.value','data.attributes.total','data.details.totals.total','amount.value']);
            if ($val !== '') $norm['value'] = $this->conversion_normalize_money_value($val, (string)($norm['source'] ?? ''));
        }
        if (empty($norm['currency'])) {
            $cur = $this->conversion_payload_top_scalar($data, ['currency','currency_code','currencyCode','currency_value','currencyValue','currency_iso','currencyIso']);
            if ($cur === '') $cur = $this->conversion_payload_first_path($data, ['purchase.price.currency_value','order.total.currency','payment.amount.currency','payment.amount.currency_code','resource.amount.currency_code','data.attributes.currency','data.details.currency_code','amount.currency']);
            if ($cur !== '') $norm['currency'] = $cur;
        }
        if (empty($norm['adapter'])) {
            $norm['adapter'] = $adapter ?: 'generic_standard_fields';
        }
        if (empty($norm['source'])) {
            $norm['source'] = ($adapter && $adapter !== 'generic_standard_fields') ? preg_replace('/_(v2|checkout|webhook|order|transaction|payment|standard_fields)$/', '', $adapter) : $origin;
        }
        return $norm;
    }


    private function conversion_event_name_from_payload($data) {
        $raw = $this->conversion_payload_scalar($data, ['event_name','event','name','type','status']);
        $name = strtolower(trim((string)($raw ?: 'purchase')));
        $name = preg_replace('/[^a-z0-9]+/', '_', $name);
        $name = trim((string)$name, '_');
        $name = sanitize_key($name ?: 'purchase');
        if ($name === '') return 'purchase';
        $purchase_aliases = ['purchase','sale','paid','payment','order_paid','payment_succeeded','checkout_session_completed','checkout_completed','checkoutsessioncompleted','completed','approved','approved_purchase','purchase_approved','purchase_completed','checkout_session_completed','order_paid','order_completed'];
        if (in_array($name, $purchase_aliases, true)) return 'purchase';
        return $name;
    }

    private function conversion_source_from_payload($data, $origin = 'webhook') {
        $source = $this->conversion_payload_scalar($data, ['source','platform','provider','gateway']);
        return sanitize_text_field($source ?: $origin);
    }

    private function conversion_value_from_payload($data) {
        $data = is_array($data) ? $data : [];
        $value = $this->conversion_payload_top_scalar($data, ['value','amount','price','total','revenue','amount_total','total_amount','gross_amount','purchase_value']);
        if ($value === '') {
            foreach (['data.purchase.price.value','data.purchase.full_price.value','data.purchase.original_offer_price.value','data.object.amount_total'] as $path) {
                $value = $this->conversion_payload_path($data, $path);
                if ($value !== '') break;
            }
        }
        // Stripe's amount_total is usually in cents. Only convert when the source/provider is Stripe.
        $source = strtolower((string)($data['source'] ?? $data['provider'] ?? ''));
        if ($value !== '' && $source === 'stripe' && isset($data['amount_total']) && (string)$data['amount_total'] === (string)$value) {
            $value = (string)((float)$value / 100);
        }
        return preg_replace('/[^0-9.,-]/', '', (string)$value);
    }

    private function conversion_currency_from_payload($data) {
        $data = is_array($data) ? $data : [];
        $currency = $this->conversion_payload_top_scalar($data, ['currency','currency_code','currencyCode','currency_value','currencyValue']);
        if ($currency === '') {
            foreach (['data.purchase.price.currency_value','data.purchase.full_price.currency_value','data.purchase.original_offer_price.currency_value','data.object.currency'] as $path) {
                $currency = $this->conversion_payload_path($data, $path);
                if ($currency !== '') break;
            }
        }
        $currency = strtoupper(preg_replace('/[^A-Z]/', '', (string)($currency ?: 'USD')));
        return $currency ?: 'USD';
    }

    private function is_test_conversion_payload($data, $origin = 'webhook') {
        $data = $this->normalize_conversion_payload(is_array($data) ? $data : [], $origin);
        if ($origin === 'admin-test') return true;
        foreach (['itp_test','test','is_test','sandbox','dry_run','debug'] as $key) {
            if (!isset($data[$key])) continue;
            $value = strtolower(trim((string)$data[$key]));
            if (in_array($value, ['1','true','yes','y','test','sandbox','demo'], true)) return true;
        }
        $mode = strtolower($this->conversion_payload_scalar($data, ['mode','environment','env']));
        if (in_array($mode, ['test','sandbox','demo'], true)) return true;
        $event = $this->conversion_event_name_from_payload($data);
        $external = strtolower($this->conversion_external_id_from_payload($data));
        $source = strtolower($this->conversion_source_from_payload($data, $origin));
        if (strpos($event, 'test') !== false || strpos($source, 'test') !== false || strpos($external, 'test-') === 0 || strpos($external, 'test_') === 0) return true;
        $blob = strtolower(wp_json_encode($data));
        $test_needles = ['product test postback', 'test postback', 'testcomprador', 'test@hotmart', '@example.com'];
        foreach ($test_needles as $needle) {
            if (strpos($blob, $needle) !== false) return true;
        }
        return false;
    }

    private function is_ignored_conversion_event($event_name) {
        $event = sanitize_key((string)$event_name);
        if ($event === '') return false;
        $ignored = ['refund','reembolso','reimbursed','chargeback','cancel','canceled','cancelled','expir','expired','update','change','abandon','waiting','awaiting','pending','delayed','atrasad','module_completed','modulo_completo','first_access','primeiro_acesso'];
        foreach ($ignored as $bad) {
            if (strpos($event, $bad) !== false) return true;
        }
        return false;
    }


    public function register_conversion_rest_routes() {
        register_rest_route('invalid-traffic/v1', '/conversion', [
            'methods' => ['GET', 'POST'],
            'callback' => [$this, 'rest_conversion_receiver'],
            'permission_callback' => '__return_true',
        ]);
        register_rest_route('invalid-traffic/v1', '/pixel.js', [
            'methods' => ['GET'],
            'callback' => [$this, 'rest_universal_pixel_js'],
            'permission_callback' => '__return_true',
        ]);
        register_rest_route('invalid-traffic/v1', '/pixel-conversion', [
            'methods' => ['GET', 'POST'],
            'callback' => [$this, 'rest_public_pixel_conversion_receiver'],
            'permission_callback' => '__return_true',
        ]);
    }

    public function rest_universal_pixel_js($request) {
        $settings = $this->global_pixel_settings();
        $token = '';
        if (is_object($request) && method_exists($request, 'get_param')) {
            $token = sanitize_text_field((string)$request->get_param('token'));
        } else {
            $token = sanitize_text_field((string)($_GET['token'] ?? ''));
        }
        $public_token = (string)($settings['public_token'] ?? '');
        $valid = $public_token !== '' && $token !== '' && hash_equals($public_token, $token);
        nocache_headers();
        header('Content-Type: application/javascript; charset=UTF-8');
        header('X-Robots-Tag: noindex, nofollow', true);
        if (!$valid) {
            echo "(function(){try{console.warn('[Invalid Traffic Pixel] Invalid or missing public token.');}catch(e){}})();";
            exit;
        }
        $cfg = [
            'collectUrl' => rest_url('invalid-traffic/v1/pixel-conversion'),
            'publicToken' => $public_token,
            'site' => home_url('/'),
            'version' => self::VERSION,
        ];
        $cfg_json = wp_json_encode($cfg, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT);
        echo "(function(){\n";
        echo "  'use strict';\n";
        echo "  if(window.__ITP_UNIVERSAL_THANKYOU_PIXEL_LOADED__) return;\n";
        echo "  window.__ITP_UNIVERSAL_THANKYOU_PIXEL_LOADED__ = true;\n";
        echo "  var cfg = " . $cfg_json . ";\n";
        echo <<<'JS'
  function log(){ try{ if(window.console && console.log) console.log.apply(console, arguments); }catch(e){} }
  function warn(){ try{ if(window.console && console.warn) console.warn.apply(console, arguments); }catch(e){} }
  function params(){ return new URLSearchParams(window.location.search || ''); }
  function pick(p, keys){ for(var i=0;i<keys.length;i++){ var v = p.get(keys[i]); if(v !== null && String(v).trim() !== '') return String(v).trim(); } return ''; }
  function normalizeEventName(v){
    var name = String(v || 'purchase').trim().toLowerCase().replace(/[^a-z0-9_]+/g, '_').replace(/^_+|_+$/g, '');
    if(!name) return 'purchase';
    var purchaseAliases = {purchase:1, sale:1, paid:1, payment:1, order_paid:1, payment_succeeded:1, checkout_session_completed:1, checkout_completed:1, completed:1, approved:1, approved_purchase:1};
    return purchaseAliases[name] ? 'purchase' : name;
  }
  function readTouch(){
    try{
      var raw = window.localStorage ? localStorage.getItem('itp_last_touch') : '';
      if(raw){
        var data = JSON.parse(raw);
        if(data && data.bridgeId && (!data.expires || Date.now() <= Number(data.expires))) return data;
      }
    }catch(e){}
    return {};
  }
  function device(){ var ua=navigator.userAgent||''; if(/Mobi|Android|iPhone|iPad/i.test(ua)) return 'mobile'; if(/Tablet|iPad/i.test(ua)) return 'tablet'; return 'desktop'; }
  function browser(){ var ua=navigator.userAgent||''; if(/Edg\//.test(ua)) return 'Edge'; if(/Chrome\//.test(ua)) return 'Chrome'; if(/Safari\//.test(ua) && !/Chrome\//.test(ua)) return 'Safari'; if(/Firefox\//.test(ua)) return 'Firefox'; return 'Other'; }
  function os(){ var ua=navigator.userAgent||''; if(/Windows/i.test(ua)) return 'Windows'; if(/Android/i.test(ua)) return 'Android'; if(/iPhone|iPad|iOS/i.test(ua)) return 'iOS'; if(/Mac OS/i.test(ua)) return 'macOS'; if(/Linux/i.test(ua)) return 'Linux'; return 'Other'; }
  function append(fd, obj){ Object.keys(obj).forEach(function(k){ if(obj[k] !== undefined && obj[k] !== null) fd.append(k, obj[k]); }); return fd; }
  function toQuery(obj){ var q = new URLSearchParams(); Object.keys(obj).forEach(function(k){ if(obj[k] !== undefined && obj[k] !== null && String(obj[k]) !== '') q.set(k, obj[k]); }); return q.toString(); }
  function mark(key){ try{ if(window.localStorage) localStorage.setItem(key, '1'); }catch(e){} }
  function already(key){ try{ return !!(window.localStorage && localStorage.getItem(key)); }catch(e){ return false; } }
  function safeHash(input){
    var str = String(input || ''), h = 0, i;
    for(i=0;i<str.length;i++){ h = ((h << 5) - h) + str.charCodeAt(i); h |= 0; }
    return Math.abs(h).toString(36);
  }
  function send(){
    var p = params();
    var touch = readTouch();
    var bridgeId = pick(p, ['itp_bridge','bridge_id','presell_id','itp_bid','campaign_id']) || String(touch.bridgeId || '');
    if(!bridgeId){ warn('[Invalid Traffic Pixel] Missing bridge id. Use a bridge button, pass itp_bridge/bridge_id, or return buyers to the same site after checkout. Purchase was not sent.'); return; }
    var checkoutId = pick(p, ['transaction_id','order_id','orderId','txn_id','transaction','sale_id','payment_id','checkout_id','external_id','event_id','receipt','session_id','checkout_session_id','purchase_id']);
    var usedFallbackId = false;
    var orderId = checkoutId;
    if(!orderId){
      var touchKey = String(touch.sessionId || touch.visitorId || 'visitor');
      orderId = 'thankyou_return_' + bridgeId + '_' + safeHash(touchKey + '|' + window.location.origin + window.location.pathname);
      usedFallbackId = true;
      warn('[Invalid Traffic Pixel] No transaction_id/order_id was found. Counting this thank-you return once using the last bridge click. Revenue will only show if value/currency are provided.');
    }
    var eventName = normalizeEventName(pick(p, ['event','event_name','name','type','status']) || 'purchase');
    var value = pick(p, ['value','amount','price','total','purchase_value']);
    var currency = (pick(p, ['currency','currency_code']) || 'USD').toUpperCase().replace(/[^A-Z]/g,'') || 'USD';
    var source = pick(p, ['source','utm_source','platform','provider']) || 'thank-you-pixel';
    var payload = {
      token: cfg.publicToken,
      public_token: cfg.publicToken,
      itp_bridge: bridgeId,
      bridge_id: bridgeId,
      presell_id: bridgeId,
      event: eventName,
      event_name: eventName,
      name: eventName,
      value: value,
      currency: currency,
      transaction_id: orderId,
      order_id: orderId,
      external_id: orderId,
      fallback_id: usedFallbackId ? '1' : '0',
      source: source,
      medium: pick(p, ['medium','utm_medium']) || 'checkout',
      campaign: pick(p, ['campaign','utm_campaign']) || '',
      content: pick(p, ['content','utm_content']) || '',
      term: pick(p, ['term','utm_term']) || '',
      itp_session: pick(p, ['itp_session']) || String(touch.sessionId || ''),
      session_id: pick(p, ['itp_session']) || String(touch.sessionId || ''),
      itp_visitor: pick(p, ['itp_visitor']) || String(touch.visitorId || ''),
      visitor_id: pick(p, ['itp_visitor']) || String(touch.visitorId || ''),
      referrer: document.referrer || String(touch.referrer || ''),
      landing_url: String(touch.landingUrl || document.referrer || ''),
      conversion_url: window.location.href,
      url: window.location.href,
      device: device(),
      browser: browser(),
      os: os(),
      screen: ((window.screen && screen.width) || 0) + 'x' + ((window.screen && screen.height) || 0),
      language: navigator.language || '',
      timezone: (window.Intl && Intl.DateTimeFormat ? (Intl.DateTimeFormat().resolvedOptions().timeZone || '') : '')
    };
    var localKey = 'itp_universal_purchase_sent_' + String(bridgeId) + '_' + String(orderId);
    if(already(localKey)){ warn('[Invalid Traffic Pixel] Duplicate purchase skipped locally:', orderId); return; }
    var fd = append(new FormData(), payload);
    function imgFallback(){ var img = new Image(1,1); img.src = cfg.collectUrl + '?' + toQuery(payload) + '&_=' + Date.now(); mark(localKey); log('[Invalid Traffic Pixel] Purchase beacon sent:', payload); }
    if(navigator.sendBeacon){
      try{
        var ok = navigator.sendBeacon(cfg.collectUrl, fd);
        if(ok){ mark(localKey); log('[Invalid Traffic Pixel] Purchase beacon sent:', payload); return; }
      }catch(e){}
    }
    if(window.fetch){
      fetch(cfg.collectUrl, {method:'POST', body:fd, credentials:'omit', keepalive:true})
        .then(function(){ mark(localKey); log('[Invalid Traffic Pixel] Purchase sent:', payload); })
        .catch(function(){ imgFallback(); });
      return;
    }
    imgFallback();
  }
  if(document.readyState === 'complete') setTimeout(send, 300);
  else window.addEventListener('load', function(){ setTimeout(send, 300); }, {once:true});
JS;
        echo "\n})();";
        exit;
    }

    public function rest_public_pixel_conversion_receiver($request) {
        $settings = $this->global_pixel_settings();
        $params = [];
        if (is_object($request) && method_exists($request, 'get_params')) {
            $params = $request->get_params();
            if (method_exists($request, 'get_json_params')) {
                $json = $request->get_json_params();
                if (is_array($json)) $params = array_merge($params, $json);
            }
        } else {
            $params = array_merge($_GET, $_POST);
        }
        $token = sanitize_text_field((string)($params['token'] ?? $params['public_token'] ?? ''));
        $public_token = (string)($settings['public_token'] ?? '');
        $valid = $public_token !== '' && $token !== '' && hash_equals($public_token, $token);
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
        header('Access-Control-Allow-Headers: Content-Type');
        if (!$valid) {
            return new WP_Error('itp_invalid_public_token', 'Invalid Invalid Traffic pixel token.', ['status' => 403]);
        }
        $params = $this->normalize_conversion_payload($params, 'thank-you-pixel');
        $adapter = sanitize_text_field((string)($params['adapter'] ?? 'generic_standard_fields'));
        $event_name = $this->conversion_event_name_from_payload($params);
        $source = $this->conversion_source_from_payload($params, 'thank-you-pixel');
        $bridge_id = $this->find_bridge_from_conversion_payload($params);
        $external_id = $this->conversion_external_id_from_payload($params);
        $value = $this->conversion_value_from_payload($params);
        $currency = $this->conversion_currency_from_payload($params);
        if ($this->is_ignored_conversion_event($event_name)) {
            $history = $this->add_pixel_webhook_history([
                'status' => 'ignored',
                'counted' => '0',
                'source' => $source,
                'event' => $event_name,
                'bridge_id' => $bridge_id,
                'value' => $value,
                'currency' => $currency,
                'external_id' => $external_id,
                'message' => 'Matched adapter: ' . $adapter . '. Universal thank-you pixel event received but not counted as a conversion.',
            ]);
            return rest_ensure_response(['ok' => true, 'status' => 'ignored', 'counted' => false, 'history' => $history]);
        }
        $result = $this->record_conversion_from_payload($params, 'thank-you-pixel');
        $this->add_pixel_webhook_history([
            'status' => !empty($result['duplicate']) ? 'duplicate ignored' : (!empty($result['ok']) ? 'conversion recorded' : 'failed'),
            'counted' => (!empty($result['ok']) && empty($result['duplicate'])) ? '1' : '0',
            'source' => $source,
            'event' => $event_name,
            'bridge_id' => !empty($result['bridge_id']) ? (int)$result['bridge_id'] : $bridge_id,
            'value' => $value ?: (string)($result['value'] ?? ''),
            'currency' => $currency ?: (string)($result['currency'] ?? ''),
            'external_id' => $external_id ?: (string)($result['external_id'] ?? ''),
            'message' => 'Matched adapter: ' . $adapter . '. ' . (string)($result['message'] ?? ''),
        ]);
        if (empty($result['ok'])) {
            return new WP_Error('itp_pixel_conversion_failed', $result['message'] ?? 'Could not record pixel conversion.', ['status' => (int)($result['status'] ?? 400)]);
        }
        return rest_ensure_response($result);
    }

    public function rest_conversion_receiver($request) {
        $settings = $this->global_pixel_settings();
        $params = [];
        if (is_object($request) && method_exists($request, 'get_params')) {
            $params = $request->get_params();
            if (method_exists($request, 'get_json_params')) {
                $json = $request->get_json_params();
                if (is_array($json)) $params = array_merge($params, $json);
            }
        } else {
            $params = array_merge($_GET, $_POST);
        }
        $token = sanitize_text_field((string)($params['token'] ?? $params['secret'] ?? $params['webhook_secret'] ?? ''));
        $secret = (string)($settings['webhook_secret'] ?? '');
        $valid = false;
        // REST/webhook endpoints must accept only the private server token.
        // The public browser token is intentionally not valid here, because it is printed on public pages.
        if ($secret !== '' && $token !== '' && hash_equals($secret, $token)) $valid = true;
        if (!$valid) {
            return new WP_Error('itp_invalid_token', 'Invalid Invalid Traffic webhook token.', ['status' => 403]);
        }
        $params = $this->normalize_conversion_payload($params, 'webhook');
        $adapter = sanitize_text_field((string)($params['adapter'] ?? 'generic_standard_fields'));
        $event_name = $this->conversion_event_name_from_payload($params);
        $source = $this->conversion_source_from_payload($params, 'webhook');
        $bridge_id = $this->find_bridge_from_conversion_payload($params);
        $external_id = $this->conversion_external_id_from_payload($params);
        $value = $this->conversion_value_from_payload($params);
        $currency = $this->conversion_currency_from_payload($params);

        if ($this->is_test_conversion_payload($params, 'webhook')) {
            $history = $this->add_pixel_webhook_history([
                'status' => 'test received',
                'counted' => '0',
                'source' => $source,
                'event' => $event_name,
                'bridge_id' => $bridge_id,
                'value' => $value,
                'currency' => $currency,
                'external_id' => $external_id,
                'message' => 'Matched adapter: ' . $adapter . '. Webhook test received. No analytics conversion was created.',
            ]);
            return rest_ensure_response([
                'ok' => true,
                'status' => 'test_received',
                'counted' => false,
                'message' => 'Webhook test received. No analytics conversion was created.',
                'history' => $history,
            ]);
        }

        if ($this->is_ignored_conversion_event($event_name)) {
            $history = $this->add_pixel_webhook_history([
                'status' => 'ignored',
                'counted' => '0',
                'source' => $source,
                'event' => $event_name,
                'bridge_id' => $bridge_id,
                'value' => $value,
                'currency' => $currency,
                'external_id' => $external_id,
                'message' => 'Matched adapter: ' . $adapter . '. Webhook event received but not counted as a conversion.',
            ]);
            return rest_ensure_response([
                'ok' => true,
                'status' => 'ignored',
                'counted' => false,
                'message' => 'Webhook event received but not counted as a conversion.',
                'history' => $history,
            ]);
        }

        $result = $this->record_conversion_from_payload($params, 'webhook');
        $this->add_pixel_webhook_history([
            'status' => !empty($result['duplicate']) ? 'duplicate ignored' : (!empty($result['ok']) ? 'conversion recorded' : 'failed'),
            'counted' => (!empty($result['ok']) && empty($result['duplicate'])) ? '1' : '0',
            'source' => $source,
            'event' => $event_name,
            'bridge_id' => !empty($result['bridge_id']) ? (int)$result['bridge_id'] : $bridge_id,
            'value' => $value ?: (string)($result['value'] ?? ''),
            'currency' => $currency ?: (string)($result['currency'] ?? ''),
            'external_id' => $external_id ?: (string)($result['external_id'] ?? ''),
            'message' => 'Matched adapter: ' . $adapter . '. ' . (string)($result['message'] ?? ''),
        ]);
        if (empty($result['ok'])) {
            return new WP_Error('itp_conversion_failed', $result['message'] ?? 'Could not record conversion.', ['status' => (int)($result['status'] ?? 400)]);
        }
        return rest_ensure_response($result);
    }

    private function find_bridge_from_conversion_payload($data) {
        $data = is_array($data) ? $data : [];
        $candidate_keys = ['presell_id', 'bridge_id', 'itp_bridge', 'itp_bid', 'campaign_id', 'tracking_id', 'sck', 'sckPaymentLink', 'subid', 'sub_id', 'itp_click_id'];
        foreach ($candidate_keys as $key) {
            if (!empty($data[$key])) {
                $raw = (string)$data[$key];
                $id = absint($raw);
                if (!$id && preg_match('/(?:itp_bridge|bridge_id|bridge|presell|itp_bid)[_=:\-]?(\d+)/i', $raw, $m)) {
                    $id = absint($m[1]);
                }
                if ($id && get_post_type($id) === self::CPT) return $id;
            }
        }
        $slug_keys = ['bridge_slug', 'slug', 'campaign_slug', 'itp_slug'];
        foreach ($slug_keys as $key) {
            if (!empty($data[$key])) {
                $slug = sanitize_title((string)$data[$key]);
                if ($slug) {
                    $post = get_page_by_path($slug, OBJECT, self::CPT);
                    if ($post && !empty($post->ID)) return (int)$post->ID;
                }
            }
        }
        foreach (['landing_url', 'conversion_url', 'url', 'referrer', 'destination', 'checkout_url'] as $key) {
            if (empty($data[$key])) continue;
            $url = esc_url_raw((string)$data[$key]);
            if (!$url) continue;
            $parts = wp_parse_url($url);
            if (!empty($parts['query'])) {
                parse_str($parts['query'], $q);
                foreach ($candidate_keys as $qk) {
                    if (!empty($q[$qk])) {
                        $id = absint($q[$qk]);
                        if ($id && get_post_type($id) === self::CPT) return $id;
                    }
                }
            }
            if (!empty($parts['path'])) {
                $path = trim((string)$parts['path'], '/');
                if ($path && strpos($path, '/') === false) {
                    $post = get_page_by_path(sanitize_title($path), OBJECT, self::CPT);
                    if ($post && !empty($post->ID)) return (int)$post->ID;
                }
            }
        }
        return 0;
    }

    private function conversion_external_id_from_payload($data) {
        $data = is_array($data) ? $data : [];
        $keys = ['transaction_id','order_id','orderId','orderID','txn_id','transaction','sale_id','payment_id','checkout_id','external_id','event_id','hotmart_transaction','receipt','session_id','checkout_session_id','purchase_id','id'];
        $value = $this->conversion_payload_top_scalar($data, $keys);
        if ($value === '') {
            foreach (['data.purchase.transaction','data.object.id'] as $path) {
                $value = $this->conversion_payload_path($data, $path);
                if ($value !== '') break;
            }
        }
        return $value !== '' ? substr(sanitize_text_field((string)$value), 0, 120) : '';
    }

    private function record_conversion_from_payload($data, $origin = 'webhook') {
        $data = $this->normalize_conversion_payload(is_array($data) ? $data : [], $origin);
        $presell_id = $this->find_bridge_from_conversion_payload($data);
        if (!$presell_id) {
            return ['ok' => false, 'status' => 400, 'message' => 'No valid bridge found. Send bridge_id, itp_bridge, bridge_slug, or a URL containing itp_bridge.'];
        }
        $event_name = $this->conversion_event_name_from_payload($data);
        $value = sanitize_text_field($this->conversion_value_from_payload($data));
        $currency = $this->conversion_currency_from_payload($data);
        $external_id = $this->conversion_external_id_from_payload($data);
        $source = $this->conversion_source_from_payload($data, $origin);
        $medium = sanitize_text_field((string)($data['medium'] ?? 'conversion'));
        $campaign = sanitize_text_field((string)($data['campaign'] ?? $data['utm_campaign'] ?? ''));
        $content = sanitize_text_field((string)($data['content'] ?? $data['utm_content'] ?? ''));
        $term = sanitize_text_field((string)($data['term'] ?? $data['utm_term'] ?? ''));
        $session_id = sanitize_text_field((string)($data['session_id'] ?? $data['itp_session'] ?? ''));
        $visitor_id = sanitize_text_field((string)($data['visitor_id'] ?? $data['itp_visitor'] ?? ''));
        if ($session_id === '') $session_id = 'postback_' . substr(md5($origin . '|' . $presell_id . '|' . ($external_id ?: wp_generate_uuid4())), 0, 16);
        if ($visitor_id === '') $visitor_id = 'postback';
        $landing_url = esc_url_raw((string)($data['landing_url'] ?? $data['conversion_url'] ?? $data['url'] ?? ''));
        $referrer = esc_url_raw((string)($data['referrer'] ?? ''));
        $destination_url = esc_url_raw((string)($data['destination'] ?? $data['checkout_url'] ?? ''));
        $destination = $origin . ':' . $event_name;
        if ($destination_url) $destination .= '|url=' . $destination_url;
        if ($value !== '') $destination .= '|value=' . $value;
        if ($currency !== '') $destination .= '|currency=' . $currency;
        if ($external_id !== '') $destination .= '|external_id=' . $external_id;

        global $wpdb;
        $table = $wpdb->prefix . self::EVENT_TABLE;
        if ($external_id !== '') {
            $like = '%' . $wpdb->esc_like('external_id=' . $external_id) . '%';
            $existing_row = $wpdb->get_row($wpdb->prepare(
                "SELECT id, presell_id FROM {$table} WHERE event_type='conversion' AND destination LIKE %s LIMIT 1",
                $like
            ), ARRAY_A);
            if (!empty($existing_row['id'])) {
                return [
                    'ok' => true,
                    'duplicate' => true,
                    'message' => 'Duplicate ignored. Conversion already exists for this external ID.',
                    'bridge_id' => (int)($existing_row['presell_id'] ?? $presell_id),
                    'event' => $event_name,
                    'event_id' => (int)$existing_row['id'],
                ];
            }
        }

        $ip = $this->client_ip();
        $payload = [
            'source' => $source,
            'medium' => $medium,
            'campaign' => $campaign,
            'referrer' => $referrer,
            'landing_url' => $landing_url,
            'device' => sanitize_text_field((string)($data['device'] ?? 'server')),
            'time_on_page' => 0,
            'scroll_depth' => 0,
        ];
        $geo = $this->detect_geo($payload);
        $ok = $wpdb->insert($table, [
            'presell_id' => $presell_id,
            'session_id' => substr($session_id, 0, 64),
            'visitor_id' => substr($visitor_id, 0, 64),
            'event_type' => 'conversion',
            'quality' => 'valid',
            'reason' => ucfirst($origin) . ' conversion: ' . $event_name,
            'source' => $this->clean_source_label($source ?: $origin),
            'medium' => $medium,
            'campaign' => $campaign,
            'content' => $content,
            'term_value' => $term,
            'referrer' => $referrer,
            'landing_url' => $landing_url ?: home_url('/'),
            'destination' => $destination,
            'device' => sanitize_text_field((string)($data['device'] ?? 'server')),
            'browser' => sanitize_text_field((string)($data['browser'] ?? 'server')),
            'os' => sanitize_text_field((string)($data['os'] ?? 'server')),
            'screen' => '',
            'language' => sanitize_text_field((string)($data['language'] ?? '')),
            'timezone' => sanitize_text_field((string)($data['timezone'] ?? wp_timezone_string())),
            'ip_hash' => $this->hash_ip($ip),
            'ip_mask' => $this->mask_ip($ip),
            'country' => $geo['country'],
            'country_code' => $geo['country_code'],
            'scroll_depth' => 0,
            'time_on_page' => 0,
            'created_at' => current_time('mysql', true),
        ]);
        if (!$ok) {
            return ['ok' => false, 'status' => 500, 'message' => 'Could not save conversion event.'];
        }
        return [
            'ok' => true,
            'message' => 'Conversion received for ' . get_the_title($presell_id) . '.',
            'bridge_id' => $presell_id,
            'bridge_title' => get_the_title($presell_id),
            'event' => $event_name,
            'value' => $value,
            'currency' => $currency,
            'external_id' => $external_id,
            'event_id' => (int)$wpdb->insert_id,
        ];
    }


    public function ajax_admin_pixel_test() {
        if (!current_user_can('manage_options')) wp_send_json_error(['message' => 'Forbidden'], 403);
        $nonce = sanitize_text_field(wp_unslash($_POST['nonce'] ?? ''));
        if (!wp_verify_nonce($nonce, 'itp_admin_pixel_test')) wp_send_json_error(['message' => 'Invalid nonce'], 403);
        $presell_id = absint($_POST['presell_id'] ?? 0);
        if (!$presell_id || get_post_type($presell_id) !== self::CPT) wp_send_json_error(['message' => 'Choose a valid bridge first.'], 400);
        $event_name = sanitize_text_field(wp_unslash($_POST['event_name'] ?? 'purchase_test'));
        $external_id = sanitize_text_field(wp_unslash($_POST['external_id'] ?? 'TEST-' . time()));
        $value = sanitize_text_field(wp_unslash($_POST['value'] ?? ''));
        $currency = sanitize_text_field(wp_unslash($_POST['currency'] ?? 'USD'));
        $source = sanitize_text_field(wp_unslash($_POST['source'] ?? 'pixel-test'));
        $history = $this->add_pixel_webhook_history([
            'status' => 'test received',
            'counted' => '0',
            'source' => $source ?: 'pixel-test',
            'event' => $event_name ?: 'purchase_test',
            'bridge_id' => $presell_id,
            'value' => $value,
            'currency' => strtoupper(preg_replace('/[^A-Z]/', '', $currency ?: 'USD')) ?: 'USD',
            'external_id' => $external_id,
            'message' => 'Webhook test received. No analytics conversion was created.',
        ]);
        wp_send_json_success([
            'message' => 'Webhook test received for ' . get_the_title($presell_id) . '. No analytics conversion was created. Refresh this page to see it in Webhook history.',
            'result' => [
                'ok' => true,
                'status' => 'test_received',
                'counted' => false,
                'bridge_id' => $presell_id,
                'bridge_title' => get_the_title($presell_id),
                'event' => $event_name ?: 'purchase_test',
                'value' => $value,
                'currency' => $currency ?: 'USD',
                'external_id' => $external_id,
                'history' => $history,
            ],
        ]);
    }

    public function track_event() {
        $presell_id = absint($_POST['presell_id'] ?? 0);
        if (!$presell_id || get_post_type($presell_id) !== self::CPT) {
            wp_send_json_error(['message' => 'Invalid bridge.'], 400);
        }
        $event_type = sanitize_key($_POST['event_type'] ?? 'visit');
        $nonce = sanitize_text_field(wp_unslash($_POST['nonce'] ?? ''));
        $valid_bridge_nonce = wp_verify_nonce($nonce, 'itp_track_' . $presell_id);
        $valid_global_conversion_nonce = ($event_type === 'conversion') && wp_verify_nonce($nonce, 'itp_global_conversion');
        $public_token = sanitize_text_field(wp_unslash($_POST['public_token'] ?? ''));
        $pixel_settings = $this->global_pixel_settings();
        $valid_public_conversion_token = ($event_type === 'conversion') && $public_token !== '' && hash_equals((string)($pixel_settings['public_token'] ?? ''), $public_token);
        if (!$valid_bridge_nonce && !$valid_global_conversion_nonce && !$valid_public_conversion_token) {
            wp_send_json_error(['message' => 'Invalid nonce.'], 403);
        }
        $allowed_events = ['visit', 'click', 'heartbeat', 'vsl_view', 'vsl_play', 'vsl_unmute', 'vsl_pause', 'vsl_resume', 'vsl_25', 'vsl_50', 'vsl_75', 'vsl_100', 'vsl_cta_view', 'vsl_cta_click', 'vsl_hook_view', 'vsl_pause_image_view', 'lead_view', 'lead_submit', 'lead_redirect', 'conversion'];
        $is_progress_event = (bool) preg_match('/^vsl_progress_[1-9][0-9]{0,4}s$/', $event_type);
        if (!in_array($event_type, $allowed_events, true) && !$is_progress_event) {
            $event_type = 'visit';
        }
        $payload = [
            'session_id' => sanitize_text_field(wp_unslash($_POST['session_id'] ?? '')),
            'visitor_id' => sanitize_text_field(wp_unslash($_POST['visitor_id'] ?? '')),
            'source' => sanitize_text_field(wp_unslash($_POST['source'] ?? '')),
            'medium' => sanitize_text_field(wp_unslash($_POST['medium'] ?? '')),
            'campaign' => sanitize_text_field(wp_unslash($_POST['campaign'] ?? '')),
            'content' => sanitize_text_field(wp_unslash($_POST['content'] ?? '')),
            'term_value' => sanitize_text_field(wp_unslash($_POST['term'] ?? '')),
            'referrer' => esc_url_raw(wp_unslash($_POST['referrer'] ?? '')),
            'landing_url' => esc_url_raw(wp_unslash($_POST['landing_url'] ?? '')),
            'destination' => sanitize_text_field(wp_unslash($_POST['destination'] ?? '')),
            'event_name' => sanitize_text_field(wp_unslash($_POST['event_name'] ?? '')),
            'value' => sanitize_text_field(wp_unslash($_POST['value'] ?? '')),
            'currency' => strtoupper(preg_replace('/[^A-Z]/', '', sanitize_text_field(wp_unslash($_POST['currency'] ?? '')))),
            'external_id' => substr(sanitize_text_field(wp_unslash($_POST['transaction_id'] ?? $_POST['order_id'] ?? $_POST['external_id'] ?? $_POST['txn_id'] ?? $_POST['transaction'] ?? $_POST['sale_id'] ?? $_POST['payment_id'] ?? $_POST['checkout_id'] ?? '')), 0, 120),
            'device' => sanitize_text_field(wp_unslash($_POST['device'] ?? '')),
            'browser' => sanitize_text_field(wp_unslash($_POST['browser'] ?? '')),
            'os' => sanitize_text_field(wp_unslash($_POST['os'] ?? '')),
            'screen' => sanitize_text_field(wp_unslash($_POST['screen'] ?? '')),
            'language' => sanitize_text_field(wp_unslash($_POST['language'] ?? '')),
            'timezone' => sanitize_text_field(wp_unslash($_POST['timezone'] ?? '')),
            'scroll_depth' => min(100, max(0, absint($_POST['scroll_depth'] ?? 0))),
            'time_on_page' => max(0, absint($_POST['time_on_page'] ?? 0)),
        ];
        $ip = $this->client_ip();
        $geo = $this->detect_geo($payload);
        $ua = sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'] ?? ''));
        $quality = $this->score_quality($presell_id, $event_type, $payload, $ip, $ua);
        $normalized_source = $this->clean_source_label($payload['source'] ?: $this->source_from_referrer($payload['referrer']));

        global $wpdb;
        $table = $wpdb->prefix . self::EVENT_TABLE;
        if ($event_type === 'conversion') {
            if ($payload['destination'] === '') {
                $payload['destination'] = 'public:' . ($payload['event_name'] ?: 'conversion');
            }
            if ($payload['value'] !== '' && strpos($payload['destination'], 'value=') === false) {
                $payload['destination'] .= '|value=' . $payload['value'];
            }
            if ($payload['currency'] !== '' && strpos($payload['destination'], 'currency=') === false) {
                $payload['destination'] .= '|currency=' . $payload['currency'];
            }
            if ($payload['external_id'] !== '' && strpos($payload['destination'], 'external_id=') === false) {
                $payload['destination'] .= '|external_id=' . $payload['external_id'];
            }
            if ($payload['external_id'] !== '') {
                $like = '%' . $wpdb->esc_like('external_id=' . $payload['external_id']) . '%';
                $existing_by_external = (int)$wpdb->get_var($wpdb->prepare(
                    "SELECT id FROM {$table} WHERE event_type='conversion' AND destination LIKE %s LIMIT 1",
                    $like
                ));
                if ($existing_by_external) {
                    $this->add_pixel_webhook_history([
                        'status' => 'duplicate ignored',
                        'counted' => '0',
                        'source' => $normalized_source ?: 'browser-helper',
                        'event' => $payload['event_name'] ?: 'conversion',
                        'bridge_id' => $presell_id,
                        'value' => $payload['value'],
                        'currency' => $payload['currency'] ?: 'USD',
                        'external_id' => $payload['external_id'],
                        'message' => 'Duplicate browser/helper conversion ignored for this external ID.',
                    ]);
                    wp_send_json_success(['quality' => 'valid', 'reason' => 'Duplicate conversion ignored', 'duplicate' => true, 'event_id' => $existing_by_external, 'bridge_id' => $presell_id, 'event' => $payload['event_name'] ?: 'conversion', 'value' => $payload['value'], 'currency' => $payload['currency'] ?: 'USD', 'external_id' => $payload['external_id']]);
                }
            }
            if ($payload['session_id'] !== '' && $payload['destination'] !== '') {
                $existing_conversion = (int)$wpdb->get_var($wpdb->prepare(
                    "SELECT id FROM {$table} WHERE presell_id=%d AND event_type='conversion' AND session_id=%s AND destination=%s AND created_at >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 MINUTE) LIMIT 1",
                    $presell_id,
                    substr($payload['session_id'], 0, 64),
                    $payload['destination']
                ));
                if ($existing_conversion) {
                    $this->add_pixel_webhook_history([
                        'status' => 'duplicate ignored',
                        'counted' => '0',
                        'source' => $normalized_source ?: 'browser-helper',
                        'event' => $payload['event_name'] ?: 'conversion',
                        'bridge_id' => $presell_id,
                        'value' => $payload['value'],
                        'currency' => $payload['currency'] ?: 'USD',
                        'external_id' => $payload['external_id'],
                        'message' => 'Duplicate browser/helper conversion ignored for this session and destination.',
                    ]);
                    wp_send_json_success(['quality' => 'valid', 'reason' => 'Duplicate conversion ignored', 'duplicate' => true, 'event_id' => $existing_conversion, 'bridge_id' => $presell_id, 'event' => $payload['event_name'] ?: 'conversion', 'value' => $payload['value'], 'currency' => $payload['currency'] ?: 'USD', 'external_id' => $payload['external_id']]);
                }
            }
        }
        $wpdb->insert($table, [
            'presell_id' => $presell_id,
            'session_id' => substr($payload['session_id'], 0, 64),
            'visitor_id' => substr($payload['visitor_id'], 0, 64),
            'event_type' => $event_type,
            'quality' => $quality['quality'],
            'reason' => $quality['reason'],
            'source' => $normalized_source,
            'medium' => $payload['medium'],
            'campaign' => $payload['campaign'],
            'content' => $payload['content'],
            'term_value' => $payload['term_value'],
            'referrer' => $payload['referrer'],
            'landing_url' => $payload['landing_url'],
            'destination' => $payload['destination'],
            'device' => $payload['device'],
            'browser' => $payload['browser'],
            'os' => $payload['os'],
            'screen' => $payload['screen'],
            'language' => $payload['language'],
            'timezone' => $payload['timezone'],
            'ip_hash' => $this->hash_ip($ip),
            'ip_mask' => $this->mask_ip($ip),
            'country' => $geo['country'],
            'country_code' => $geo['country_code'],
            'scroll_depth' => $payload['scroll_depth'],
            'time_on_page' => $payload['time_on_page'],
            'created_at' => current_time('mysql', true),
        ], ['%d','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%d','%d','%s']);
        $event_id = (int)$wpdb->insert_id;
        if ($event_type === 'conversion') {
            $this->add_pixel_webhook_history([
                'status' => 'conversion recorded',
                'counted' => '1',
                'source' => $normalized_source ?: 'browser-helper',
                'event' => $payload['event_name'] ?: 'conversion',
                'bridge_id' => $presell_id,
                'value' => $payload['value'],
                'currency' => $payload['currency'] ?: 'USD',
                'external_id' => $payload['external_id'],
                'message' => 'Browser/thank-you helper conversion recorded for this bridge.',
            ]);
        }
        wp_send_json_success(['quality' => $quality['quality'], 'reason' => $quality['reason'], 'event_id' => $event_id, 'bridge_id' => $presell_id, 'event' => $payload['event_name'] ?: $event_type, 'value' => $payload['value'], 'currency' => $payload['currency'] ?: '', 'external_id' => $payload['external_id']]);
    }

    private function risk_rule_settings() {
        $s = $this->settings();
        $rules = [
            'engaged_seconds' => max(3, min(120, (int)($s['engaged_seconds'] ?? 10))),
            'engaged_scroll' => max(5, min(100, (int)($s['engaged_scroll'] ?? 30))),
            'fast_click_seconds' => max(1, min(20, (int)($s['fast_click_seconds'] ?? 2))),
            'repeat_visit_limit' => max(2, min(100, (int)($s['repeat_visit_limit'] ?? 8))),
            'repeat_window_minutes' => max(5, min(1440, (int)($s['repeat_window_minutes'] ?? 60))),
            'emergency_mode' => (($s['emergency_mode'] ?? '0') === '1') ? 1 : 0,
        ];
        if ($rules['emergency_mode']) {
            $rules['engaged_seconds'] = max($rules['engaged_seconds'], 15);
            $rules['engaged_scroll'] = max($rules['engaged_scroll'], 50);
            $rules['fast_click_seconds'] = max($rules['fast_click_seconds'], 5);
            $rules['repeat_visit_limit'] = min($rules['repeat_visit_limit'], 4);
        }
        return $rules;
    }

    private function score_quality($presell_id, $event_type, $p, $ip, $ua) {
        $ua_l = strtolower($ua);
        $bot_patterns = ['bot', 'crawler', 'spider', 'headless', 'curl', 'wget', 'python-requests', 'phantomjs', 'selenium'];
        foreach ($bot_patterns as $pattern) {
            if ($ua_l && strpos($ua_l, $pattern) !== false) {
                return ['quality' => 'bot_like', 'reason' => 'Bot-like user agent'];
            }
        }
        global $wpdb;
        $table = $wpdb->prefix . self::EVENT_TABLE;
        $rules = $this->risk_rule_settings();
        $ip_hash = $this->hash_ip($ip);
        $repeat_count = 0;
        if ($event_type === 'visit' && $ip_hash) {
            $repeat_count = (int)$wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$table} WHERE presell_id=%d AND ip_hash=%s AND event_type='visit' AND created_at >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL %d MINUTE)", $presell_id, $ip_hash, $rules['repeat_window_minutes']));
            if ($repeat_count >= $rules['repeat_visit_limit']) {
                return ['quality' => 'repeated', 'reason' => $rules['emergency_mode'] ? 'Repeated visits from same masked IP (Emergency Mode)' : 'Repeated visits from same masked IP'];
            }
        }
        $time = (int)$p['time_on_page'];
        $scroll = (int)$p['scroll_depth'];
        if ($event_type === 'click') {
            if ($time > 0 && $time <= $rules['fast_click_seconds']) {
                return ['quality' => 'suspicious', 'reason' => $rules['emergency_mode'] ? 'Very fast click (Emergency Mode)' : 'Very fast click'];
            }
            if ($time >= $rules['engaged_seconds'] || $scroll >= $rules['engaged_scroll']) {
                return ['quality' => 'engaged', 'reason' => 'Good engagement before click'];
            }
            if ($time >= max(3, $rules['fast_click_seconds'] + 1)) {
                return ['quality' => 'valid', 'reason' => 'Real forward click'];
            }
        }
        if ($event_type === 'heartbeat') {
            if ($time >= $rules['engaged_seconds'] || $scroll >= $rules['engaged_scroll']) return ['quality' => 'engaged', 'reason' => 'Engaged session'];
        }
        if ($event_type === 'visit') {
            if (empty($p['source']) && empty($p['referrer'])) {
                return ['quality' => 'valid', 'reason' => 'Direct or internal visit'];
            }
            return ['quality' => 'valid', 'reason' => 'Page visit'];
        }
        if ($time <= 1 && $scroll <= 0) {
            return ['quality' => 'low_intent', 'reason' => 'No scroll or engagement yet'];
        }
        return ['quality' => 'valid', 'reason' => 'Normal session'];
    }

    private function client_ip() {
        $headers = ['HTTP_CF_CONNECTING_IP', 'HTTP_X_REAL_IP', 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR'];
        foreach ($headers as $h) {
            if (empty($_SERVER[$h])) continue;
            $raw = sanitize_text_field(wp_unslash($_SERVER[$h]));
            if ($h === 'HTTP_X_FORWARDED_FOR') {
                $parts = explode(',', $raw);
                $raw = trim($parts[0]);
            }
            if (filter_var($raw, FILTER_VALIDATE_IP)) return $raw;
        }
        return '';
    }

    private function hash_ip($ip) {
        if (!$ip) return '';
        return hash_hmac('sha256', $ip, wp_salt('auth'));
    }

    private function mask_ip($ip) {
        if (!$ip) return '';
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            $parts = explode('.', $ip);
            $parts[3] = '0';
            return implode('.', $parts);
        }
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            $parts = explode(':', $ip);
            return implode(':', array_slice($parts, 0, 4)) . '::';
        }
        return '';
    }

    private function detect_geo($payload = []) {
        $code = '';
        $method = 'unknown';

        $headers = [
            'HTTP_CF_IPCOUNTRY',
            'HTTP_CLOUDFRONT_VIEWER_COUNTRY',
            'GEOIP_COUNTRY_CODE',
            'HTTP_X_APPENGINE_COUNTRY',
            'HTTP_X_COUNTRY_CODE',
        ];
        foreach ($headers as $h) {
            if (!empty($_SERVER[$h])) {
                $raw = strtoupper(substr(sanitize_text_field(wp_unslash($_SERVER[$h])), 0, 2));
                if ($raw && $raw !== 'XX' && preg_match('/^[A-Z]{2}$/', $raw)) {
                    $code = $raw;
                    $method = 'server';
                    break;
                }
            }
        }

        if (!$code) {
            $tz = (string)($payload['timezone'] ?? '');
            $tz_map = [
                'America/Sao_Paulo' => 'BR', 'America/Bahia' => 'BR', 'America/Fortaleza' => 'BR', 'America/Recife' => 'BR', 'America/Manaus' => 'BR', 'America/Cuiaba' => 'BR',
                'America/New_York' => 'US', 'America/Chicago' => 'US', 'America/Denver' => 'US', 'America/Los_Angeles' => 'US', 'America/Phoenix' => 'US', 'America/Detroit' => 'US', 'America/Anchorage' => 'US', 'Pacific/Honolulu' => 'US',
                'America/Toronto' => 'CA', 'America/Vancouver' => 'CA', 'America/Montreal' => 'CA', 'America/Edmonton' => 'CA', 'America/Winnipeg' => 'CA',
                'Europe/London' => 'GB', 'Europe/Dublin' => 'IE', 'Europe/Paris' => 'FR', 'Europe/Berlin' => 'DE', 'Europe/Madrid' => 'ES', 'Europe/Lisbon' => 'PT', 'Europe/Rome' => 'IT', 'Europe/Amsterdam' => 'NL', 'Europe/Brussels' => 'BE', 'Europe/Zurich' => 'CH',
                'Asia/Dubai' => 'AE', 'Asia/Kolkata' => 'IN', 'Asia/Tokyo' => 'JP', 'Asia/Singapore' => 'SG', 'Asia/Hong_Kong' => 'HK',
                'Australia/Sydney' => 'AU', 'Australia/Melbourne' => 'AU', 'Australia/Brisbane' => 'AU', 'Pacific/Auckland' => 'NZ',
            ];
            if (isset($tz_map[$tz])) {
                $code = $tz_map[$tz];
                $method = 'timezone';
            }
        }

        if (!$code) {
            $lang = strtolower((string)($payload['language'] ?? ''));
            if (preg_match('/-([a-z]{2})$/', $lang, $m)) {
                $candidate = strtoupper($m[1]);
                $allowed = ['BR','US','GB','CA','AU','NZ','PT','ES','FR','DE','IT','NL','BE','CH','IE','IN','AE','SG','JP','MX','AR','CL','CO'];
                if (in_array($candidate, $allowed, true)) {
                    $code = $candidate;
                    $method = 'language';
                }
            }
        }

        $names = $this->country_names();
        $name = $code && isset($names[$code]) ? $names[$code] : ($code ?: 'Unknown');
        if ($code && $method !== 'server') {
            $name .= ' (inferred)';
        }

        return ['country_code' => $code ?: 'Unknown', 'country' => $name];
    }

    private function country_names() {
        return [
            'BR' => 'Brazil', 'US' => 'United States', 'GB' => 'United Kingdom', 'CA' => 'Canada', 'AU' => 'Australia', 'NZ' => 'New Zealand',
            'PT' => 'Portugal', 'ES' => 'Spain', 'FR' => 'France', 'DE' => 'Germany', 'IT' => 'Italy', 'NL' => 'Netherlands', 'BE' => 'Belgium',
            'CH' => 'Switzerland', 'IE' => 'Ireland', 'IN' => 'India', 'AE' => 'United Arab Emirates', 'SG' => 'Singapore', 'JP' => 'Japan',
            'MX' => 'Mexico', 'AR' => 'Argentina', 'CL' => 'Chile', 'CO' => 'Colombia', 'HK' => 'Hong Kong',
        ];
    }


    private function source_from_referrer($ref) {
        if (!$ref) return 'direct/internal';
        $host = wp_parse_url($ref, PHP_URL_HOST);
        $current = wp_parse_url(home_url(), PHP_URL_HOST);
        if (!$host || $host === $current) return 'direct/internal';
        return preg_replace('/^www\./', '', strtolower($host));
    }


    public function cleanup_old_events($manual = false) {
        global $wpdb;
        $settings = $this->settings();
        $days = max(7, min(365, absint($settings['retention_days'] ?? 30)));
        $cutoff = gmdate('Y-m-d H:i:s', time() - ($days * DAY_IN_SECONDS));
        $table = $wpdb->prefix . self::EVENT_TABLE;
        $deleted = (int)$wpdb->query($wpdb->prepare("DELETE FROM {$table} WHERE created_at < %s", $cutoff));
        return $deleted;
    }

    private function live_stats($minutes = 30) {
        global $wpdb;
        $table = $wpdb->prefix . self::EVENT_TABLE;
        $minutes = max(1, min(1440, (int)$minutes));
        $since_sql = $wpdb->prepare('created_at >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL %d MINUTE)', $minutes);
        $active = (int)$wpdb->get_var("SELECT COUNT(DISTINCT session_id) FROM {$table} WHERE {$since_sql} AND session_id <> ''");
        $events = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE {$since_sql}");
        $clicks = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE {$since_sql} AND event_type='click'");
        return ['active_sessions' => $active, 'events' => $events, 'clicks' => $clicks];
    }

    private function recent_live_events($limit = 50) {
        global $wpdb;
        $table = $wpdb->prefix . self::EVENT_TABLE;
        $limit = max(1, min(100, (int)$limit));
        return $wpdb->get_results($wpdb->prepare("SELECT * FROM {$table} WHERE created_at >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 MINUTE) ORDER BY id DESC LIMIT %d", $limit), ARRAY_A);
    }

    private function risk_rows_recent($id = 0, $limit = 6, $minutes = 30) {
        global $wpdb;
        $minutes = max(1, min(1440, (int)$minutes));
        $where = $wpdb->prepare("quality IN ('suspicious','bot_like','repeated','low_intent') AND created_at >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL %d MINUTE)", $minutes);
        return $this->breakdown_rows('reason', $id, $limit, $where);
    }

    private function global_stats() {
        $count = wp_count_posts(self::CPT);
        $presells = (int)($count->publish ?? 0) + (int)($count->draft ?? 0);
        global $wpdb;
        $table = $wpdb->prefix . self::EVENT_TABLE;
        $date_sql = $this->analytics_date_sql(true);
        $visits = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE event_type='visit'{$date_sql}");
        $clicks = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE event_type='click'{$date_sql}");
        $lead_table = $wpdb->prefix . self::LEAD_TABLE;
        $lead_date_sql = $this->lead_date_sql(true, '');
        $leads = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$lead_table} WHERE 1=1{$lead_date_sql}");
        return ['presells' => $presells, 'visits' => $visits, 'clicks' => $clicks, 'leads' => $leads];
    }

    private function global_vsl_stats() {
        global $wpdb;
        $table = $wpdb->prefix . self::EVENT_TABLE;
        $date_sql = $this->analytics_date_sql(true);
        $identity = "NULLIF(CASE WHEN visitor_id <> '' THEN visitor_id ELSE session_id END, '')";
        $views = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE event_type='vsl_view'{$date_sql}");
        $plays = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE event_type='vsl_play'{$date_sql}");
        $unmutes = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE event_type='vsl_unmute'{$date_sql}");
        $cta_views = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE event_type='vsl_cta_view'{$date_sql}");
        $cta_clicks = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE event_type='vsl_cta_click'{$date_sql}");
        $unique_views = (int)$wpdb->get_var("SELECT COUNT(DISTINCT {$identity}) FROM {$table} WHERE event_type='vsl_view'{$date_sql}");
        $unique_plays = (int)$wpdb->get_var("SELECT COUNT(DISTINCT {$identity}) FROM {$table} WHERE event_type='vsl_play'{$date_sql}");
        $play_rate = $unique_views ? round(($unique_plays / max(1, $unique_views)) * 100, 1) : 0;
        return [
            'views' => $views,
            'plays' => $plays,
            'unmutes' => $unmutes,
            'unique_views' => $unique_views,
            'unique_plays' => $unique_plays,
            'play_rate' => $play_rate,
            'cta_views' => $cta_views,
            'cta_clicks' => $cta_clicks,
        ];
    }

    private function campaign_type_label($template) {
        $map = [
            'cards' => 'Cards Bridge',
            'cookie' => 'Cookie Modal',
            'cookie_modal' => 'Cookie Modal',
            'vsl_video' => 'VSL / Video',
            'vsl_landing' => 'VSL + Landing Page',
            'landing_page' => 'Landing Page',
        ];
        return $map[$template] ?? 'Bridge';
    }

    private function vsl_progress_interval_for_duration($duration) {
        $duration = max(1, (int)$duration);
        if ($duration <= 5) return 1;
        if ($duration <= 30) return 2;
        if ($duration <= 120) return 5;
        if ($duration <= 600) return 10;
        if ($duration <= 1200) return 15;
        return 30;
    }

    private function vsl_retention_from_points($points, $seconds) {
        if (empty($points)) return 0;
        usort($points, function($a, $b) { return ((float)$a['t']) <=> ((float)$b['t']); });
        $seconds = max(0, (float)$seconds);
        $first = reset($points);
        if ($seconds <= (float)$first['t']) return round((float)$first['r'], 2);
        for ($i = 1; $i < count($points); $i++) {
            $a = $points[$i - 1];
            $b = $points[$i];
            $at = (float)$a['t'];
            $bt = (float)$b['t'];
            if ($seconds <= $bt) {
                $span = max(0.01, $bt - $at);
                $ratio = ($seconds - $at) / $span;
                return round(((float)$a['r']) + (((float)$b['r'] - (float)$a['r']) * $ratio), 2);
            }
        }
        $last = end($points);
        return round((float)$last['r'], 2);
    }

    private function vsl_curve_paths_from_points($points, $duration) {
        $duration = max(1, (float)$duration);
        if (empty($points)) $points = [['t' => 0, 'r' => 100], ['t' => $duration, 'r' => 0]];
        usort($points, function($a, $b) { return ((float)$a['t']) <=> ((float)$b['t']); });
        $items = [];
        foreach ($points as $pt) {
            $x = max(0, min(100, round(((float)$pt['t'] / $duration) * 100, 2)));
            $y = max(0, min(100, round(100 - (float)$pt['r'], 2)));
            $items[] = [$x, $y];
        }
        if (empty($items) || $items[0][0] > 0) array_unshift($items, [0, 0]);
        $last = $items[count($items) - 1];
        if ($last[0] < 100) $items[] = [100, $last[1]];
        $line = '';
        foreach ($items as $i => $pt) {
            $line .= ($i === 0 ? 'M' : ' L') . $pt[0] . ' ' . $pt[1];
        }
        $shade = $line . ' L100 100 L0 100 Z';
        return ['line' => $line, 'shade' => $shade];
    }

    private function conversion_performance_stats($id) {
        global $wpdb;
        $table = $wpdb->prefix . self::EVENT_TABLE;
        $date_sql = $this->analytics_date_sql(true);
        $id = absint($id);
        $id_sql = $id ? $wpdb->prepare('presell_id=%d AND ', $id) : '';

        $visits = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE {$id_sql}event_type='visit'{$date_sql}");
        $clicks = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE {$id_sql}event_type='click'{$date_sql}");
        $conversions = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE {$id_sql}event_type='conversion'{$date_sql}");
        $destinations = $wpdb->get_col("SELECT destination FROM {$table} WHERE {$id_sql}event_type='conversion'{$date_sql}");

        $sums = [];
        foreach ((array)$destinations as $dest_value) {
            $dest_value = (string)$dest_value;
            if (!preg_match('/(?:^|[|&?])value=([0-9]+(?:[\.,][0-9]+)?)/', $dest_value, $m)) {
                continue;
            }
            $value = (float)str_replace(',', '.', $m[1]);
            if ($value <= 0) {
                continue;
            }
            $currency = 'USD';
            if (preg_match('/(?:^|[|&?])currency=([A-Z]{3,8})/i', $dest_value, $cm)) {
                $currency = strtoupper($cm[1]);
            }
            if (!isset($sums[$currency])) {
                $sums[$currency] = 0.0;
            }
            $sums[$currency] += $value;
        }

        if (empty($sums)) {
            $revenue = 'USD 0.00';
        } else {
            arsort($sums);
            $pieces = [];
            $i = 0;
            foreach ($sums as $currency => $sum) {
                $pieces[] = $currency . ' ' . number_format((float)$sum, 2, '.', ',');
                $i++;
                if ($i >= 2) {
                    break;
                }
            }
            $remaining = count($sums) - count($pieces);
            $revenue = implode(' · ', $pieces) . ($remaining > 0 ? ' +' . $remaining . ' more' : '');
        }

        $source_rows = $this->breakdown_rows('source', $id, 6, "event_type='conversion'");
        $event_rows = [];
        foreach ((array)$destinations as $dest_value) {
            $label = 'conversion';
            if (preg_match('/^(?:webhook|admin-test|manual|url-match|public|server|thank-you-pixel):([^|]+)/', (string)$dest_value, $m)) {
                $label = sanitize_text_field($m[1]);
            }
            if (!isset($event_rows[$label])) $event_rows[$label] = ['label' => $label, 'count' => 0];
            $event_rows[$label]['count']++;
        }
        uasort($event_rows, function($a, $b) { return ((int)$b['count']) <=> ((int)$a['count']); });
        return [
            'conversions' => $conversions,
            'revenue' => $revenue,
            'visit_rate' => $visits ? round(($conversions / max(1, $visits)) * 100, 2) : 0,
            'click_rate' => $clicks ? round(($conversions / max(1, $clicks)) * 100, 2) : 0,
            'source_rows' => array_values($source_rows),
            'event_rows' => array_slice(array_values($event_rows), 0, 6),
        ];
    }

    private function vsl_performance_stats($id) {
        global $wpdb;
        $table = $wpdb->prefix . self::EVENT_TABLE;
        $events = ['vsl_view','vsl_play','vsl_unmute','vsl_pause','vsl_resume','vsl_25','vsl_50','vsl_75','vsl_100','vsl_cta_view','vsl_cta_click','vsl_hook_view','vsl_pause_image_view','conversion'];
        $out = array_fill_keys($events, 0);
        $out['unique_views'] = 0;
        $out['unique_plays'] = 0;
        $out['play_rate'] = 0;
        $out['engagement_rate'] = 0;
        $out['button_clicks'] = 0;
        $out['conversion_rate'] = 0;
        $out['revenue'] = 'USD 0.00';
        $analytics_meta = $this->vsl_analytics_meta($id);
        $out['custom_metric'] = '—';
        $out['custom_metric_name'] = '';
        $out['custom_metric_value'] = 0;
        $out['custom_metric_label'] = '';
        $out['pitch_label'] = $this->format_seconds_mmss($analytics_meta['pitch_seconds']);
        $out['pitch_retention'] = 0;
        $out['pitch_audience'] = 0;
        $out['pitch_pct'] = 25;
        $out['revenue'] = $analytics_meta['revenue'] ?: 'USD 0.00';
        $out['r25'] = 0;
        $out['r50'] = 0;
        $out['r75'] = 0;
        $out['r100'] = 0;

        $date_sql = $this->analytics_date_sql(true);
        $placeholders = implode(',', array_fill(0, count($events), '%s'));
        $params = array_merge([$id], $events);
        $sql = $wpdb->prepare("SELECT event_type, COUNT(*) c FROM {$table} WHERE presell_id=%d AND event_type IN ({$placeholders}){$date_sql} GROUP BY event_type", $params);
        $rows = $wpdb->get_results($sql, ARRAY_A);
        foreach ((array)$rows as $r) {
            if (isset($out[$r['event_type']])) $out[$r['event_type']] = (int)$r['c'];
        }

        $identity = "NULLIF(CASE WHEN visitor_id <> '' THEN visitor_id ELSE session_id END, '')";
        $out['unique_views'] = (int)$wpdb->get_var($wpdb->prepare("SELECT COUNT(DISTINCT {$identity}) FROM {$table} WHERE presell_id=%d AND event_type='vsl_view'{$date_sql}", $id));
        $out['unique_plays'] = (int)$wpdb->get_var($wpdb->prepare("SELECT COUNT(DISTINCT {$identity}) FROM {$table} WHERE presell_id=%d AND event_type='vsl_play'{$date_sql}", $id));

        $view_base = max(1, (int)$out['unique_views']);
        $play_base = max(1, (int)$out['unique_plays']);
        $out['play_rate'] = $out['unique_views'] ? round($out['unique_plays'] / $view_base * 100, 2) : 0;
        $out['button_clicks'] = (int)$out['vsl_cta_click'];
        $out['conversion_rate'] = $out['unique_plays'] ? round($out['conversion'] / $play_base * 100, 2) : 0;
        $auto_revenue_rows = $wpdb->get_col($wpdb->prepare("SELECT destination FROM {$table} WHERE presell_id=%d AND event_type='conversion'{$date_sql}", $id));
        $auto_revenue_sum = 0.0;
        $auto_currency = '';
        foreach ((array)$auto_revenue_rows as $dest_value) {
            if (preg_match('/(?:^|[|&?])value=([0-9]+(?:[\.,][0-9]+)?)/', (string)$dest_value, $m)) {
                $auto_revenue_sum += (float)str_replace(',', '.', $m[1]);
            }
            if (!$auto_currency && preg_match('/(?:^|[|&?])currency=([A-Z]{3,8})/i', (string)$dest_value, $m)) {
                $auto_currency = strtoupper($m[1]);
            }
        }
        if ($auto_revenue_sum > 0) {
            $out['revenue'] = ($auto_currency ?: 'USD') . ' ' . number_format($auto_revenue_sum, 2, '.', ',');
        }

        $raw_engagement = ($out['vsl_25'] + $out['vsl_50'] + $out['vsl_75'] + $out['vsl_100']) / max(1, (int)$out['vsl_play'] * 4) * 100;
        $out['engagement_rate'] = $out['vsl_play'] ? round($raw_engagement, 2) : 0;

        $milestone_base = max(1, (int)$out['vsl_play']);
        $out['r25'] = min(100, round($out['vsl_25'] / $milestone_base * 100));
        $out['r50'] = min(100, round($out['vsl_50'] / $milestone_base * 100));
        $out['r75'] = min(100, round($out['vsl_75'] / $milestone_base * 100));
        $out['r100'] = min(100, round($out['vsl_100'] / $milestone_base * 100));

        $meta = $this->bridge_meta($id);
        $duration_guess = (int)($meta['vsl_duration_seconds'] ?? 0);
        if ($duration_guess <= 0) {
            $duration_guess = max(20, (int)$wpdb->get_var($wpdb->prepare("SELECT MAX(time_on_page) FROM {$table} WHERE presell_id=%d AND event_type IN ('vsl_100','heartbeat'){$date_sql}", $id)));
        }

        $progress_counts = [];
        $progress_rows = $wpdb->get_results($wpdb->prepare("SELECT event_type, COUNT(DISTINCT {$identity}) c FROM {$table} WHERE presell_id=%d AND event_type LIKE %s{$date_sql} GROUP BY event_type", $id, 'vsl_progress_%'), ARRAY_A);
        foreach ((array)$progress_rows as $r) {
            if (preg_match('/^vsl_progress_([1-9][0-9]{0,4})s$/', (string)$r['event_type'], $m)) {
                $sec = (int)$m[1];
                if ($sec > 0) $progress_counts[$sec] = max($progress_counts[$sec] ?? 0, (int)$r['c']);
            }
        }
        ksort($progress_counts, SORT_NUMERIC);

        $retention_points = [['t' => 0, 'r' => 100]];
        if (!empty($progress_counts) && $out['unique_plays'] > 0) {
            $prev = 100;
            foreach ($progress_counts as $sec => $count) {
                if ($duration_guess > 0 && $sec > $duration_guess) continue;
                $ret = round(min(100, max(0, ($count / $play_base) * 100)), 2);
                $ret = min($prev, $ret);
                $prev = $ret;
                $retention_points[] = ['t' => $sec, 'r' => $ret];
            }
            if (empty($progress_counts[$duration_guess])) {
                $end_ret = $out['r100'] > 0 ? $out['r100'] : min($prev, $this->vsl_estimate_retention_percent(100, $out['r25'], $out['r50'], $out['r75'], $out['r100']));
                $retention_points[] = ['t' => $duration_guess, 'r' => $end_ret];
            }
        } else {
            $retention_points[] = ['t' => round($duration_guess * 0.25, 2), 'r' => $out['r25']];
            $retention_points[] = ['t' => round($duration_guess * 0.50, 2), 'r' => $out['r50']];
            $retention_points[] = ['t' => round($duration_guess * 0.75, 2), 'r' => $out['r75']];
            $retention_points[] = ['t' => $duration_guess, 'r' => $out['r100']];
        }
        $out['has_granular_progress'] = !empty($progress_counts);

        $pitch_seconds = max(0, (int)$analytics_meta['pitch_seconds']);
        $out['pitch_enabled'] = $pitch_seconds > 0;
        if ($out['pitch_enabled']) {
            $pitch_retention = !empty($progress_counts) ? $this->vsl_retention_from_points($retention_points, $pitch_seconds) : $this->vsl_metric_at_seconds($pitch_seconds, $duration_guess, (int)$out['vsl_play'], $out['r25'], $out['r50'], $out['r75'], $out['r100'])['retention'];
            $out['pitch_pct'] = min(100, max(0, round(($pitch_seconds / max(1, $duration_guess)) * 100, 2)));
            $out['pitch_label'] = $this->format_seconds_mmss($pitch_seconds);
            $out['pitch_retention'] = round($pitch_retention, 2);
            $out['pitch_audience'] = $out['unique_plays'] ? (int)round(($out['pitch_retention'] / 100) * $play_base) : 0;
        } else {
            $out['pitch_pct'] = 0;
            $out['pitch_label'] = 'Off';
            $out['pitch_retention'] = 0;
            $out['pitch_audience'] = 0;
        }

        $custom_seconds = max(0, (int)$analytics_meta['custom_seconds']);
        $custom_name = trim((string)$analytics_meta['custom_name']);
        if ($custom_name !== '') {
            $custom_retention = !empty($progress_counts) ? $this->vsl_retention_from_points($retention_points, $custom_seconds) : $this->vsl_metric_at_seconds($custom_seconds, $duration_guess, (int)$out['vsl_play'], $out['r25'], $out['r50'], $out['r75'], $out['r100'])['retention'];
            $out['custom_metric_name'] = $custom_name;
            $out['custom_metric_value'] = round($custom_retention, 2);
            $out['custom_metric_label'] = $this->format_seconds_mmss($custom_seconds);
            $out['custom_metric_audience'] = $out['unique_plays'] ? (int)round(($out['custom_metric_value'] / 100) * $play_base) : 0;
        }

        $paths = $this->vsl_curve_paths_from_points($retention_points, $duration_guess);
        $out['curve_line_d'] = $paths['line'];
        $out['curve_shade_d'] = $paths['shade'];
        $out['curve_y0'] = 0;
        $out['curve_y25'] = max(0, 100 - $out['r25']);
        $out['curve_y50'] = max(0, 100 - $out['r50']);
        $out['curve_y75'] = max(0, 100 - $out['r75']);
        $out['curve_y100'] = max(0, 100 - $out['r100']);
        $bg = $meta['vsl_pause_image'] ?: ($meta['background_image'] ?: plugin_dir_url(__FILE__) . 'assets/logo.png');
        $out['retention_bg'] = $bg;
        $out['retention_video_url'] = $meta['vsl_video_url'] ?? '';
        $out['retention_duration'] = $duration_guess;
        $out['retention_points_json'] = wp_json_encode($retention_points);

        return $out;
    }

    private function vsl_stats($id) {
        global $wpdb;
        $table = $wpdb->prefix . self::EVENT_TABLE;
        $events = ['vsl_view','vsl_play','vsl_unmute','vsl_pause','vsl_resume','vsl_25','vsl_50','vsl_75','vsl_100','vsl_cta_view','vsl_cta_click','vsl_hook_view','vsl_pause_image_view','conversion'];
        $out = array_fill_keys($events, 0);
        $date_sql = $this->analytics_date_sql(true);
        $placeholders = implode(',', array_fill(0, count($events), '%s'));
        $params = array_merge([$id], $events);
        $sql = $wpdb->prepare("SELECT event_type, COUNT(*) c FROM {$table} WHERE presell_id=%d AND event_type IN ({$placeholders}){$date_sql} GROUP BY event_type", $params);
        $rows = $wpdb->get_results($sql, ARRAY_A);
        foreach ((array)$rows as $r) {
            if (isset($out[$r['event_type']])) $out[$r['event_type']] = (int)$r['c'];
        }
        $out['play_rate'] = $out['vsl_view'] ? round($out['vsl_play'] / $out['vsl_view'] * 100, 1) : 0;
        return $out;
    }

    private function stats_for_presell($id) {
        global $wpdb;
        $table = $wpdb->prefix . self::EVENT_TABLE;
        $date_sql = $this->analytics_date_sql(true);
        $visits = (int)$wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$table} WHERE presell_id=%d AND event_type='visit'{$date_sql}", $id));
        $clicks = (int)$wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$table} WHERE presell_id=%d AND event_type='click'{$date_sql}", $id));
        $unique = (int)$wpdb->get_var($wpdb->prepare("SELECT COUNT(DISTINCT visitor_id) FROM {$table} WHERE presell_id=%d AND event_type='visit' AND visitor_id <> ''{$date_sql}", $id));
        $avg_scroll = (int)$wpdb->get_var($wpdb->prepare("SELECT AVG(scroll_depth) FROM {$table} WHERE presell_id=%d{$date_sql}", $id));
        $avg_time = (int)$wpdb->get_var($wpdb->prepare("SELECT AVG(time_on_page) FROM {$table} WHERE presell_id=%d{$date_sql}", $id));
        return ['visits' => $visits, 'clicks' => $clicks, 'unique_visits' => $unique, 'avg_scroll' => $avg_scroll, 'avg_time' => $avg_time];
    }


    private function lead_date_sql($has_where = true, $alias = '') {
        $sql = $this->analytics_date_sql($has_where);
        if ($alias) {
            $sql = preg_replace('/(?<![A-Za-z0-9_\.])created_at(?![A-Za-z0-9_])/', $alias . '.created_at', $sql);
        }
        return $sql;
    }

    private function lead_summary($id = 0) {
        global $wpdb;
        $lead_table = $wpdb->prefix . self::LEAD_TABLE;
        $event_table = $wpdb->prefix . self::EVENT_TABLE;
        $id = absint($id);
        $where_leads = $id ? $wpdb->prepare('WHERE presell_id=%d', $id) : 'WHERE 1=1';
        $where_leads .= $this->lead_date_sql(true, '');
        $where_events = $id ? $wpdb->prepare('WHERE presell_id=%d', $id) : 'WHERE 1=1';
        $where_events .= $this->analytics_date_sql(true);
        $leads = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$lead_table} {$where_leads}");
        $unique = (int)$wpdb->get_var("SELECT COUNT(DISTINCT email) FROM {$lead_table} {$where_leads}");
        $views = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$event_table} {$where_events} AND event_type='lead_view'");
        $submits = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$event_table} {$where_events} AND event_type='lead_submit'");
        return [
            'leads' => $leads,
            'unique_emails' => $unique,
            'views' => $views,
            'submits' => $submits,
            'submit_rate' => $views ? round(($leads / max(1, $views)) * 100, 1) : 0,
        ];
    }

    private function recent_leads($id = 0, $limit = 200) {
        global $wpdb;
        $lead_table = $wpdb->prefix . self::LEAD_TABLE;
        $posts_table = $wpdb->posts;
        $id = absint($id);
        $limit = max(1, min(1000, absint($limit)));
        $where = $id ? $wpdb->prepare('WHERE l.presell_id=%d', $id) : 'WHERE 1=1';
        $where .= $this->lead_date_sql(true, 'l');
        return $wpdb->get_results($wpdb->prepare("SELECT l.*, p.post_title AS bridge_title FROM {$lead_table} l LEFT JOIN {$posts_table} p ON p.ID = l.presell_id {$where} ORDER BY l.id DESC LIMIT %d", $limit), ARRAY_A);
    }

    private function export_leads_csv($id = 0) {
        $rows = $this->recent_leads($id, 5000);
        if (ob_get_length()) { @ob_clean(); }
        nocache_headers();
        header('Content-Type: text/csv; charset=utf-8');
        header('Content-Disposition: attachment; filename=invalid-traffic-leads-' . date('Y-m-d') . '.csv');
        $out = fopen('php://output', 'w');
        fputcsv($out, ['created_at','email','name','phone','bridge_id','bridge_title','landing_url','source','medium','campaign','destination','referrer']);
        foreach ($rows as $row) {
            fputcsv($out, [
                $row['created_at'] ?? '',
                $row['email'] ?? '',
                $row['name'] ?? '',
                $row['phone'] ?? '',
                $row['presell_id'] ?? '',
                $row['bridge_title'] ?? '',
                $row['landing_url'] ?? '',
                $row['source'] ?? '',
                $row['medium'] ?? '',
                $row['campaign'] ?? '',
                $row['destination'] ?? '',
                $row['referrer'] ?? '',
            ]);
        }
        fclose($out);
        exit;
    }

    private function quality_breakdown($id = 0) {
        global $wpdb;
        $table = $wpdb->prefix . self::EVENT_TABLE;
        $where = $id ? $wpdb->prepare('WHERE presell_id=%d', $id) : 'WHERE 1=1';
        $where .= $this->analytics_date_sql(true);
        $rows = $wpdb->get_results("SELECT session_id, quality FROM {$table} {$where} ORDER BY id ASC LIMIT 5000", ARRAY_A);
        $out = ['valid' => 0, 'engaged' => 0, 'low_intent' => 0, 'suspicious' => 0, 'bot_like' => 0, 'repeated' => 0];
        $priority = ['low_intent' => 1, 'valid' => 2, 'engaged' => 3, 'suspicious' => 4, 'bot_like' => 5, 'repeated' => 5];
        $sessions = [];
        foreach ($rows as $r) {
            $sid = $r['session_id'] ?: 'event_' . count($sessions);
            $q = isset($out[$r['quality']]) ? $r['quality'] : 'valid';
            if (!isset($sessions[$sid]) || ($priority[$q] ?? 0) > ($priority[$sessions[$sid]] ?? 0)) {
                $sessions[$sid] = $q;
            }
        }
        foreach ($sessions as $q) {
            if (isset($out[$q])) $out[$q]++;
        }
        return $out;
    }


    private function quality_label($q) {
        $map = ['valid' => 'Valid', 'engaged' => 'Engaged', 'low_intent' => 'Low intent', 'suspicious' => 'Suspicious', 'bot_like' => 'Bot-like', 'repeated' => 'Repeated'];
        return $map[$q] ?? ucfirst($q);
    }

    private function daily_series($id) {
        global $wpdb;
        $table = $wpdb->prefix . self::EVENT_TABLE;
        $range = $this->analytics_period();
        $tz = wp_timezone();
        $utc = new DateTimeZone('UTC');
        $labels = [];
        $visits = [];
        $clicks = [];

        if (!empty($range['start']) && !empty($range['end'])) {
            $start = DateTime::createFromFormat('Y-m-d H:i:s', $range['start'], $utc);
            $end = DateTime::createFromFormat('Y-m-d H:i:s', $range['end'], $utc);
            if (!$start || !$end) {
                $start = new DateTime('-6 days', $tz);
                $end = new DateTime('tomorrow', $tz);
            } else {
                $start->setTimezone($tz);
                $end->setTimezone($tz);
            }
            $days = max(1, min(62, (int)$start->diff($end)->days));
        } else {
            $start = new DateTime('-6 days', $tz);
            $end = new DateTime('tomorrow', $tz);
            $days = 7;
        }

        for ($i = 0; $i < $days; $i++) {
            $day = clone $start;
            $day->modify("+{$i} days");
            $key = $day->format('Y-m-d');
            $labels[] = $day->format('M d');
            $visits[$key] = 0;
            $clicks[$key] = 0;
        }

        $where = $id ? $wpdb->prepare('WHERE presell_id=%d', $id) : 'WHERE 1=1';
        $where .= $this->analytics_date_sql(true);
        if (empty($range['start']) || empty($range['end'])) {
            $where .= ' AND created_at >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 7 DAY)';
        }
        $rows = $wpdb->get_results("SELECT DATE(created_at) d, event_type, COUNT(*) c FROM {$table} {$where} AND event_type IN ('visit','click') GROUP BY DATE(created_at), event_type", ARRAY_A);
        foreach ((array)$rows as $r) {
            if ($r['event_type'] === 'visit' && isset($visits[$r['d']])) $visits[$r['d']] = (int)$r['c'];
            if ($r['event_type'] === 'click' && isset($clicks[$r['d']])) $clicks[$r['d']] = (int)$r['c'];
        }
        return ['labels' => $labels, 'visits' => array_values($visits), 'clicks' => array_values($clicks)];
    }

    private function recent_timeline_sessions($id, $limit = 4) {
        global $wpdb;
        $table = $wpdb->prefix . self::EVENT_TABLE;
        $date_sql = $this->analytics_date_sql(true);
        $rows = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$table} WHERE presell_id=%d{$date_sql} ORDER BY id DESC LIMIT 160", $id), ARRAY_A);
        if (empty($rows)) return [];
        $grouped = [];
        foreach ($rows as $row) {
            $sid = $row['session_id'] ?: 'event_' . $row['id'];
            if (!isset($grouped[$sid])) $grouped[$sid] = [];
            $grouped[$sid][] = $row;
        }
        $out = [];
        foreach ($grouped as $sid => $events) {
            usort($events, function($a, $b){ return strcmp((string)$a['created_at'], (string)$b['created_at']); });
            $first = reset($events);
            $steps = [];
            foreach ($events as $ev) {
                $event_labels = ['visit' => 'Page visit', 'click' => 'Forward click', 'heartbeat' => 'Heartbeat', 'vsl_view' => 'VSL view', 'vsl_play' => 'VSL play', 'vsl_unmute' => 'Unmute', 'vsl_pause' => 'Pause', 'vsl_resume' => 'Resume', 'vsl_25' => '25% watched', 'vsl_50' => '50% watched', 'vsl_75' => '75% watched', 'vsl_100' => '100% watched', 'vsl_cta_view' => 'CTA appeared', 'vsl_cta_click' => 'CTA clicked'];
                $label = $event_labels[$ev['event_type']] ?? ucfirst(str_replace('_', ' ', (string)$ev['event_type']));
                $steps[] = [
                    'label' => $label,
                    'class' => sanitize_html_class($ev['quality'] ?: 'valid'),
                    'time' => ((int)$ev['time_on_page']) . 's',
                    'reason' => $ev['reason'] ?: $this->quality_label($ev['quality']),
                ];
            }
            $out[] = [
                'label' => $this->short_session_label($sid),
                'source' => $this->clean_source_label($first['source'] ?? 'direct/internal'),
                'country' => $first['country_code'] ?: 'Unknown',
                'device' => $first['device'] ?: 'unknown',
                'events' => $steps,
                'last' => end($events)['created_at'] ?? '',
            ];
            if (count($out) >= $limit) break;
        }
        return $out;
    }

    private function short_session_label($sid) {
        $sid = preg_replace('/[^a-zA-Z0-9_\-]/', '', (string)$sid);
        return $sid ? 'Session ' . substr($sid, -6) : 'Session';
    }

    private function recent_events($id, $limit = 50) {
        global $wpdb;
        $table = $wpdb->prefix . self::EVENT_TABLE;
        $date_sql = $this->analytics_date_sql(true);
        return $wpdb->get_results($wpdb->prepare("SELECT * FROM {$table} WHERE presell_id=%d{$date_sql} ORDER BY id DESC LIMIT %d", $id, $limit), ARRAY_A);
    }


    private function breakdown_rows($field, $id = 0, $limit = 6, $extra_where = '1=1') {
    global $wpdb;
    $table = $wpdb->prefix . self::EVENT_TABLE;
    $allowed = ['source', 'campaign', 'country_code', 'device', 'browser', 'reason'];

    if (!in_array($field, $allowed, true)) {
        return [];
    }

    $where = $extra_where ?: '1=1';

    if ($id) {
        $where .= $wpdb->prepare(' AND presell_id=%d', $id);
    }
    $where .= $this->analytics_date_sql(true);

    $limit = max(1, min(20, (int)$limit));

    $sql = "SELECT {$field} label, COUNT(*) count_value
            FROM {$table}
            WHERE {$where}
            GROUP BY {$field}
            ORDER BY count_value DESC";

    $rows = $wpdb->get_results($sql, ARRAY_A);
    $grouped = [];

    foreach ((array)$rows as $r) {
        $label = trim((string)($r['label'] ?? ''));

        if ($field === 'source') {
            $label = $this->clean_source_label($label);
        }

        if ($field === 'country_code' && ($label === '' || strtolower($label) === 'unknown')) {
            $label = 'Unknown';
        } elseif ($field === 'country_code') {
            $names = $this->country_names();
            $label = isset($names[$label]) ? $label . ' · ' . $names[$label] : $label;
        } elseif ($label === '') {
            $label = '—';
        }

        if (!isset($grouped[$label])) {
            $grouped[$label] = 0;
        }

        $grouped[$label] += (int)$r['count_value'];
    }

    arsort($grouped);

    $out = [];

    foreach (array_slice($grouped, 0, $limit, true) as $label => $count) {
        $out[] = [
            'label' => $label,
            'count' => (int)$count,
        ];
    }

    return $out;
}

    private function risk_rows($id = 0, $limit = 6) {
        return $this->breakdown_rows('reason', $id, $limit, "quality IN ('suspicious','bot_like','repeated','low_intent')");
    }

    private function mini_breakdown_html($rows, $empty = 'No data yet.') {
        if (empty($rows)) {
            return '<div class="itp-empty-mini">' . esc_html($empty) . '</div>';
        }
        $max = 1;
        foreach ($rows as $row) {
            $max = max($max, (int)$row['count']);
        }
        $html = '<div class="itp-breakdown-list">';
        foreach ($rows as $row) {
            $pct = round(((int)$row['count'] / $max) * 100, 1);
            $html .= '<div class="itp-breakdown-row"><div><strong>' . esc_html($row['label']) . '</strong><span>' . esc_html((string)$row['count']) . '</span></div><i style="width:' . esc_attr($pct) . '%"></i></div>';
        }
        $html .= '</div>';
        return $html;
    }

    private function country_map_html($rows, $empty = 'No country data yet.') {
        $total = 0;
        $values = [];
        $labels = [];
        $legend = [];
        $unknown = 0;

        foreach ((array)$rows as $row) {
            $label = trim((string)($row['label'] ?? ''));
            $count = (int)($row['count'] ?? 0);
            if ($count <= 0) continue;
            $total += $count;

            $code = strtoupper(trim(strtok($label, '·')));
            $code = preg_replace('/[^A-Z]/', '', $code);

            if ($code === '' || $code === 'UNKNOWN' || $code === 'UNK' || strlen($code) !== 2) {
                $unknown += $count;
                continue;
            }

            if (!isset($values[$code])) {
                $values[$code] = 0;
                $labels[$code] = $label !== '' ? $label : $code;
            }
            $values[$code] += $count;
        }

        arsort($values);

        foreach ($values as $code => $count) {
            $pct = $total > 0 ? round(($count / $total) * 100, 1) : 0;
            $legend[] = ['code' => $code, 'label' => $labels[$code] ?? $code, 'count' => (int)$count, 'pct' => $pct];
        }

        $payload = [
            'values' => $values,
            'labels' => $labels,
            'legend' => $legend,
            'unknown' => $unknown,
            'total' => $total,
            'empty' => $empty,
        ];

        $html = '<div class="itp-country-map-wrap">';
        $html .= '<div class="itp-world-map itp-jvm-card" role="img" aria-label="Country traffic map">';
        $html .= '<div class="itp-jvm-map" data-itp-country-map="' . esc_attr(wp_json_encode($payload)) . '"></div>';
        if ($total <= 0) {
            $html .= '<div class="itp-empty-mini itp-map-empty-overlay">' . esc_html($empty) . '</div>';
        }
        $html .= '</div>';

        $html .= '<div class="itp-country-map-side"><strong>Top country signals</strong><div class="itp-map-legend">';

        foreach (array_slice($legend, 0, 8) as $m) {
            $html .= '<div><span><i></i>' . esc_html($m['label']) . '</span><b>' . esc_html((string)$m['count']) . '</b><em>' . esc_html((string)$m['pct']) . '%</em></div>';
        }

        if ($unknown > 0) {
            $pct = $total > 0 ? round(($unknown / $total) * 100, 1) : 0;
            $html .= '<div><span><i></i>Unknown / not detected</span><b>' . esc_html((string)$unknown) . '</b><em>' . esc_html((string)$pct) . '%</em></div>';
        }

        if (empty($legend) && $unknown <= 0) {
            $html .= '<div class="itp-empty-mini">' . esc_html($empty) . '</div>';
        }

        $html .= '</div><p>Interactive vector map powered locally by the plugin. Country detection uses server/CDN geo when available; otherwise it falls back to timezone and browser language signals.</p></div>';
        $html .= '</div>';
        return $html;
    }


    private function world_map_svg() {
        return '<svg class="itp-svg-world" viewBox="0 0 1000 520" aria-hidden="true" focusable="false" preserveAspectRatio="xMidYMid meet">
            <defs>
                <linearGradient id="itpLand" x1="0" y1="0" x2="1" y2="1">
                    <stop offset="0%" stop-color="#c8dded" stop-opacity="0.96" />
                    <stop offset="100%" stop-color="#a9c3d9" stop-opacity="0.82" />
                </linearGradient>
                <linearGradient id="itpLandHot" x1="0" y1="0" x2="1" y2="1">
                    <stop offset="0%" stop-color="#dff5ff" stop-opacity="0.98" />
                    <stop offset="100%" stop-color="#bad7ea" stop-opacity="0.9" />
                </linearGradient>
                <filter id="itpMapSoftShadow" x="-20%" y="-20%" width="140%" height="140%">
                    <feDropShadow dx="0" dy="12" stdDeviation="12" flood-color="#071528" flood-opacity="0.10" />
                </filter>
            </defs>
            <g class="itp-map-latlines">
                <path d="M40 110H960"/><path d="M40 180H960"/><path d="M40 250H960"/><path d="M40 320H960"/><path d="M40 390H960"/>
                <path d="M145 45V475"/><path d="M250 45V475"/><path d="M355 45V475"/><path d="M460 45V475"/><path d="M565 45V475"/><path d="M670 45V475"/><path d="M775 45V475"/><path d="M880 45V475"/>
            </g>
            <g class="itp-map-land" filter="url(#itpMapSoftShadow)">
                <path d="M116 150C145 92 235 68 316 91C374 107 405 143 374 186C349 222 288 215 262 250C239 281 204 300 164 266C128 236 78 237 66 205C58 183 82 162 116 150Z"/>
                <path d="M255 253C303 253 349 276 383 307C368 324 326 321 291 297C270 283 250 276 255 253Z"/>
                <path d="M345 300C386 322 410 374 391 432C377 481 328 506 295 457C262 407 267 345 302 314C315 302 328 295 345 300Z"/>
                <path d="M276 54C334 34 399 45 423 76C390 108 318 105 258 81C257 69 265 60 276 54Z"/>
                <path d="M487 142C533 124 581 139 608 172C584 198 523 190 485 177C468 165 469 151 487 142Z"/>
                <path d="M527 200C591 203 635 266 622 343C611 414 556 432 512 378C476 333 466 253 527 200Z"/>
                <path d="M592 145C711 91 854 123 923 205C895 265 797 254 752 294C716 327 660 285 630 240C604 202 558 177 592 145Z"/>
                <path d="M636 222C684 213 727 235 747 279C710 294 661 276 636 222Z"/>
                <path d="M752 378C816 356 884 378 913 425C876 465 801 461 746 431C731 412 735 391 752 378Z"/>
                <path d="M880 301C906 294 938 306 952 330C930 352 892 351 871 330C866 317 870 307 880 301Z"/>
            </g>
            <g class="itp-map-coast-glow">
                <path d="M40 260C190 240 260 290 340 300C458 315 487 180 600 205C740 235 805 190 960 210"/>
                <path d="M80 380C210 335 310 455 420 398C525 343 612 438 730 390C835 350 890 380 940 430"/>
            </g>
        </svg>';
    }

    private function country_map_points() {
        return [
            'US' => [22, 40], 'CA' => [20, 24], 'MX' => [20, 54],
            'BR' => [35, 72], 'AR' => [32, 84], 'CL' => [28, 82], 'CO' => [29, 64],
            'GB' => [47, 34], 'IE' => [45, 35], 'PT' => [45, 45], 'ES' => [47, 46], 'FR' => [50, 43], 'DE' => [53, 39], 'IT' => [54, 48], 'NL' => [52, 37], 'BE' => [51, 40], 'CH' => [52, 43],
            'AE' => [64, 55], 'IN' => [70, 58], 'SG' => [76, 70], 'HK' => [79, 58], 'JP' => [86, 45],
            'AU' => [82, 80], 'NZ' => [90, 86]
        ];
    }

    private function bridge_path($id) {
        $path = wp_parse_url(get_permalink($id), PHP_URL_PATH);
        return $path ? $path : '/' . trim((string)get_post_field('post_name', $id), '/') . '/';
    }

    private function clean_source_label($source) {
        $source = trim((string)$source);
        if ($source === '') return 'direct/internal';
        $host = wp_parse_url(home_url(), PHP_URL_HOST);
        $source_host = preg_replace('/^www\./', '', strtolower($source));
        $home_host = preg_replace('/^www\./', '', strtolower((string)$host));
        if ($home_host && $source_host === $home_host) return 'direct/internal';
        return $source;
    }

    private function analytics_rows() {
        $posts = get_posts(['post_type' => self::CPT, 'post_status' => ['publish', 'draft'], 'numberposts' => 200]);
        $out = [];
        global $wpdb;
        $table = $wpdb->prefix . self::EVENT_TABLE;
        foreach ($posts as $p) {
            $stats = $this->stats_for_presell($p->ID);
            $meta = $this->bridge_meta($p->ID);
            $is_vsl = in_array(($meta['template'] ?? ''), ['vsl_video','vsl_landing'], true);
            $vsl = $is_vsl ? $this->vsl_performance_stats($p->ID) : null;
            $date_sql = $this->analytics_date_sql(true);
            $top = $wpdb->get_row($wpdb->prepare("SELECT source, campaign, country_code FROM {$table} WHERE presell_id=%d{$date_sql} ORDER BY id DESC LIMIT 1", $p->ID), ARRAY_A);
            $ctr = $stats['visits'] ? round($stats['clicks'] / $stats['visits'] * 100, 1) : 0;
            $q = $this->quality_breakdown($p->ID);
            arsort($q);
            $topq = key($q);
            $out[] = ['id' => $p->ID, 'title' => $p->post_title, 'type' => $this->campaign_type_label($meta['template'] ?? 'cards'), 'path' => $this->bridge_path($p->ID), 'source' => $this->clean_source_label($top['source'] ?? '—'), 'country' => $top['country_code'] ?? 'Unknown', 'campaign' => $top['campaign'] ?? '—', 'visits' => $stats['visits'], 'clicks' => $stats['clicks'], 'ctr' => $ctr, 'vsl_plays' => $vsl ? $vsl['vsl_play'] : '—', 'vsl_play_rate' => $vsl ? $vsl['play_rate'] : '—', 'avg_scroll' => $stats['avg_scroll'], 'avg_time' => $stats['avg_time'], 'quality' => $this->quality_label($topq)];
        }
        return $out;
    }

    private function validate_bridge($id) {
        $meta = $this->bridge_meta($id);
        $s = $this->settings();
        $template = $meta['template'] ?? 'blue_cards';
        $is_cookie = ($template === 'cookie_modal');
        $is_vsl = in_array($template, ['vsl_video','vsl_landing'], true);
        $is_landing = in_array($template, ['vsl_landing','landing_page'], true);
        if ($is_cookie) {
            $content_ok = (bool)($meta['cookie_title'] && $meta['cookie_copy']);
        } elseif ($is_vsl) {
            $content_ok = (bool)($meta['vsl_headline'] && $meta['vsl_video_url']);
        } elseif ($is_landing) {
            $content_ok = (bool)($meta['landing_headline'] && $meta['landing_copy']);
        } else {
            $content_ok = (bool)($meta['headline'] && $meta['short_copy']);
        }
        $policy_ok = (bool)((int)$s['contact_page_id'] && (int)$s['privacy_page_id'] && (int)$s['terms_page_id']);
        $asset_ok = $is_vsl ? (bool)$meta['vsl_video_url'] : ($is_cookie ? (bool)($meta['background_image'] || $meta['top_cta_url']) : (bool)($meta['logo'] || $meta['background_image']));
        $dest_ok = $is_vsl ? true : (bool)($meta['top_cta_url'] || $meta['secondary_cta_url'] || $meta['landing_button_url']);
        foreach ((array)$meta['cards'] as $card) {
            if (!$is_cookie && !empty($card['image'])) $asset_ok = true;
            if (!empty($card['url'])) $dest_ok = true;
        }
        $https_ok = strpos(get_permalink($id), 'https://') === 0;
        $checks = [
            ['label' => 'Campaign URL', 'text' => 'Use this bridge URL in paid ads, not the final money page', 'ok' => true],
            ['label' => 'No-ad bridge', 'text' => 'AdSense and common ad scripts are stripped on bridge pages', 'ok' => true],
            ['label' => 'UTM passthrough', 'text' => 'UTMs and major click IDs are preserved on forward clicks', 'ok' => true],
            ['label' => $is_cookie ? 'Cookie content' : ($is_vsl ? 'VSL video' : 'Page content'), 'text' => $content_ok ? 'Core campaign content is present' : ($is_vsl ? 'Add a WordPress Media video URL and headline' : ($is_cookie ? 'Add cookie title and text' : 'Add headline and short copy')), 'ok' => $content_ok],
            ['label' => 'Destination links', 'text' => $dest_ok ? 'Destination/CTA setup is usable' : 'Add at least one destination URL', 'ok' => $dest_ok],
            ['label' => 'Policy links', 'text' => $policy_ok ? 'Contact / Privacy / Terms connected' : 'Connect policy pages', 'ok' => $policy_ok],
            ['label' => 'HTTPS', 'text' => $https_ok ? 'Secure URL detected' : 'Use HTTPS for paid traffic', 'ok' => $https_ok],
            ['label' => 'Preview', 'text' => $asset_ok ? 'Landing preview available' : 'Add destination or screenshot URL', 'ok' => $asset_ok],
        ];
        $score = 0;
        $points = 100 / max(1, count($checks));
        foreach ($checks as $c) $score += $c['ok'] ? $points : 0;
        $score = min(100, (int)round($score));
        $fixes = [];
        foreach ($checks as $c) if (!$c['ok']) $fixes[] = $c['text'] . '.';
        $class = $score >= 90 ? 'ready' : ($score >= 70 ? 'review' : 'danger');
        $title = $score >= 90 ? 'Ready' : 'Review';
        return compact('score', 'checks', 'fixes', 'class', 'title', 'asset_ok', 'dest_ok', 'policy_ok');
    }

    private function validate_modal($id, $validate) {
        ?>
        <div class="itp-modal-backdrop"><div class="itp-modal"><a class="itp-modal-close" href="<?php echo esc_url(admin_url('admin.php?page=itp-presells')); ?>">×</a><div class="itp-readiness mini"><div class="itp-score-circle <?php echo esc_attr($validate['class']); ?>"><strong><?php echo esc_html($validate['score']); ?>%</strong><span>/100</span></div><div class="itp-readiness-body"><h2>Campaign Readiness Check: <?php echo esc_html($validate['title']); ?></h2><p>This is a setup checklist, not a Google Ads, Meta, AdSense or affiliate-network approval guarantee.</p><div class="itp-check-grid"><?php foreach ($validate['checks'] as $check) echo '<div class="' . ($check['ok'] ? 'ok' : 'warn') . '"><strong>' . esc_html($check['label']) . '</strong><span>' . esc_html($check['text']) . '</span></div>'; ?></div><?php if (empty($validate['fixes'])) echo '<p class="itp-ok-text">No obvious setup problems detected.</p>'; else echo '<div class="itp-fixes"><strong>Recommended fixes</strong><p>' . esc_html(implode(' ', $validate['fixes'])) . '</p></div>'; ?><div class="itp-row-actions"><a class="itp-btn" href="<?php echo esc_url(get_permalink($id)); ?>" target="_blank">Open bridge</a><a class="itp-btn itp-btn-ghost" href="<?php echo esc_url(admin_url('admin.php?page=itp-create&id=' . $id)); ?>">Edit bridge</a><a class="itp-btn" href="<?php echo esc_url(admin_url('admin.php?page=itp-analytics&id=' . $id . $this->analytics_query_suffix())); ?>">Open full analytics</a></div></div></div></div></div>
        <?php
    }

    public function ajax_validate_bridge() {
        if (!current_user_can('manage_options')) wp_send_json_error([], 403);
        check_ajax_referer('itp_admin_nonce', 'nonce');
        $id = absint($_POST['id'] ?? 0);
        if (!$id || get_post_type($id) !== self::CPT) wp_send_json_error([], 404);
        $v = $this->validate_bridge($id);
        ob_start();
        $this->validate_modal($id, $v);
        $html = ob_get_clean();
        wp_send_json_success(['html' => $html]);
    }


    public function ajax_reorder_bridges() {
        if (!current_user_can('manage_options')) wp_send_json_error([], 403);
        check_ajax_referer('itp_admin_nonce', 'nonce');
        $ids = isset($_POST['ids']) && is_array($_POST['ids']) ? array_map('absint', $_POST['ids']) : [];
        $order = 0;
        foreach ($ids as $id) {
            if ($id && get_post_type($id) === self::CPT) {
                wp_update_post(['ID' => $id, 'menu_order' => $order]);
                $order++;
            }
        }
        wp_send_json_success(['updated' => $order]);
    }

    private function channel_mix_rows($id = 0, $limit = 8) {
        global $wpdb;
        $table = $wpdb->prefix . self::EVENT_TABLE;
        $where = "event_type IN ('visit','click')";
        if ($id) {
            $where .= $wpdb->prepare(' AND presell_id=%d', $id);
        }
        $where .= $this->analytics_date_sql(true);
        $rows = $wpdb->get_results("SELECT source, medium, campaign, quality FROM {$table} WHERE {$where} ORDER BY id DESC LIMIT 5000", ARRAY_A);
        $out = [];
        foreach ((array)$rows as $r) {
            $label = $this->classify_channel($r);
            if (!isset($out[$label])) $out[$label] = 0;
            $out[$label]++;
        }
        arsort($out);
        $final = [];
        foreach (array_slice($out, 0, max(1, min(20, (int)$limit)), true) as $label => $count) {
            $final[] = ['label' => $label, 'count' => (int)$count];
        }
        return $final;
    }

    private function classify_channel($row) {
        $source = strtolower((string)($row['source'] ?? ''));
        $medium = strtolower((string)($row['medium'] ?? ''));
        $campaign = strtolower((string)($row['campaign'] ?? ''));
        $quality = strtolower((string)($row['quality'] ?? ''));
        $hay = $source . ' ' . $medium . ' ' . $campaign;
        if (in_array($quality, ['bot_like','repeated','suspicious'], true)) return 'Risk / suspicious';
        if ($source === '' || $source === '—' || $source === 'direct/internal' || $source === 'direct') return 'Direct / internal';
        foreach (['taboola','outbrain','mgid','revcontent','zemanta'] as $n) if (strpos($hay, $n) !== false) return 'Native ads';
        foreach (['facebook','instagram','meta','tiktok','twitter','x.com','linkedin','pinterest','reddit'] as $n) if (strpos($hay, $n) !== false) return 'Social';
        foreach (['google','bing','yahoo','duckduckgo','baidu','yandex'] as $n) {
            if (strpos($source, $n) !== false && !preg_match('/cpc|ppc|paid|ads|display|native/', $hay)) return 'Search / organic';
        }
        if (preg_match('/cpc|ppc|paid|ads|display|campaign|gclid|fbclid|msclkid|ttclid/', $hay)) return 'Paid campaign';
        return 'Referral / other';
    }

    private function create_policy_pages() {
        $settings = $this->settings();
        $pages = [
            'contact_page_id' => [
                'title' => 'Contact',
                'slugs' => ['contact', 'contato', 'contact-us', 'fale-conosco'],
                'content' => '<h1>Contact</h1><p>For questions about this website, advertising campaigns or content, please contact the site owner using the email or contact form provided by this website.</p>'
            ],
            'privacy_page_id' => [
                'title' => 'Privacy Policy',
                'slugs' => ['privacy-policy', 'privacy', 'politica-de-privacidade', 'privacidade'],
                'content' => '<h1>Privacy Policy</h1><p>This website may collect basic analytics, device and traffic information to understand visits, clicks and campaign performance. The site may also use cookies or similar technologies when enabled by the owner.</p><p>External destination websites may have their own privacy practices.</p>'
            ],
            'terms_page_id' => [
                'title' => 'Terms of Use',
                'slugs' => ['terms-of-use', 'terms', 'termos-de-uso', 'termos'],
                'content' => '<h1>Terms of Use</h1><p>By using this website, you agree to use it responsibly. The pages on this site may contain links to external websites, products, articles or offers. The site owner is responsible for the final content, claims and compliance of any campaign using these pages.</p>'
            ],
        ];
        foreach ($pages as $key => $data) {
            if (!empty($settings[$key]) && get_post((int)$settings[$key])) continue;
            $existing = 0;
            if ($key === 'privacy_page_id' && function_exists('wp_get_privacy_policy_page_id')) {
                $privacy_id = (int)wp_get_privacy_policy_page_id();
                if ($privacy_id && get_post($privacy_id)) $existing = $privacy_id;
            }
            if (!$existing) {
                foreach ($data['slugs'] as $slug) {
                    $page = get_page_by_path($slug);
                    if ($page) { $existing = (int)$page->ID; break; }
                }
            }
            if (!$existing) {
                $page = get_page_by_title($data['title']);
                if ($page) $existing = (int)$page->ID;
            }
            if ($existing) {
                $settings[$key] = $existing;
                continue;
            }
            $pid = wp_insert_post(['post_type' => 'page', 'post_status' => 'publish', 'post_title' => $data['title'], 'post_content' => $data['content']]);
            if ($pid && !is_wp_error($pid)) $settings[$key] = $pid;
        }
        update_option(self::OPT, $settings, false);
    }

    private function export_csv($id = 0) {
        global $wpdb;
        $table = $wpdb->prefix . self::EVENT_TABLE;
        $lead_table = $wpdb->prefix . self::LEAD_TABLE;
        $where = $id ? $wpdb->prepare('WHERE presell_id=%d', $id) : 'WHERE 1=1';
        $where .= $this->analytics_date_sql(true);
        $rows = $wpdb->get_results("SELECT * FROM {$table} {$where} ORDER BY id DESC LIMIT 5000", ARRAY_A);

        // Include lead fields in the bridge analytics export when a lead belongs to the same session/visitor.
        // The dedicated Leads export remains the clean email list; this makes the Analytics CSV useful too.
        $lead_where = $id ? $wpdb->prepare('WHERE presell_id=%d', $id) : 'WHERE 1=1';
        $lead_where .= $this->lead_date_sql(true, '');
        $lead_rows = $wpdb->get_results("SELECT * FROM {$lead_table} {$lead_where} ORDER BY id DESC LIMIT 5000", ARRAY_A);
        $lead_by_session = [];
        $lead_by_visitor = [];
        $lead_by_landing = [];
        foreach ((array)$lead_rows as $lead) {
            if (!empty($lead['session_id'])) $lead_by_session[(string)$lead['session_id']] = $lead;
            if (!empty($lead['visitor_id'])) $lead_by_visitor[(string)$lead['visitor_id']] = $lead;
            $landing_key = (string)($lead['presell_id'] ?? '') . '|' . (string)($lead['landing_url'] ?? '');
            if (trim($landing_key, '|') !== '') $lead_by_landing[$landing_key] = $lead;
        }

        if (ob_get_length()) { @ob_clean(); }
        nocache_headers();
        header('Content-Type: text/csv; charset=utf-8');
        header('Content-Disposition: attachment; filename=invalid-traffic-events-' . date('Y-m-d') . '.csv');
        $out = fopen('php://output', 'w');
        if (!empty($rows)) {
            $base_header = array_keys($rows[0]);
            $lead_header = ['lead_email','lead_name','lead_phone'];
            fputcsv($out, array_merge($base_header, $lead_header));
            foreach ($rows as $row) {
                $lead = null;
                if (!$lead && !empty($row['session_id']) && isset($lead_by_session[(string)$row['session_id']])) $lead = $lead_by_session[(string)$row['session_id']];
                if (!$lead && !empty($row['visitor_id']) && isset($lead_by_visitor[(string)$row['visitor_id']])) $lead = $lead_by_visitor[(string)$row['visitor_id']];
                $landing_key = (string)($row['presell_id'] ?? '') . '|' . (string)($row['landing_url'] ?? '');
                if (!$lead && isset($lead_by_landing[$landing_key])) $lead = $lead_by_landing[$landing_key];
                fputcsv($out, array_merge($row, [
                    $lead['email'] ?? '',
                    $lead['name'] ?? '',
                    $lead['phone'] ?? '',
                ]));
            }
        } else {
            fputcsv($out, ['no_events']);
        }
        fclose($out);
        exit;
    }
}
if (!function_exists('it_maybe_auto_check_license')) {
    function it_maybe_auto_check_license() {
        if (!is_admin()) {
            return;
        }

        if (!current_user_can('manage_options')) {
            return;
        }

        $page = isset($_GET['page']) ? sanitize_text_field((string) $_GET['page']) : '';

        if ($page === '' || strpos($page, 'itp-') !== 0) {
            return;
        }

        if (!function_exists('itp_license_check')) {
            return;
        }

        $license_key = trim((string) get_option('itp_license_key', ''));

        if ($license_key === '') {
            return;
        }

        $last_check = (int) get_option('itp_license_last_check', 0);

        // Para teste: checa a cada 60 segundos.
        // Depois pode trocar para 5 * MINUTE_IN_SECONDS.
        if ($last_check && (time() - $last_check) < 60) {
            return;
        }

        itp_license_check(true);
    }
}
register_activation_hook(__FILE__, ['ITP_Invalid_Traffic_Presell', 'activate']);
register_deactivation_hook(__FILE__, ['ITP_Invalid_Traffic_Presell', 'deactivate']);
ITP_Invalid_Traffic_Presell::instance();
