AlkantarClanX12

Your IP : 216.73.217.24


Current Path : /www/capitalgmcbuickregina_830/public/wp-content/plugins/leadbox/includes/
Upload File :
Current File : /www/capitalgmcbuickregina_830/public/wp-content/plugins/leadbox/includes/lbx-slider-admin.php

<?php
/**
 * LBX Slider Admin Settings
 *
 * Adds a submenu page under "Leadbox Settings" to manage sliders.
 * Supports two data sources:
 * - OEM Offers (GraphQL/Minerva): Auto-generated slides from manufacturer offers
 * - Custom: Fully custom slides built in the admin (image, text, CTAs)
 */

if (!defined('ABSPATH')) {
    exit;
}

class LBX_Slider_Admin
{
    const OPTION_KEY = 'lbx_sliders';

    public function __construct()
    {
        add_action('admin_menu', [$this, 'add_admin_menu'], 99);
        add_action('admin_init', [$this, 'register_settings']);
        add_action('rest_api_init', [$this, 'register_rest_field']);
        add_action('wp_ajax_lbx_save_slider', [$this, 'ajax_save_slider']);
        add_action('wp_ajax_lbx_delete_slider', [$this, 'ajax_delete_slider']);
        add_action('wp_ajax_lbx_toggle_slider', [$this, 'ajax_toggle_slider']);
        add_action('wp_ajax_lbx_duplicate_slider', [$this, 'ajax_duplicate_slider']);
        // Live preview: stash the current (unsaved) form config to a transient, then
        // render it in an isolated frontend page loaded inside the admin modal's iframe.
        add_action('wp_ajax_lbx_slider_preview_stash', [$this, 'ajax_preview_stash']);
        add_action('wp_ajax_lbx_slider_refresh_minerva', [$this, 'ajax_refresh_minerva']);
        add_action('template_redirect', [$this, 'maybe_render_preview']);
        add_action('rest_api_init', [$this, 'register_rest_routes']);
        add_action('admin_enqueue_scripts', [$this, 'localize_editor_data']);
    }

    public function register_rest_field()
    {
        register_setting(self::OPTION_KEY . '-group', self::OPTION_KEY, [
            'show_in_rest' => [
                'schema' => [
                    'type' => 'object',
                    'additionalProperties' => [
                        'type' => 'object',
                        'properties' => [
                            'name'    => ['type' => 'string'],
                            'enabled' => ['type' => 'boolean'],
                            'source'  => ['type' => 'string'],
                        ],
                        'additionalProperties' => true,
                    ],
                ],
            ],
            'type' => 'object',
            'default' => [],
        ]);
    }

    /**
     * Register REST route for editor to fetch Minerva offers preview
     */
    public function register_rest_routes()
    {
        register_rest_route('lbx-slider/v1', '/minerva-preview', [
            'methods'  => 'GET',
            'callback' => [$this, 'rest_minerva_preview'],
            'permission_callback' => function () {
                return current_user_can('edit_posts');
            },
        ]);
        // Static Offers — image-only promo slides from Minerva's staticOffers endpoint.
        register_rest_route('lbx-slider/v1', '/static-offers-preview', [
            'methods'  => 'GET',
            'callback' => [$this, 'rest_static_offers_preview'],
            'permission_callback' => function () {
                return current_user_can('edit_posts');
            },
        ]);
        // Real-render preview URL for a saved slider — used by the Gutenberg editor iframe.
        register_rest_route('lbx-slider/v1', '/preview-url', [
            'methods'  => 'GET',
            'callback' => [$this, 'rest_preview_url'],
            'permission_callback' => function () {
                return current_user_can('manage_options');
            },
            'args' => ['slider_id' => ['type' => 'string', 'required' => true]],
        ]);
    }

    /**
     * REST callback: build the front-end preview URL for a saved slider so the editor
     * can render it in an iframe (real Blade → Vue → Swiper output). Nonce is generated
     * here in PHP; maybe_render_preview() validates it.
     */
    public function rest_preview_url($req)
    {
        $id = sanitize_text_field($req->get_param('slider_id'));
        $sliders = self::get_sliders();
        if ($id === '' || !isset($sliders[$id])) {
            return new WP_REST_Response(['url' => ''], 200);
        }
        $url = add_query_arg([
            'lbx_slider_preview' => '1',
            'slider_id'          => $id,
            '_wpnonce'           => wp_create_nonce('lbx_slider_preview_view'),
        ], home_url('/'));
        return new WP_REST_Response(['url' => $url], 200);
    }

    /**
     * REST callback: fetch offers from Minerva GraphQL and return for editor preview
     */
    public function rest_minerva_preview()
    {
        if (!function_exists('getGraphqlEndPoint') || !function_exists('get_leadbox_manufacturer')) {
            return new \WP_REST_Response(['offers' => []], 200);
        }

        $endpoint = getGraphqlEndPoint();
        $manufacturer = get_leadbox_manufacturer();
        $lbsettings = function_exists('get_leadbox_settings') ? get_leadbox_settings() : [];
        $all_brands = $lbsettings['all_brands'] ?? '';
        $province = '';
        if (function_exists('get_dealer_contact')) {
            $contact = get_dealer_contact();
            $province = $contact['address_province'] ?? '';
        }

        // Authenticate with Minerva
        $auth_token = $this->get_minerva_token();
        if (!$auth_token) {
            return new \WP_REST_Response(['offers' => [], 'error' => 'auth_failed'], 200);
        }

        // GraphQL query
        $query = '{
            offers(first: 1000) {
                nodes {
                    offers {
                        year make model trim contentAlignment
                        financeApr financeLabel financeLabelFr financeDisclaimer financeDisclaimerFr
                        leaseApr leaseLabel leaseLabelFr leaseDisclaimer leaseDisclaimerFr
                        payment paymentLabel paymentLabelFr paymentDisclaimer paymentDisclaimerFr
                        downPayment downPaymentLabel downPaymentLabelFr
                        normalOfferLine1 normalOfferLine2 normalOfferLine1Fr normalOfferLine2Fr
                        shopNowUrl shopNowUrlFr paymentTaxLabel paymentTaxLabelFr offerLogo { sourceUrl } offerLogoFr { sourceUrl }
                        legalEn legalFr
                        startDate endDate province
                        mainImage { sourceUrl }
                        mainImageMobile { sourceUrl }
                        desktopImage { sourceUrl }
                    }
                }
            }
        }';

        $response = wp_remote_post($endpoint, [
            'headers' => [
                'Content-Type'  => 'application/json',
                'Authorization' => 'Bearer ' . $auth_token,
                'Referer'       => home_url(),
            ],
            'body'    => wp_json_encode(['query' => $query]),
            'timeout' => 15,
        ]);

        if (is_wp_error($response)) {
            return new \WP_REST_Response(['offers' => [], 'error' => $response->get_error_message()], 200);
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);
        $nodes = $body['data']['offers']['nodes'] ?? [];

        // Filter by manufacturer + brands
        $makes = strtolower($manufacturer);
        if ($all_brands) {
            $makes .= ',' . strtolower($all_brands);
        }
        $makes_arr = array_map('trim', explode(',', $makes));

        $today = date('Y-m-d');
        $filtered = [];
        $seen_raw = [];

        foreach ($nodes as $node) {
            $o = $node['offers'] ?? [];
            if (empty($o['make'])) continue;

            // Filter by make
            if (!in_array(strtolower($o['make']), $makes_arr)) continue;

            // Filter by date
            if (!empty($o['startDate'])) {
                $parts = explode('/', $o['startDate']);
                if (count($parts) === 3) {
                    $sd = $parts[2] . '-' . $parts[1] . '-' . $parts[0];
                    if ($sd > $today) continue;
                }
            }
            if (!empty($o['endDate'])) {
                $parts = explode('/', $o['endDate']);
                if (count($parts) === 3) {
                    $ed = $parts[2] . '-' . $parts[1] . '-' . $parts[0];
                    if ($ed <= $today) continue;
                }
            }

            // Filter by province for Ford
            if (strtolower($manufacturer) === 'ford' && $province) {
                $offerProv = $o['province'] ?? [];
                if (!empty($offerProv)) {
                    $dp = strtoupper($province);
                    if ($dp === 'QC') {
                        if (!in_array('QC', $offerProv)) continue;
                    } else {
                        if (!in_array('NATIONAL', $offerProv)) continue;
                    }
                }
            }

            // Build simplified offer for editor
            $img = '';
            if (!empty($o['mainImage']['sourceUrl'])) {
                $img = $o['mainImage']['sourceUrl'];
            } elseif (!empty($o['desktopImage']['sourceUrl'])) {
                $img = $o['desktopImage']['sourceUrl'];
            }

            // Deduplicate before building: skip if year+make+model already seen
            $dkey = strtolower(($o['year'] ?? '') . '|' . ($o['make'] ?? '') . '|' . ($o['model'] ?? ''));
            if (isset($seen_raw[$dkey])) continue;
            $seen_raw[$dkey] = true;

            $finLabel = $o['financeLabel'] ?? '';
            $finApr = $o['financeApr'] ?? '';
            $finDisc = $o['financeDisclaimer'] ?? '';
            $leLabel = $o['leaseLabel'] ?? '';
            $leApr = $o['leaseApr'] ?? '';
            $leDisc = $o['leaseDisclaimer'] ?? '';
            $pay = $o['payment'] ?? '';
            $payLabel = $o['paymentLabel'] ?? '';

            $filtered[] = [
                'year'    => $o['year'] ?? '',
                'make'    => $o['make'] ?? '',
                'model'   => $o['model'] ?? '',
                'trim'    => $o['trim'] ?? '',
                // Minerva per-offer default content alignment (left/center/right).
                'contentAlignment' => $o['contentAlignment'] ?? '',
                'image'   => $img,
                // Pre-formatted summaries
                'finance' => $finLabel ? ($finLabel . ($finApr ? ' ' . $finApr . '%' : '') . ($finDisc ? ' ' . $finDisc : '')) : '',
                'lease'   => $leLabel ? ($leLabel . ($leApr ? ' ' . $leApr . '%' : '') . ($leDisc ? ' ' . $leDisc : '')) : '',
                'payment' => $pay ? ('$' . $pay . ($payLabel ? ' ' . $payLabel : '')) : '',
                'normal1' => $o['normalOfferLine1'] ?? '',
                'normal2' => $o['normalOfferLine2'] ?? '',
                // Raw individual fields for editing
                'financeApr'       => $finApr,
                'financeLabel'     => $finLabel,
                'financeLabelFr'   => $o['financeLabelFr'] ?? '',
                'leaseApr'         => $leApr,
                'leaseLabel'       => $leLabel,
                'leaseLabelFr'     => $o['leaseLabelFr'] ?? '',
                'paymentAmount'    => $pay,
                'paymentLabel'     => $payLabel,
                'paymentLabelFr'   => $o['paymentLabelFr'] ?? '',
                'downPayment'        => $o['downPayment'] ?? '',
                'downPaymentLabel'   => $o['downPaymentLabel'] ?? '',
                'downPaymentLabelFr' => $o['downPaymentLabelFr'] ?? '',
                'normal1Fr'        => $o['normalOfferLine1Fr'] ?? '',
                'normal2Fr'        => $o['normalOfferLine2Fr'] ?? '',
                // Legal disclaimers — shown via the disclaimer icon/popup in the slider UI.
                'legalEn'          => $o['legalEn'] ?? '',
                'legalFr'          => $o['legalFr'] ?? '',
                // Learn More / Shop Now destination URL (+ FR variant) — editable per slide.
                'shopNowUrl'       => $o['shopNowUrl'] ?? '',
                'shopNowUrlFr'     => $o['shopNowUrlFr'] ?? '',
            ];
        }

        // Sort by make asc, model asc, year desc
        usort($filtered, function ($a, $b) {
            $c = strcasecmp($a['make'], $b['make']);
            if ($c !== 0) return $c;
            $c = strcasecmp($a['model'], $b['model']);
            if ($c !== 0) return $c;
            return intval($b['year']) - intval($a['year']);
        });

        return new \WP_REST_Response(['offers' => $filtered], 200);
    }

    /**
     * Get cached Minerva offers for frontend pre-rendering (SSR).
     * Returns offers in the same nested format Vue expects: [{ offers: { year, make, ... } }, ...]
     * Cached in a transient for 2 hours to avoid hitting GraphQL on every page load.
     */
    public static function get_cached_minerva_offers($force_refresh = false)
    {
        $cache_key = 'lbx_slider_minerva_ssr';
        // $force_refresh bypasses the 2-hour cache and re-fetches from Minerva — used by the
        // editor preview so newly-added feed fields show up without waiting for expiry.
        if (!$force_refresh) {
            $cached = get_transient($cache_key);
            if ($cached !== false) {
                return $cached;
            }
        }

        // Requirements check
        if (!function_exists('getGraphqlEndPoint') || !function_exists('get_leadbox_manufacturer')) {
            return [];
        }

        $endpoint = getGraphqlEndPoint();
        $manufacturer = get_leadbox_manufacturer();
        $lbsettings = function_exists('get_leadbox_settings') ? get_leadbox_settings() : [];
        $all_brands = $lbsettings['all_brands'] ?? '';
        $province = '';
        if (function_exists('get_dealer_contact')) {
            $contact = get_dealer_contact();
            $province = $contact['address_province'] ?? '';
        }

        // Authenticate
        $instance = new self();
        $auth_token = $instance->get_minerva_token();
        if (!$auth_token) {
            // Cache empty for 5 min to avoid hammering on auth failure
            set_transient($cache_key, [], 5 * MINUTE_IN_SECONDS);
            return [];
        }

        $query = '{
            offers(first: 1000) {
                nodes {
                    offers {
                        year make model trim contentAlignment
                        financeApr financeLabel financeLabelFr financeDisclaimer financeDisclaimerFr
                        leaseApr leaseLabel leaseLabelFr leaseDisclaimer leaseDisclaimerFr
                        payment paymentLabel paymentLabelFr paymentDisclaimer paymentDisclaimerFr
                        downPayment downPaymentLabel downPaymentLabelFr
                        normalOfferLine1 normalOfferLine2 normalOfferLine1Fr normalOfferLine2Fr
                        shopNowUrl shopNowUrlFr paymentTaxLabel paymentTaxLabelFr offerLogo { sourceUrl } offerLogoFr { sourceUrl }
                        legalEn legalFr
                        startDate endDate province
                        mainImage { sourceUrl }
                        mainImageMobile { sourceUrl }
                        desktopImage { sourceUrl }
                    }
                }
            }
        }';

        $response = wp_remote_post($endpoint, [
            'headers' => [
                'Content-Type'  => 'application/json',
                'Authorization' => 'Bearer ' . $auth_token,
                'Referer'       => home_url(),
            ],
            'body'    => wp_json_encode(['query' => $query]),
            'timeout' => 15,
        ]);

        if (is_wp_error($response)) {
            set_transient($cache_key, [], 5 * MINUTE_IN_SECONDS);
            return [];
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);
        $nodes = $body['data']['offers']['nodes'] ?? [];

        // Filter by manufacturer + brands
        $makes = strtolower($manufacturer);
        if ($all_brands) {
            $makes .= ',' . strtolower($all_brands);
        }
        $makes_arr = array_map('trim', explode(',', $makes));
        $today = date('Y-m-d');
        $seen = [];
        $filtered = [];

        foreach ($nodes as $node) {
            $o = $node['offers'] ?? [];
            if (empty($o['make'])) continue;
            if (!in_array(strtolower($o['make']), $makes_arr)) continue;

            // Filter by date
            if (!empty($o['startDate'])) {
                $parts = explode('/', $o['startDate']);
                if (count($parts) === 3) {
                    $sd = $parts[2] . '-' . $parts[1] . '-' . $parts[0];
                    if ($sd > $today) continue;
                }
            }
            if (!empty($o['endDate'])) {
                $parts = explode('/', $o['endDate']);
                if (count($parts) === 3) {
                    $ed = $parts[2] . '-' . $parts[1] . '-' . $parts[0];
                    if ($ed <= $today) continue;
                }
            }

            // Province filter (Ford)
            if (strtolower($manufacturer) === 'ford' && $province) {
                $offerProv = $o['province'] ?? [];
                if (!empty($offerProv)) {
                    $dp = strtoupper($province);
                    if ($dp === 'QC') {
                        if (!in_array('QC', $offerProv)) continue;
                    } else {
                        if (!in_array('NATIONAL', $offerProv)) continue;
                    }
                }
            }

            // Deduplicate by year+make+model
            $dkey = strtolower(($o['year'] ?? '') . '|' . ($o['make'] ?? '') . '|' . ($o['model'] ?? ''));
            if (isset($seen[$dkey])) continue;
            $seen[$dkey] = true;

            // Keep in the format Vue expects: { offers: { ... } }
            $filtered[] = $node;
        }

        // Sort by make asc, model asc, year desc
        usort($filtered, function ($a, $b) {
            $c = strcasecmp($a['offers']['make'], $b['offers']['make']);
            if ($c !== 0) return $c;
            $c = strcasecmp($a['offers']['model'], $b['offers']['model']);
            if ($c !== 0) return $c;
            return intval($b['offers']['year']) - intval($a['offers']['year']);
        });

        // Cache for 2 hours
        set_transient($cache_key, $filtered, 2 * HOUR_IN_SECONDS);

        return $filtered;
    }

    /**
     * Get auth token for Minerva GraphQL
     */
    private function get_minerva_token()
    {
        // Use the same AJAX action the frontend uses
        $response = wp_remote_post(admin_url('admin-ajax.php'), [
            'body' => [
                'action' => 'get_leadbox_token_from_graphql',
            ],
            'cookies' => $_COOKIE,
            'timeout' => 10,
        ]);

        if (is_wp_error($response)) return null;

        $body = json_decode(wp_remote_retrieve_body($response), true);
        return $body['data'] ?? ($body['success'] ? ($body['data'] ?? null) : null);
    }

    /**
     * Fetch + filter Static Offers from Minerva's `staticOffers` endpoint.
     * Image-only promo slides (desktop/mobile image, optional link + endDate).
     * Filtering mirrors the OEM offers: by make (manufacturer + configured brands),
     * by province (Ford rule), and dropping anything past its endDate.
     *
     * Returns a flat, normalized array:
     *   [ ['id'=>databaseId,'title'=>..,'make'=>..,'image'=>..,'image_mobile'=>..,
     *      'alt'=>..,'link_url'=>..,'end_date'=>..], ... ]
     *
     * @return array
     */
    private static function fetch_static_offers()
    {
        if (!function_exists('getGraphqlEndPoint') || !function_exists('get_leadbox_manufacturer')) {
            return [];
        }

        $endpoint = getGraphqlEndPoint();
        $manufacturer = get_leadbox_manufacturer();
        $lbsettings = function_exists('get_leadbox_settings') ? get_leadbox_settings() : [];
        $all_brands = $lbsettings['all_brands'] ?? '';
        $province = '';
        if (function_exists('get_dealer_contact')) {
            $contact = get_dealer_contact();
            $province = $contact['address_province'] ?? '';
        }

        $instance = new self();
        $auth_token = $instance->get_minerva_token();
        if (!$auth_token) {
            return [];
        }

        $query = '{
            staticOffers(first: 100) {
                nodes {
                    databaseId
                    title
                    static_offers {
                        make
                        province
                        endDate
                        linkUrl
                        disclaimer
                        desktopImage { sourceUrl altText }
                        mobileImage  { sourceUrl altText }
                    }
                }
            }
        }';

        $response = wp_remote_post($endpoint, [
            'headers' => [
                'Content-Type'  => 'application/json',
                'Authorization' => 'Bearer ' . $auth_token,
                'Referer'       => home_url(),
            ],
            'body'    => wp_json_encode(['query' => $query]),
            'timeout' => 15,
        ]);

        if (is_wp_error($response)) {
            return [];
        }

        $body  = json_decode(wp_remote_retrieve_body($response), true);
        $nodes = $body['data']['staticOffers']['nodes'] ?? [];

        // Make filter set (manufacturer + other brands), same as OEM offers.
        $makes = strtolower($manufacturer);
        if ($all_brands) {
            $makes .= ',' . strtolower($all_brands);
        }
        $makes_arr = array_filter(array_map('trim', explode(',', $makes)));

        $today = date('Y-m-d');
        $filtered = [];

        foreach ($nodes as $node) {
            $so = $node['static_offers'] ?? [];
            if (empty($so)) continue;

            // Make filter. Static offers expose `make` as an ARRAY (e.g. ["Ford"]) — unlike
            // OEM offers where it's a string. Normalize to a list and keep the offer when any
            // of its makes matches the dealer's set (offers with no make are dealer-wide → kept).
            $make_raw  = $so['make'] ?? '';
            $make_list = is_array($make_raw) ? $make_raw : ($make_raw !== '' ? [$make_raw] : []);
            if (!empty($make_list) && !empty($makes_arr)) {
                $make_match = false;
                foreach ($make_list as $m) {
                    if (in_array(strtolower(trim((string) $m)), $makes_arr, true)) { $make_match = true; break; }
                }
                if (!$make_match) continue;
            }

            // endDate filter — drop expired. Minerva sends DD/MM/YYYY; tolerate ISO too.
            $end_raw = $so['endDate'] ?? '';
            if (!empty($end_raw)) {
                $end_ymd = self::normalize_minerva_date($end_raw);
                if ($end_ymd && $end_ymd < $today) continue;
            }

            // Province filter (Ford rule). `province` may be a string ("NATIONAL") or an array.
            if (strtolower($manufacturer) === 'ford' && $province) {
                $offerProv = $so['province'] ?? [];
                if (!is_array($offerProv)) $offerProv = ($offerProv !== '' ? [$offerProv] : []);
                $offerProv = array_map('strtoupper', array_map('strval', $offerProv));
                if (!empty($offerProv)) {
                    $dp = strtoupper($province);
                    if ($dp === 'QC') {
                        if (!in_array('QC', $offerProv, true)) continue;
                    } else {
                        if (!in_array('NATIONAL', $offerProv, true)) continue;
                    }
                }
            }

            // Images — null-safe (mobileImage can be null; desktopImage too defensively).
            $desktop = (isset($so['desktopImage']) && is_array($so['desktopImage'])) ? ($so['desktopImage']['sourceUrl'] ?? '') : '';
            $mobile  = (isset($so['mobileImage'])  && is_array($so['mobileImage']))  ? ($so['mobileImage']['sourceUrl']  ?? '') : '';
            if (!$desktop && !$mobile) continue; // image-only slides need an image

            $filtered[] = [
                'id'           => (string) ($node['databaseId'] ?? ''),
                'title'        => $node['title'] ?? '',
                'make'         => !empty($make_list) ? (string) $make_list[0] : '',
                // Full make list — static offers can target several makes; the per-slider
                // make filter must match against ALL of them, not just the first.
                'makes'        => array_values(array_map('strval', $make_list)),
                'image'        => $desktop ?: $mobile,
                'image_mobile' => $mobile,
                'alt'          => (isset($so['desktopImage']['altText']) && $so['desktopImage']['altText'] !== '') ? $so['desktopImage']['altText'] : ($node['title'] ?? ''),
                'link_url'     => $so['linkUrl'] ?? '',
                'disclaimer'   => $so['disclaimer'] ?? '',
                'end_date'     => $end_raw,
            ];
        }

        return $filtered;
    }

    /**
     * Normalize a Minerva date string (DD/MM/YYYY or ISO) to Y-m-d, or '' on failure.
     */
    private static function normalize_minerva_date($raw)
    {
        $raw = trim((string) $raw);
        if ($raw === '') return '';
        if (strpos($raw, '/') !== false) {
            $parts = explode('/', $raw);
            if (count($parts) === 3) {
                return $parts[2] . '-' . str_pad($parts[1], 2, '0', STR_PAD_LEFT) . '-' . str_pad($parts[0], 2, '0', STR_PAD_LEFT);
            }
        }
        $ts = strtotime($raw);
        return $ts ? date('Y-m-d', $ts) : '';
    }

    /**
     * Cached Static Offers for frontend SSR. 2-hour transient, same as OEM offers.
     */
    public static function get_cached_static_offers()
    {
        // v2: entries now carry the full `makes` list (make-filter support). The key bump
        // guarantees fresh-shaped data on deploy; the old key just expires on its own.
        $cache_key = 'lbx_slider_static_ssr_v2';
        $cached = get_transient($cache_key);
        if ($cached !== false) {
            return $cached;
        }
        $offers = self::fetch_static_offers();
        set_transient($cache_key, $offers, 2 * HOUR_IN_SECONDS);
        return $offers;
    }

    /**
     * REST: live Static Offers for the editor's "Add Static OEM Offers" button.
     * No transient here — the editor wants the freshest list when the user clicks.
     */
    public function rest_static_offers_preview()
    {
        return new \WP_REST_Response(['offers' => self::fetch_static_offers()], 200);
    }

    /**
     * Localize minerva config for the block editor
     */
    public function localize_editor_data()
    {
        $screen = get_current_screen();
        if (!$screen || $screen->base !== 'post') return;

        wp_localize_script('wp-block-editor', 'lbxSliderEditor', [
            'restUrl'   => rest_url('lbx-slider/v1/minerva-preview'),
            'nonce'     => wp_create_nonce('wp_rest'),
            'ajaxNonce' => wp_create_nonce('lbx_slider_nonce'),
        ]);
    }

    public function add_admin_menu()
    {
        global $menu;

        // Find Leadbox Settings position and place right after it
        $position = 58;
        if (is_array($menu)) {
            foreach ($menu as $pos => $item) {
                if (isset($item[2]) && $item[2] === 'leadbox-settings') {
                    // Find next free slot right after Leadbox Settings
                    $position = $pos + 0.1;
                    break;
                }
            }
        }

        add_menu_page(
            __('LBX Slider', 'leadbox'),
            __('LBX Slider', 'leadbox'),
            'manage_options',
            'lbx-slider',
            [$this, 'render_admin_page'],
            'dashicons-slides',
            $position
        );
    }

    public function register_settings()
    {
        register_setting(self::OPTION_KEY . '-group', self::OPTION_KEY);
    }

    public static function get_sliders()
    {
        return get_option(self::OPTION_KEY, []);
    }

    public static function get_slider($slider_id)
    {
        $sliders = self::get_sliders();
        return $sliders[$slider_id] ?? null;
    }

    /**
     * LBT-1472: whether a slider auto-populates the dealer's per-model OEM offers.
     * Migration: honors the new flag if present; falls back to the single `auto_populate`
     * field from the first iteration; otherwise legacy minerva sliders default ON.
     */
    public static function is_auto_populate_oem($slider)
    {
        if (!is_array($slider)) return false;
        if (array_key_exists('auto_populate_oem', $slider)) return (bool) $slider['auto_populate_oem'];
        if (array_key_exists('auto_populate', $slider))     return (bool) $slider['auto_populate'];
        return ($slider['source'] ?? '') === 'minerva';
    }

    /**
     * LBT-1472: whether a slider auto-populates the dealer's Static offers.
     * Migration: new flag wins; else the single `auto_populate` field; else OFF
     * (Static offers are a new source — legacy sliders never had them).
     */
    public static function is_auto_populate_static($slider)
    {
        if (!is_array($slider)) return false;
        if (array_key_exists('auto_populate_static', $slider)) return (bool) $slider['auto_populate_static'];
        if (array_key_exists('auto_populate', $slider))        return (bool) $slider['auto_populate'];
        return false;
    }

    /**
     * Multi-make dealers: the slider's "Show Only {Make} Slides" filter.
     * Returns the make normalized to lowercase, or '' when the slider shows all
     * makes (the default). Applies to auto-populated OEM + Static offers only —
     * manual (Additional) slides are never filtered.
     */
    public static function get_make_filter($slider)
    {
        if (!is_array($slider)) return '';
        return strtolower(trim((string) ($slider['make_filter'] ?? '')));
    }

    public static function get_enabled_sliders()
    {
        return array_filter(self::get_sliders(), function ($s) {
            return !empty($s['enabled']);
        });
    }

    /**
     * Sanitize per-slide settings for OEM (Minerva) sliders.
     *
     * Override fields that come from the content server (title, description,
     * APR/payment values, finance/lease/payment labels, normal offer lines)
     * are allowed to contain HTML so admins can author rich formatting
     * (e.g. <sup>*</sup>, <strong>FROM</strong>, inline color spans).
     *
     * `wp_kses_post` filters dangerous markup (script/iframe/event handlers,
     * unsafe CSS) while preserving the post-content tag whitelist. Non-listed
     * fields are passed through untouched — visibility flags, alignment, image
     * URLs, embed code, etc., have their own sanitization at the field level.
     */
    private static function sanitize_slide_settings($settings)
    {
        if (!is_array($settings)) {
            return [];
        }

        // Override fields where HTML is welcome. Keep this list in sync with
        // the OEM edit modal in `render_admin_page()` and the v-html bindings
        // in `lbx-slider.blade.php`.
        $html_fields = [
            'title', 'title_fr',
            'description', 'description_fr',
            'financeLabel', 'financeLabelFr',
            'leaseLabel', 'leaseLabelFr',
            'paymentLabel', 'paymentLabelFr',
            'financeApr', 'leaseApr',
            'paymentAmount',
            'downPayment', 'downPaymentLabel', 'downPaymentLabelFr',
            'normal1', 'normal2',
            'normal1Fr', 'normal2Fr',
            // Disclaimer overrides for the per-slide info popup (LBT-1460).
            'disclaimer_en', 'disclaimer_fr',
        ];

        $clean = [];
        foreach ($settings as $slide_key => $slide_data) {
            if (!is_array($slide_data)) {
                $clean[$slide_key] = $slide_data;
                continue;
            }
            $row = $slide_data;
            foreach ($html_fields as $field) {
                if (isset($row[$field]) && is_string($row[$field])) {
                    $row[$field] = wp_kses_post($row[$field]);
                }
            }
            // URL override fields — sanitized as URLs (esc_url_raw, NOT wp_kses_post) so
            // query strings stay intact and unsafe protocols (javascript:) are stripped.
            foreach (['shopNowUrl', 'shopNowUrlFr'] as $url_field) {
                if (isset($row[$url_field]) && is_string($row[$url_field]) && $row[$url_field] !== '') {
                    $row[$url_field] = esc_url_raw($row[$url_field]);
                }
            }
            // Per-slide responsive visibility — breakpoints where this slide is hidden.
            if (isset($row['hidden_on'])) {
                $row['hidden_on'] = self::sanitize_hidden_on($row['hidden_on']);
            }
            // Per-slide Animated Frame Line — full settings object, or dropped when off.
            if (isset($row['frame_line'])) {
                $fl_clean = self::sanitize_frame_line($row['frame_line']);
                if ($fl_clean) { $row['frame_line'] = $fl_clean; }
                else { unset($row['frame_line']); }
            }
            $clean[$slide_key] = $row;
        }
        return $clean;
    }

    /**
     * Sanitize a per-slide Animated Frame Line config. The feature is PER SLIDE:
     * each slide carries its own full settings object (speed/length/width/color/
     * glow/outline). Returns the clean array, or null when the slide doesn't
     * enable the line (so disabled slides store nothing).
     */
    private static function sanitize_frame_line($fl)
    {
        if (!is_array($fl) || empty($fl['enabled'])) {
            return null;
        }
        return [
            'enabled' => true,
            // 'travel' = dash loops around forever; 'draw' = the line draws itself
            // in once (top center → down both sides → hugging the logo) and stays.
            'style'   => in_array($fl['style'] ?? 'travel', ['travel', 'draw'], true) ? $fl['style'] : 'travel',
            'speed'   => max(3, min(30, (float) ($fl['speed'] ?? 9))),
            'length'  => max(4, min(45, absint($fl['length'] ?? 18))),
            'width'   => max(1, min(10, (float) ($fl['width'] ?? 4))),
            'color'   => sanitize_hex_color($fl['color'] ?? '') ?: '#ffffff',
            'glow'    => !empty($fl['glow']),
            'outline' => !empty($fl['outline']),
        ];
    }

    /**
     * Sanitize a per-slide responsive-visibility list to known breakpoint buckets.
     * Returns a de-duplicated array containing only 'mobile' | 'tablet' | 'desktop'
     * (the buckets where the slide should be HIDDEN). Empty = visible everywhere.
     */
    private static function sanitize_hidden_on($val)
    {
        $allowed = ['mobile', 'tablet', 'desktop'];
        if (!is_array($val)) {
            return [];
        }
        $out = [];
        foreach ($val as $b) {
            $b = is_string($b) ? strtolower(trim($b)) : '';
            if (in_array($b, $allowed, true) && !in_array($b, $out, true)) {
                $out[] = $b;
            }
        }
        return $out;
    }


    public function ajax_save_slider()
    {
        check_ajax_referer('lbx_slider_nonce', 'nonce');
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Unauthorized');
        }

        // Partial update (used by Gutenberg editor)
        if (!empty($_POST['_partial'])) {
            $slider_id = sanitize_text_field($_POST['slider_id'] ?? '');
            $sliders = self::get_sliders();
            if (!isset($sliders[$slider_id])) {
                wp_send_json_error(__('Invalid slider.', 'leadbox'));
            }

            $partial = sanitize_text_field($_POST['_partial']);

            if ($partial === 'slide_settings' && !empty($_POST['slide_settings'])) {
                $raw_ss = json_decode(stripslashes($_POST['slide_settings']), true);
                $sliders[$slider_id]['slide_settings'] = is_array($raw_ss) ? self::sanitize_slide_settings($raw_ss) : [];
            }

            if ($partial === 'custom_slides' && !empty($_POST['custom_slides'])) {
                $raw = json_decode(stripslashes($_POST['custom_slides']), true);
                $sliders[$slider_id]['custom_slides'] = is_array($raw) ? $raw : [];
            }

            if ($partial === 'slider_field' && !empty($_POST['field_name'])) {
                $field = sanitize_text_field($_POST['field_name']);
                $value = sanitize_text_field($_POST['field_value'] ?? '');
                $sliders[$slider_id][$field] = $value;
            }

            update_option(self::OPTION_KEY, $sliders);
            wp_send_json_success(['message' => __('Updated.', 'leadbox')]);
        }

        $sliders = self::get_sliders();
        $slider_id = sanitize_text_field($_POST['slider_id'] ?? '');
        $is_new = empty($slider_id) || $slider_id === 'new';
        if ($is_new) {
            $slider_id = 'slider_' . uniqid();
        }

        $data = self::build_slider_data_from_post($slider_id, $is_new, $sliders);

        $sliders[$slider_id] = $data;
        update_option(self::OPTION_KEY, $sliders);

        // Invalidate SSR cache so changes appear immediately
        delete_transient('lbx_slider_minerva_ssr'); delete_transient('lbx_slider_static_ssr_v2');

        wp_send_json_success([
            'slider_id' => $slider_id,
            'message' => $is_new ? __('Slider created.', 'leadbox') : __('Slider updated.', 'leadbox'),
        ]);
    }

    /**
     * Build the fully-sanitized slider config array from the current $_POST.
     * Extracted so ajax_save_slider (persists to the option) and ajax_preview_stash
     * (stashes to a transient for the live preview) share the exact same sanitization —
     * the preview can never drift from what actually gets saved.
     *
     * @param string $slider_id     Target slider id (real or preview placeholder).
     * @param bool   $is_new        Whether this is a brand-new slider.
     * @param array  $sliders       All existing sliders (for preserving slide_settings on parse failure).
     * @return array Sanitized slider config.
     */
    private static function build_slider_data_from_post($slider_id, $is_new, $sliders)
    {
        $source = sanitize_text_field($_POST['slider_source'] ?? 'minerva');

        $data = [
            'name'       => sanitize_text_field($_POST['slider_name'] ?? __('Untitled Slider', 'leadbox')),
            'enabled'    => !empty($_POST['slider_enabled']),
            'source'     => $source,
            'updated_at' => current_time('mysql'),
            // LBT-1472: two independent auto-populate switches (Slides mode). Each pulls the
            // dealer's offers of that kind (filtered by make/province); off = nothing injected.
            // They coexist freely with each other and with manual (Additional) slides.
            'auto_populate_oem'    => !empty($_POST['auto_populate_oem']),
            'auto_populate_static' => !empty($_POST['auto_populate_static']),
            // Multi-make dealers: show only slides of this make ('' = all makes, the
            // default). Stored lowercase so Blade/Vue compare without re-normalizing.
            'make_filter'          => strtolower(sanitize_text_field($_POST['make_filter'] ?? '')),
            // Scheduling
            'start_date' => sanitize_text_field($_POST['slider_start_date'] ?? ''),
            'end_date'   => sanitize_text_field($_POST['slider_end_date'] ?? ''),
            // Layout & Design
            'layout'           => sanitize_text_field($_POST['slider_layout'] ?? 'carousel'),
            'transition'       => sanitize_text_field($_POST['slider_transition'] ?? 'slide'),
            'content_align'    => sanitize_text_field($_POST['slider_content_align'] ?? 'left'),
            'slider_height'    => sanitize_text_field($_POST['slider_height'] ?? ''),
            // Slider-wide image fit (forces every slide when set; '' = per-slide).
            'image_fit'        => (function () {
                $v = sanitize_text_field($_POST['slider_image_fit'] ?? '');
                return in_array($v, ['cover', 'contain', 'fill', 'scale-down'], true) ? $v : '';
            })(),
            // OEM Offer Hero — intro animation for the offer text (whitelisted, CSS-safe).
            'hero_intro_animation' => (function () {
                $v = sanitize_text_field($_POST['slider_hero_intro_animation'] ?? '');
                return in_array($v, ['none', 'fade-in', 'fade-up', 'fade-down', 'slide-in', 'zoom-in', 'blur-in'], true) ? $v : 'none';
            })(),
            'show_view_inventory' => !empty($_POST['show_view_inventory']),
            'gradient_color'   => sanitize_hex_color($_POST['gradient_color'] ?? '') ?: '',
            // Slide behavior
            'autoplay'        => !empty($_POST['slider_autoplay']),
            'autoplay_speed'  => max(500, min(20000, absint($_POST['slider_autoplay_speed'] ?? 5000))),
            'loop'            => !empty($_POST['slider_loop']),
            // Navigation (arrows + pagination)
            'show_arrows'        => isset($_POST['slider_show_arrows']) ? !empty($_POST['slider_show_arrows']) : true,
            'arrow_style'        => sanitize_text_field($_POST['slider_arrow_style'] ?? 'circle'),
            'arrow_color'        => sanitize_hex_color($_POST['slider_arrow_color'] ?? '') ?: '',
            'arrow_opacity'      => max(10, min(100, absint($_POST['slider_arrow_opacity'] ?? 90))),
            'arrow_visibility'   => sanitize_text_field($_POST['slider_arrow_visibility'] ?? 'always'),
            'show_pagination'    => isset($_POST['slider_show_pagination']) ? !empty($_POST['slider_show_pagination']) : true,
            'pagination_color'   => sanitize_hex_color($_POST['slider_pagination_color'] ?? '') ?: '',
            'show_overlay'       => isset($_POST['slider_show_overlay']) ? !empty($_POST['slider_show_overlay']) : true,
            // Custom CSS — admins are trusted; raw CSS allowed (browser will validate)
            'custom_css'         => isset($_POST['slider_custom_css']) ? wp_unslash($_POST['slider_custom_css']) : '',
            // Watermark
            // Video playback
            'video_autoplay'     => !empty($_POST['video_autoplay']),
            'video_hover_play'   => !empty($_POST['video_hover_play']),
            'video_loop'         => !empty($_POST['video_loop']),
            'watermark_image'    => esc_url_raw($_POST['watermark_image'] ?? ''),
            // Whitelisted — the value becomes a frontend CSS class (same rationale as
            // disclaimer_position). Center positions added alongside the 4 corners.
            'watermark_position' => (function () {
                $allowed = ['top-left', 'top-center', 'top-right', 'bottom-left', 'bottom-center', 'bottom-right'];
                $v = sanitize_text_field($_POST['watermark_position'] ?? 'bottom-right');
                return in_array($v, $allowed, true) ? $v : 'bottom-right';
            })(),
            'watermark_size'     => absint($_POST['watermark_size'] ?? 50),
            // Disclaimer icon — whitelist position values to avoid leaking arbitrary CSS classes downstream.
            'disclaimer_position' => (function () {
                $allowed = ['hidden', 'top-left', 'top-right', 'bottom-left', 'bottom-right'];
                $v = sanitize_text_field($_POST['disclaimer_position'] ?? 'hidden');
                return in_array($v, $allowed, true) ? $v : 'hidden';
            })(),
            'media_columns_mobile' => max(1, min(3, absint($_POST['media_columns_mobile'] ?? 1))),
            'media_columns_tablet' => max(1, min(4, absint($_POST['media_columns_tablet'] ?? 2))),
            'media_columns_desktop' => max(1, min(6, absint($_POST['media_columns_desktop'] ?? 3))),
            'carousel_columns_mobile'  => max(1, min(3, absint($_POST['carousel_columns_mobile'] ?? 1))),
            'carousel_columns_tablet'  => max(1, min(4, absint($_POST['carousel_columns_tablet'] ?? 2))),
            'carousel_columns_desktop' => max(1, min(6, absint($_POST['carousel_columns_desktop'] ?? 3))),
            // Configurable breakpoints (px). Clamp to sane ranges and keep mobile < tablet.
            'breakpoint_mobile' => (function () {
                $m = max(200, min(2000, absint($_POST['breakpoint_mobile'] ?? 640) ?: 640));
                return $m;
            })(),
            'breakpoint_tablet' => (function () {
                $m = max(200, min(2000, absint($_POST['breakpoint_mobile'] ?? 640) ?: 640));
                $t = max(201, min(3000, absint($_POST['breakpoint_tablet'] ?? 1024) ?: 1024));
                return $t > $m ? $t : ($m + 1); // tablet must exceed mobile
            })(),
            'hide_offer_data'          => !empty($_POST['hide_offer_data']),
            'boxed_offer_text'         => !empty($_POST['boxed_offer_text']),
        ];

        if ($source === 'custom') {
            $custom_slides = [];
            if (!empty($_POST['custom_slides'])) {
                $raw = json_decode(stripslashes($_POST['custom_slides']), true);
                if (is_array($raw)) {
                    foreach ($raw as $slide) {
                        // Build ctas array — migrate from main_cta/secondary_cta if ctas not present
                        $raw_ctas = is_array($slide['ctas'] ?? null) ? $slide['ctas'] : [];
                        if (empty($raw_ctas)) {
                            if (!empty($slide['main_cta']['label'])) $raw_ctas[] = array_merge((array)$slide['main_cta'], ['position' => 'inline']);
                            if (!empty($slide['secondary_cta']['label'])) $raw_ctas[] = array_merge((array)$slide['secondary_cta'], ['position' => 'inline']);
                        }
                        $sanitized_ctas = [];
                        foreach ($raw_ctas as $cta) {
                            if (empty($cta['label']) && empty($cta['url'])) continue;
                            $sanitized_ctas[] = [
                                'label'      => sanitize_text_field($cta['label'] ?? ''),
                                'url'        => esc_url_raw($cta['url'] ?? ''),
                                'target'     => sanitize_text_field($cta['target'] ?? '_self'),
                                'color'      => sanitize_hex_color($cta['color'] ?? ''),
                                'text_color' => sanitize_hex_color($cta['text_color'] ?? ''),
                                'position'   => sanitize_text_field($cta['position'] ?? 'inline'),
                            ];
                        }
                        $custom_slides[] = [
                            'image_id'  => absint($slide['image_id'] ?? 0),
                            'image_url' => esc_url_raw($slide['image_url'] ?? ''),
                            'image_id_mobile'  => absint($slide['image_id_mobile'] ?? 0),
                            'image_url_mobile' => esc_url_raw($slide['image_url_mobile'] ?? ''),
                            'media_type' => sanitize_text_field($slide['media_type'] ?? 'image'),
                            'video_id'  => absint($slide['video_id'] ?? 0),
                            'video_url' => esc_url_raw($slide['video_url'] ?? ''),
                            'align'     => sanitize_text_field($slide['align'] ?? ''),
                            // Safe HTML allowed (parity with extra slides' titles).
                            'title'     => wp_kses_post($slide['title'] ?? ''),
                            'title_color' => sanitize_hex_color($slide['title_color'] ?? ''),
                            'title_bold'  => !empty($slide['title_bold']),
                            'title_italic' => !empty($slide['title_italic']),
                            'desc_color'  => sanitize_hex_color($slide['desc_color'] ?? ''),
                            'description' => wp_kses_post($slide['description'] ?? ''),
                            'ctas'        => $sanitized_ctas,
                            'image_size'        => sanitize_text_field($slide['image_size'] ?? 'full'),
                            'image_fit'         => sanitize_text_field($slide['image_fit'] ?? 'cover'),
                            'image_link_url'    => esc_url_raw($slide['image_link_url'] ?? ''),
                            'image_link_target' => sanitize_text_field($slide['image_link_target'] ?? '_self'),
                            'hide_watermark'    => !empty($slide['hide_watermark']),
                            'start_date'        => sanitize_text_field($slide['start_date'] ?? ''),
                            'end_date'          => sanitize_text_field($slide['end_date'] ?? ''),
                            // Embed: raw HTML (admins trusted — see "Permitir HTML completo" decision)
                            'embed_code'        => isset($slide['embed_code']) ? wp_unslash((string) $slide['embed_code']) : '',
                        ];
                    }
                }
            }
            $data['custom_slides'] = $custom_slides;
        }

        if ($source === 'minerva') {
            // Per-slide settings (visibility, alignment, media overrides, CTAs).
            // IMPORTANT: Preserve existing slide_settings on parse failure — a silent reset would
            // wipe overrides the user already saved before.
            $existing_ss = (!$is_new && isset($sliders[$slider_id]['slide_settings']) && is_array($sliders[$slider_id]['slide_settings']))
                ? $sliders[$slider_id]['slide_settings']
                : [];
            $raw_post = isset($_POST['slide_settings']) ? (string) $_POST['slide_settings'] : '';
            if ($raw_post !== '' && $raw_post !== '[]') {
                $raw_ss = json_decode(stripslashes($raw_post), true);
                $data['slide_settings'] = is_array($raw_ss) ? self::sanitize_slide_settings($raw_ss) : $existing_ss;
            } else {
                // Frontend sent "[]" or nothing — preserve what was saved before to avoid wiping.
                $data['slide_settings'] = $existing_ss;
            }
            $extra_slides = [];
            if (!empty($_POST['extra_slides'])) {
                $raw = json_decode(stripslashes($_POST['extra_slides']), true);
                if (is_array($raw)) {
                    foreach ($raw as $slide) {
                        // Build ctas array — migrate from main_cta/secondary_cta if ctas not present
                        $raw_ctas_ex = is_array($slide['ctas'] ?? null) ? $slide['ctas'] : [];
                        if (empty($raw_ctas_ex)) {
                            if (!empty($slide['main_cta']['label'])) $raw_ctas_ex[] = array_merge((array)$slide['main_cta'], ['position' => 'inline']);
                            if (!empty($slide['secondary_cta']['label'])) $raw_ctas_ex[] = array_merge((array)$slide['secondary_cta'], ['position' => 'inline']);
                        }
                        $sanitized_ctas_ex = [];
                        foreach ($raw_ctas_ex as $cta) {
                            if (empty($cta['label']) && empty($cta['url'])) continue;
                            $sanitized_ctas_ex[] = [
                                'label'      => sanitize_text_field($cta['label'] ?? ''),
                                'url'        => esc_url_raw($cta['url'] ?? ''),
                                'target'     => sanitize_text_field($cta['target'] ?? '_self'),
                                'color'      => sanitize_hex_color($cta['color'] ?? ''),
                                'text_color' => sanitize_hex_color($cta['text_color'] ?? ''),
                                'position'   => sanitize_text_field($cta['position'] ?? 'inline'),
                            ];
                        }
                        // French CTAs (LBT-1489) — same shape/sanitization as the EN set.
                        $raw_ctas_ex_fr = is_array($slide['ctas_fr'] ?? null) ? $slide['ctas_fr'] : [];
                        $sanitized_ctas_ex_fr = [];
                        foreach ($raw_ctas_ex_fr as $cta) {
                            if (empty($cta['label']) && empty($cta['url'])) continue;
                            $sanitized_ctas_ex_fr[] = [
                                'label'      => sanitize_text_field($cta['label'] ?? ''),
                                'url'        => esc_url_raw($cta['url'] ?? ''),
                                'target'     => sanitize_text_field($cta['target'] ?? '_self'),
                                'color'      => sanitize_hex_color($cta['color'] ?? ''),
                                'text_color' => sanitize_hex_color($cta['text_color'] ?? ''),
                                'position'   => sanitize_text_field($cta['position'] ?? 'inline'),
                            ];
                        }
                        $extra_id = !empty($slide['id']) ? sanitize_text_field($slide['id']) : uniqid('ex_', false);
                        $extra_slides[] = [
                            'id'        => $extra_id,
                            'image_id'  => absint($slide['image_id'] ?? 0),
                            'image_url' => esc_url_raw($slide['image_url'] ?? ''),
                            'image_id_fr'  => absint($slide['image_id_fr'] ?? 0),
                            'image_url_fr' => esc_url_raw($slide['image_url_fr'] ?? ''),
                            'image_id_mobile'  => absint($slide['image_id_mobile'] ?? 0),
                            'image_url_mobile' => esc_url_raw($slide['image_url_mobile'] ?? ''),
                            'image_id_mobile_fr'  => absint($slide['image_id_mobile_fr'] ?? 0),
                            'image_url_mobile_fr' => esc_url_raw($slide['image_url_mobile_fr'] ?? ''),
                            'media_type' => sanitize_text_field($slide['media_type'] ?? 'image'),
                            'media_type_fr' => sanitize_text_field($slide['media_type_fr'] ?? ''),
                            'video_id'  => absint($slide['video_id'] ?? 0),
                            'video_url' => esc_url_raw($slide['video_url'] ?? ''),
                            'video_url_fr' => esc_url_raw($slide['video_url_fr'] ?? ''),
                            // Legacy: kept so older Blade fallback path still works for sliders
                            // saved with the unified UI but read on a deployment where the
                            // theme hasn't shipped the unified Blade/Vue updates yet.
                            'position'  => absint($slide['position'] ?? 1),
                            // Titles allow safe HTML (spans/colors/<br>) just like descriptions —
                            // wp_kses_post strips scripts/event handlers, keeps formatting.
                            'title'     => wp_kses_post($slide['title'] ?? ''),
                            'title_fr'  => wp_kses_post($slide['title_fr'] ?? ''),
                            'description'    => wp_kses_post($slide['description'] ?? ''),
                            'description_fr' => wp_kses_post($slide['description_fr'] ?? ''),
                            'ctas'        => $sanitized_ctas_ex,
                            'ctas_fr'     => $sanitized_ctas_ex_fr,
                            'image_size'        => sanitize_text_field($slide['image_size'] ?? 'full'),
                            'image_size_fr'     => sanitize_text_field($slide['image_size_fr'] ?? ''),
                            'image_fit'         => sanitize_text_field($slide['image_fit'] ?? 'cover'),
                            'image_fit_fr'      => sanitize_text_field($slide['image_fit_fr'] ?? ''),
                            'image_link_url'    => esc_url_raw($slide['image_link_url'] ?? ''),
                            'image_link_url_fr' => esc_url_raw($slide['image_link_url_fr'] ?? ''),
                            'image_link_target' => sanitize_text_field($slide['image_link_target'] ?? '_self'),
                            'image_link_target_fr' => sanitize_text_field($slide['image_link_target_fr'] ?? '_self'),
                            'hide_watermark'    => !empty($slide['hide_watermark']),
                            'hide_watermark_fr' => !empty($slide['hide_watermark_fr']),
                            'start_date'        => sanitize_text_field($slide['start_date'] ?? ''),
                            'end_date'          => sanitize_text_field($slide['end_date'] ?? ''),
                            'embed_code'        => isset($slide['embed_code']) ? wp_unslash((string) $slide['embed_code']) : '',
                            'embed_code_fr'     => isset($slide['embed_code_fr']) ? wp_unslash((string) $slide['embed_code_fr']) : '',
                            // Disclaimer popup text (LBT-1460). wp_kses_post allows safe HTML so
                            // operators can paste rich legal text (links, line breaks, etc.).
                            'disclaimer_en'     => wp_kses_post($slide['disclaimer_en'] ?? ''),
                            'disclaimer_fr'     => wp_kses_post($slide['disclaimer_fr'] ?? ''),
                            // Per-slide kill switch for the disclaimer icon (LBT-1460).
                            'hide_disclaimer'   => !empty($slide['hide_disclaimer']),
                            // Per-slide responsive visibility — breakpoints where this slide is hidden.
                            'hidden_on'         => self::sanitize_hidden_on($slide['hidden_on'] ?? []),
                            // Per-slide Animated Frame Line — full settings object (null when off).
                            'frame_line'        => self::sanitize_frame_line($slide['frame_line'] ?? null),
                            // Title/description/CTAs block position ('' = layout default).
                            'content_position'  => in_array($slide['content_position'] ?? '', ['top-left', 'top-center', 'top-right', 'bottom-left', 'bottom-center', 'bottom-right'], true) ? $slide['content_position'] : '',
                        ];
                    }
                }
            }
            $data['extra_slides'] = $extra_slides;

            // Process unified_slides (authoritative ordering for OEM + Additional slides).
            // Each entry: { type: 'minerva', key: '<year_make_model>' } | { type: 'extra', id: 'ex_...' }
            // We sanitize then derive minerva_order from it for legacy back-compat.
            $unified_slides = [];
            if (!empty($_POST['unified_slides'])) {
                $raw_unified = json_decode(stripslashes($_POST['unified_slides']), true);
                if (is_array($raw_unified)) {
                    // Build a set of valid extra IDs from the slides we just processed.
                    $valid_extra_ids = [];
                    foreach ($extra_slides as $es) {
                        if (!empty($es['id'])) $valid_extra_ids[$es['id']] = true;
                    }
                    foreach ($raw_unified as $entry) {
                        if (!is_array($entry) || empty($entry['type'])) continue;
                        $type = sanitize_text_field($entry['type']);
                        if ($type === 'minerva' && !empty($entry['key'])) {
                            $unified_slides[] = ['type' => 'minerva', 'key' => sanitize_text_field($entry['key'])];
                        } elseif ($type === 'extra' && !empty($entry['id'])) {
                            $id = sanitize_text_field($entry['id']);
                            // Drop entries that don't match an existing extra (deleted/orphaned).
                            if (isset($valid_extra_ids[$id])) {
                                $unified_slides[] = ['type' => 'extra', 'id' => $id];
                            }
                        } elseif ($type === 'static' && !empty($entry['key'])) {
                            // Static offers reference Minerva's databaseId. We can't validate
                            // against a live fetch here, so trust the sanitized key — the
                            // frontend drops any that no longer resolve.
                            $unified_slides[] = ['type' => 'static', 'key' => sanitize_text_field($entry['key'])];
                        }
                    }
                }
            }
            $data['unified_slides'] = $unified_slides;

            // Per-static-offer settings: { "<databaseId>": { visible, link_url } }.
            // Visibility toggle + optional link override; the image itself comes from Minerva.
            $static_settings = [];
            if (!empty($_POST['static_settings'])) {
                $raw_static = json_decode(stripslashes($_POST['static_settings']), true);
                if (is_array($raw_static)) {
                    foreach ($raw_static as $sid => $cfg) {
                        if (!is_array($cfg)) continue;
                        $clean = [];
                        if (array_key_exists('visible', $cfg)) $clean['visible'] = (bool) $cfg['visible'];
                        if (!empty($cfg['link_url'])) $clean['link_url'] = esc_url_raw($cfg['link_url']);
                        if (!empty($cfg['link_target'])) $clean['link_target'] = sanitize_text_field($cfg['link_target']);
                        if (isset($cfg['hidden_on'])) $clean['hidden_on'] = self::sanitize_hidden_on($cfg['hidden_on']);
                        // Per-slide Animated Frame Line — full settings object (dropped when off).
                        if (isset($cfg['frame_line'])) {
                            $fl_clean = self::sanitize_frame_line($cfg['frame_line']);
                            if ($fl_clean) $clean['frame_line'] = $fl_clean;
                        }
                        $static_settings[sanitize_text_field($sid)] = $clean;
                    }
                }
            }
            $data['static_settings'] = $static_settings;

            // Derive minerva_order from unified_slides for legacy compat (older Blade/Vue
            // codepaths still read this field). Fall back to the raw POST value when no
            // unified payload arrived (very old admin pages submitting via a stale JS).
            if (!empty($unified_slides)) {
                $data['minerva_order'] = array_values(array_map(
                    function ($entry) { return $entry['key']; },
                    array_filter($unified_slides, function ($entry) { return ($entry['type'] ?? '') === 'minerva'; })
                ));
            } elseif (!empty($_POST['minerva_order'])) {
                $raw_order = json_decode(stripslashes($_POST['minerva_order']), true);
                $data['minerva_order'] = is_array($raw_order) ? array_map('sanitize_text_field', $raw_order) : [];
            } else {
                $data['minerva_order'] = [];
            }
        }

        if ($source === 'countdown') {
            $data['countdown_end_date']   = sanitize_text_field($_POST['countdown_end_date'] ?? '');
            $data['countdown_start_date'] = sanitize_text_field($_POST['countdown_start_date'] ?? '');
            $data['countdown_title']      = sanitize_text_field($_POST['countdown_title'] ?? '');
            $data['countdown_desc']       = sanitize_text_field($_POST['countdown_desc'] ?? '');
            $data['countdown_expired_msg'] = sanitize_text_field($_POST['countdown_expired_msg'] ?? '');
            $data['countdown_bg_image']   = esc_url_raw($_POST['countdown_bg_image'] ?? '');
            $data['countdown_cta_label']  = sanitize_text_field($_POST['countdown_cta_label'] ?? '');
            $data['countdown_cta_url']    = esc_url_raw($_POST['countdown_cta_url'] ?? '');
            $data['countdown_image_link_url']    = esc_url_raw($_POST['countdown_image_link_url'] ?? '');
            $data['countdown_image_link_target'] = sanitize_text_field($_POST['countdown_image_link_target'] ?? '_self');
            $data['countdown_hide_watermark'] = !empty($_POST['countdown_hide_watermark']);
            // Visual style of the countdown digits (matches the Gutenberg block's options).
            // Whitelisted to avoid storing arbitrary values that the frontend doesn't handle.
            $allowed_cd_styles = ['flip', 'minimal', 'boxed', 'neon'];
            $cd_style_in = sanitize_text_field($_POST['countdown_style'] ?? 'flip');
            $data['countdown_style'] = in_array($cd_style_in, $allowed_cd_styles, true) ? $cd_style_in : 'flip';
            $data['countdown_overlay_opacity'] = max(0, min(100, absint($_POST['countdown_overlay_opacity'] ?? 60)));
        }

        return $data;
    }

    /**
     * Placeholder slider id used only for the live preview. Never persisted to the
     * lbx_sliders option — it's injected on the fly via the option_lbx_sliders filter.
     */
    const PREVIEW_SLIDER_ID = '__lbx_preview__';

    /**
     * Transient key holding the current user's stashed preview config.
     */
    private static function preview_transient_key()
    {
        return 'lbx_slider_preview_' . get_current_user_id();
    }

    /**
     * AJAX: stash the current (possibly unsaved) form config into a short-lived
     * transient and return the URL the iframe should load. Lets the preview reflect
     * in-progress edits without forcing a save.
     */
    public function ajax_preview_stash()
    {
        check_ajax_referer('lbx_slider_nonce', 'nonce');
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Unauthorized');
        }

        $sliders = self::get_sliders();
        $slider_id = sanitize_text_field($_POST['slider_id'] ?? '');
        $is_new = empty($slider_id) || $slider_id === 'new';
        if ($is_new) {
            $slider_id = 'slider_preview';
        }

        $data = self::build_slider_data_from_post($slider_id, $is_new, $sliders);
        // Preview ignores the enabled toggle and publish window so the operator always
        // sees the content — those gates only matter for the live, public render.
        $data['enabled']    = true;
        $data['start_date'] = '';
        $data['end_date']   = '';

        set_transient(self::preview_transient_key(), $data, 15 * MINUTE_IN_SECONDS);

        wp_send_json_success([
            'url' => add_query_arg(
                [
                    'lbx_slider_preview' => '1',
                    '_wpnonce'           => wp_create_nonce('lbx_slider_preview_view'),
                    't'                  => time(), // cache-bust so the iframe always reloads fresh
                ],
                home_url('/')
            ),
        ]);
    }

    /**
     * AJAX: clear the cached Minerva (and static) offers so the next preview / front-end render
     * pulls FRESH data from the feed. Does NOT touch saved sliders or per-slide custom overrides
     * — those live in the 'lbx_sliders' option and are re-applied ON TOP of the fresh feed data,
     * so refreshing never overwrites the client's edits.
     */
    public function ajax_refresh_minerva()
    {
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'forbidden'], 403);
        }
        check_ajax_referer('lbx_slider_nonce', 'nonce');
        delete_transient('lbx_slider_minerva_ssr');
        delete_transient('lbx_slider_static_ssr_v2');
        wp_send_json_success(['cleared' => true]);
    }

    /**
     * English locale for the editor preview's EN toggle. Prefers an English locale the site
     * actually registers via Polylang (e.g. en_CA) so we never push an unknown locale into a
     * French-first Polylang install (which can fatal the render); falls back to en_US.
     */
    private static function preview_en_locale()
    {
        if (function_exists('pll_languages_list')) {
            $locales = pll_languages_list(['fields' => 'locale']);
            if (is_array($locales)) {
                foreach ($locales as $loc) {
                    if (is_string($loc) && stripos($loc, 'en') === 0) {
                        return $loc;
                    }
                }
            }
        }
        return 'en_US';
    }

    /**
     * Front-end: when ?lbx_slider_preview=1 is hit by an authorized user, render a
     * minimal, chrome-free page that loads the THEME's own assets and renders the
     * lbx-slider block from the stashed config. Because it runs through the real
     * Blade → Vue → Swiper pipeline with the theme's app.css, the result is identical
     * to production — just isolated from the admin's CSS and the site's header/footer.
     */
    public function maybe_render_preview()
    {
        if (!isset($_GET['lbx_slider_preview'])) {
            return;
        }
        if (!is_user_logged_in() || !current_user_can('manage_options')) {
            wp_die(esc_html__('You are not allowed to preview sliders.', 'leadbox'), '', ['response' => 403]);
        }
        $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : '';
        if (!wp_verify_nonce($nonce, 'lbx_slider_preview_view')) {
            wp_die(esc_html__('This preview link expired. Close the preview and open it again.', 'leadbox'), '', ['response' => 403]);
        }

        // Two modes:
        //  - slider_id present → render an ALREADY-SAVED slider by id (used by the
        //    Gutenberg editor preview iframe). No stash needed.
        //  - otherwise → render the stashed (unsaved) config (the admin Live Preview).
        $req_id = sanitize_text_field($_GET['slider_id'] ?? '');
        if ($req_id !== '' && $req_id !== self::PREVIEW_SLIDER_ID) {
            $sliders = self::get_sliders();
            if (!isset($sliders[$req_id])) {
                wp_die(esc_html__('Slider not found.', 'leadbox'), '', ['response' => 404]);
            }
            $preview_id = $req_id;
        } else {
            $data = get_transient(self::preview_transient_key());
            if (empty($data) || !is_array($data)) {
                wp_die(esc_html__('Nothing to preview yet — close this and reopen the preview.', 'leadbox'), '', ['response' => 410]);
            }
            // Inject the stashed config as a temporary slider the Blade can resolve by id.
            $preview_id = self::PREVIEW_SLIDER_ID;
            add_filter('option_lbx_sliders', function ($value) use ($preview_id, $data) {
                if (!is_array($value)) {
                    $value = [];
                }
                $value[$preview_id] = $data;
                return $value;
            });
        }

        // Optional locale override so the editor preview can switch EN ⇄ FR. With Polylang the
        // preview otherwise renders in the site's default language (it loads on the default
        // home_url), so the French legal text / labels never show. We force get_locale() via a
        // filter (below) — the theme + slider Blade key their EN/FR output off it.
        // Read 'lbx_lang', NOT 'lang' — 'lang' is Polylang's reserved query var; passing it
        // reinterprets the request as the secondary language and drops the admin session on
        // French-first sites, which 500s the preview (wp_die above) before we even get here.
        $req_lang = sanitize_text_field($_GET['lbx_lang'] ?? '');
        $preview_locale = '';
        if ($req_lang === 'fr') {
            $preview_locale = 'fr_CA';
        } elseif ($req_lang === 'en') {
            $preview_locale = self::preview_en_locale();
        }
        if ($preview_locale) {
            // Tell ONLY the slider which language to render, via a constant the slider Blade
            // reads ($lbx_locale) — WITHOUT changing WordPress's global locale. Both
            // switch_to_locale() and add_filter('locale') flip get_locale() site-wide, which
            // crashes the Acorn/Blade render on some Polylang French-first production sites
            // (the 500 error view renders uncompiled). This touches nothing outside the slider.
            if (!defined('LBX_SLIDER_PREVIEW_LOCALE')) {
                define('LBX_SLIDER_PREVIEW_LOCALE', $preview_locale);
            }
            // Re-point the slider's 'leadbox' text domain at the PREVIEW locale so translated
            // __() strings (e.g. the "Learn More" button, "or") render in the chosen language —
            // not just the Minerva data. The theme owns this text domain (load_theme_textdomain
            // from /languages/{locale}.mo). We swap ONLY this one domain — no switch_to_locale(),
            // so the global locale / Polylang / Acorn stay untouched. Unload first → __() falls
            // back to the English source strings; then load the preview-locale .mo if present.
            if (function_exists('unload_textdomain')) {
                unload_textdomain('leadbox');
                $lbx_mo = get_template_directory() . '/languages/' . $preview_locale . '.mo';
                if (file_exists($lbx_mo)) {
                    load_textdomain('leadbox', $lbx_mo);
                }
            }
        }

        // NOTE: the preview reads Minerva offers from the normal 2-hour SSR cache. We do NOT
        // force a fresh fetch here — doing so ran a synchronous Minerva request (15s timeout)
        // on every preview load, which could exceed PHP's max_execution_time in production and
        // 500 the preview. To see newly-added feed fields right away, re-save any slider (that
        // clears the cache) or wait for it to expire.

        // Clean stage: no admin bar, no theme header/footer — just the slider on the
        // theme's assets so it matches production exactly without surrounding noise.
        add_filter('show_admin_bar', '__return_false');
        // The admin-bar "bump" (html { margin-top: 32px !important }) is hooked to
        // wp_head during `init` — earlier than this template_redirect callback — so the
        // show_admin_bar filter alone can't stop it. Remove the printer directly; that
        // 32px offset was pushing the slider down and leaving a white gap at the top.
        remove_action('wp_head', '_admin_bar_bump_cb');
        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 esc_html_e('Slider Preview', 'leadbox'); ?></title>
    <?php wp_head(); ?>
    <style>
        /* Belt-and-suspenders: neutralize the admin-bar margin + any stray body
           offset so the slider sits flush at the very top of the preview frame. */
        html { margin-top: 0 !important; }
        html, body { margin: 0 !important; padding: 0 !important; background: #ffffff !important; min-height: 0 !important; }
        #wpadminbar { display: none !important; }
        .lbx-preview-stage { width: 100%; overflow-x: hidden; }
    </style>
</head>
<body <?php body_class('lbx-slider-preview-page'); ?>>
    <?php
    // CRITICAL: the theme's app.js mounts Vue only on `.vue-app` containers
    // (see resources/scripts/app.js). Without this wrapper the <lbx-slider>
    // inline-template never boots — no Swiper, no arrows, no responsive — and the
    // slide markup renders raw/unstyled. Mirror the real front-end structure here.
    ?>
    <div class="vue-app lbx-preview-stage">
        <?php
        echo do_blocks(
            '<!-- wp:sage/lbx-slider {"sliderId":"' . esc_js($preview_id) . '"} --><!-- /wp:sage/lbx-slider -->'
        );
        ?>
    </div>
    <script>
    (function () {
        // Report the rendered height to the parent (Gutenberg editor) so the preview
        // iframe can size itself to the real slider. Re-posts as images/Swiper settle.
        function postH() {
            var h = Math.max(
                document.body ? document.body.scrollHeight : 0,
                document.documentElement ? document.documentElement.scrollHeight : 0
            );
            try { parent.postMessage({ lbxSliderPreviewHeight: h }, '*'); } catch (e) {}
        }
        window.addEventListener('load', function () { postH(); [300, 900, 1800, 3000].forEach(function (t) { setTimeout(postH, t); }); });
        window.addEventListener('resize', postH);
    })();
    </script>
    <?php wp_footer(); ?>
</body>
</html><?php
        exit;
    }

    public function ajax_delete_slider()
    {
        check_ajax_referer('lbx_slider_nonce', 'nonce');
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Unauthorized');
        }

        $slider_id = sanitize_text_field($_POST['slider_id'] ?? '');
        $sliders = self::get_sliders();

        if (isset($sliders[$slider_id])) {
            unset($sliders[$slider_id]);
            update_option(self::OPTION_KEY, $sliders);
            delete_transient('lbx_slider_minerva_ssr'); delete_transient('lbx_slider_static_ssr_v2');
            wp_send_json_success(['message' => __('Slider deleted.', 'leadbox')]);
        }
        wp_send_json_error(__('Slider not found.', 'leadbox'));
    }

    public function ajax_toggle_slider()
    {
        check_ajax_referer('lbx_slider_nonce', 'nonce');
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Unauthorized');
        }

        $slider_id = sanitize_text_field($_POST['slider_id'] ?? '');
        $sliders = self::get_sliders();

        if (isset($sliders[$slider_id])) {
            $sliders[$slider_id]['enabled'] = !$sliders[$slider_id]['enabled'];
            update_option(self::OPTION_KEY, $sliders);
            delete_transient('lbx_slider_minerva_ssr'); delete_transient('lbx_slider_static_ssr_v2');
            wp_send_json_success([
                'enabled' => $sliders[$slider_id]['enabled'],
                'message' => $sliders[$slider_id]['enabled'] ? __('Slider enabled.', 'leadbox') : __('Slider disabled.', 'leadbox'),
            ]);
        }
        wp_send_json_error(__('Slider not found.', 'leadbox'));
    }

    /**
     * Duplicate an existing slider.
     *
     * Creates a deep copy of the source slider under a fresh `slider_<uniqid>` key,
     * appends " (Copy)" to the name and starts the duplicate as disabled so it can
     * be reviewed before going live. Slide_settings and slides arrays are copied
     * verbatim — the duplicate renders identical content until the user edits it.
     */
    public function ajax_duplicate_slider()
    {
        check_ajax_referer('lbx_slider_nonce', 'nonce');
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Unauthorized');
        }

        $slider_id = sanitize_text_field($_POST['slider_id'] ?? '');
        $sliders = self::get_sliders();

        if (!isset($sliders[$slider_id])) {
            wp_send_json_error(__('Slider not found.', 'leadbox'));
        }

        // Deep copy via serialize/unserialize — covers nested slide_settings, ctas, etc.
        $copy = unserialize(serialize($sliders[$slider_id]));

        // Fresh unique id; guard against the (very unlikely) collision on rapid dup.
        do {
            $new_id = 'slider_' . uniqid();
        } while (isset($sliders[$new_id]));

        // Append " (Copy)" — avoid piling it up on a name that already ends in "(Copy)".
        $orig_name = isset($copy['name']) ? (string) $copy['name'] : __('Untitled Slider', 'leadbox');
        if (!preg_match('/\s*\(Copy( \d+)?\)\s*$/i', $orig_name)) {
            $copy['name'] = $orig_name . ' (Copy)';
        } else {
            // Already has "(Copy)"/"(Copy 2)" → bump the counter for clarity.
            if (preg_match('/\(Copy\s*(\d*)\)\s*$/i', $orig_name, $m)) {
                $n = !empty($m[1]) ? ((int) $m[1] + 1) : 2;
                $copy['name'] = preg_replace('/\s*\(Copy\s*\d*\)\s*$/i', ' (Copy ' . $n . ')', $orig_name);
            }
        }

        // Always start disabled so production doesn't see a stray duplicate.
        $copy['enabled'] = false;

        $sliders[$new_id] = $copy;
        update_option(self::OPTION_KEY, $sliders);
        delete_transient('lbx_slider_minerva_ssr'); delete_transient('lbx_slider_static_ssr_v2');

        wp_send_json_success([
            'slider_id' => $new_id,
            'name'      => $copy['name'],
            'message'   => __('Slider duplicated. Review and enable when ready.', 'leadbox'),
        ]);
    }

    public function render_admin_page()
    {
        nocache_headers();
        wp_enqueue_media();
        // Load sortable in the header so it's available when the inline IIFE runs.
        wp_enqueue_script('jquery-ui-sortable', false, ['jquery'], false, false);
        // CodeMirror (WP core) for the Developer tab's Custom CSS field — syntax
        // highlighting, CSS linting, bracket matching/auto-close, Tab indentation.
        // Returns false when the user disabled syntax highlighting in their WP
        // profile; in that case nothing is enqueued and the JS falls back to the
        // plain textarea.
        $css_editor_settings = wp_enqueue_code_editor(['type' => 'text/css']);
        if ($css_editor_settings) {
            wp_add_inline_script(
                'code-editor',
                'window.lbxCssEditorSettings = ' . wp_json_encode($css_editor_settings) . ';',
                'after'
            );
        }
        $sliders = self::get_sliders();
        $nonce = wp_create_nonce('lbx_slider_nonce');

        $editing_id = isset($_GET['edit_slider']) ? sanitize_text_field($_GET['edit_slider']) : '';
        $editing_slider = $editing_id && $editing_id !== 'new' && isset($sliders[$editing_id]) ? $sliders[$editing_id] : null;

        // Clean up orphaned slider saved with key 'new' (legacy bug)
        if (isset($sliders['new'])) {
            unset($sliders['new']);
            update_option(self::OPTION_KEY, $sliders);
        }
        $editing_source = $editing_slider['source'] ?? 'minerva';
        // LBT-1472 two independent auto-populate switches. New "Slides" sliders default OEM
        // ON / Static OFF; existing ones use the migration rule. The old standalone "Custom"
        // type only stays selectable when we're editing one that was already saved as custom.
        // New sliders default OEM Offers OFF (opt-in). Existing sliders keep their saved
        // value via is_auto_populate_oem (legacy minerva sliders still migrate to ON).
        $ed_auto_oem    = $editing_slider ? self::is_auto_populate_oem($editing_slider) : false;
        $ed_auto_static = $editing_slider ? self::is_auto_populate_static($editing_slider) : false;
        $ed_make_filter = $editing_slider ? self::get_make_filter($editing_slider) : '';
        // Dealer makes (manufacturer + all_brands) for the "Show Only {Make} Slides"
        // selector. The control only renders on multi-make sites (2+ makes) — or when
        // a saved filter exists, so it can still be cleared if the site's makes change.
        $dealer_makes = [];
        if (function_exists('get_leadbox_manufacturer')) {
            $mk = trim((string) get_leadbox_manufacturer());
            if ($mk !== '') $dealer_makes[] = $mk;
        }
        if (function_exists('get_leadbox_settings')) {
            $lbs_makes = get_leadbox_settings();
            foreach (explode(',', (string) ($lbs_makes['all_brands'] ?? '')) as $mk) {
                $mk = trim($mk);
                if ($mk !== '') $dealer_makes[] = $mk;
            }
        }
        // Dedupe case-insensitively, keeping the first spelling for display.
        $seen_makes = [];
        $dealer_makes = array_values(array_filter($dealer_makes, function ($mk) use (&$seen_makes) {
            $k = strtolower($mk);
            if (isset($seen_makes[$k])) return false;
            $seen_makes[$k] = true;
            return true;
        }));
        $is_legacy_custom = ($editing_source === 'custom');
        $editing_custom_slides = $editing_slider['custom_slides'] ?? [];
        $editing_extra_slides = $editing_slider['extra_slides'] ?? [];
        // Backfill stable IDs on legacy extra_slides that lack them. The unified
        // ordering references extras by ID, so every extra needs one before the
        // UI can render. Persisted IDs are assigned on save (ajax_save_slider).
        if (is_array($editing_extra_slides)) {
            foreach ($editing_extra_slides as $i => $es) {
                if (empty($es['id'])) {
                    $editing_extra_slides[$i]['id'] = uniqid('ex_', false);
                }
            }
        }
        $editing_unified_slides = $editing_slider['unified_slides'] ?? [];
        ?>
        <style>
            /* ═══════════════════════════════════════════════════════════
               LBX Slider Admin — Pro UI
               ═══════════════════════════════════════════════════════════ */
            :root {
                --lbx-primary: #2563eb;
                --lbx-primary-hover: #1d4ed8;
                --lbx-primary-light: #eff6ff;
                --lbx-primary-lighter: #f8faff;
                --lbx-success: #059669;
                --lbx-success-bg: #ecfdf5;
                --lbx-danger: #dc2626;
                --lbx-danger-bg: #fef2f2;
                --lbx-gray-50: #f9fafb;
                --lbx-gray-100: #f3f4f6;
                --lbx-gray-200: #e5e7eb;
                --lbx-gray-300: #d1d5db;
                --lbx-gray-400: #9ca3af;
                --lbx-gray-500: #6b7280;
                --lbx-gray-600: #4b5563;
                --lbx-gray-700: #374151;
                --lbx-gray-800: #1f2937;
                --lbx-gray-900: #111827;
                --lbx-radius: 10px;
                --lbx-radius-sm: 6px;
                --lbx-shadow: 0 1px 3px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.04);
                --lbx-shadow-md: 0 4px 6px -1px rgba(0,0,0,0.07), 0 2px 4px -2px rgba(0,0,0,0.05);
                --lbx-shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.08), 0 4px 6px -4px rgba(0,0,0,0.04);
            }

            .lbx-admin { max-width: 100%; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin-right: 16px; }
            .lbx-admin * { box-sizing: border-box; }

            /* ─── Header ──────────────────────────────────────────── */
            .lbx-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
            .lbx-header-left { display: flex; align-items: center; gap: 10px; }
            .lbx-header-icon { width: 34px; height: 34px; background: linear-gradient(135deg, var(--lbx-primary), #7c3aed); border-radius: 10px; display: flex; align-items: center; justify-content: center; color: #fff; font-size: 18px; box-shadow: 0 2px 8px rgba(37,99,235,0.22); }
            .lbx-header-icon .dashicons { font-size: 18px; width: 18px; height: 18px; }
            /* Brand-mark variant: drop the gradient and show the Leadbox icon on a clean tile. */
            .lbx-header-icon--brand { background: #fff; border: 1px solid var(--lbx-gray-200); box-shadow: 0 1px 3px rgba(0,0,0,0.08); padding: 5px; box-sizing: border-box; }
            .lbx-header-icon--brand img { width: 100%; height: 100%; object-fit: contain; display: block; }
            .lbx-header h1 { margin: 0; font-size: 18px; font-weight: 700; color: var(--lbx-gray-900); line-height: 1.2; letter-spacing: -0.01em; }
            .lbx-header p { margin: 2px 0 0; font-size: 12px; color: var(--lbx-gray-500); }
            .lbx-header-version { display: inline-flex; align-items: center; padding: 1px 6px; background: var(--lbx-gray-100); border-radius: 3px; font-size: 10px; font-weight: 600; color: var(--lbx-gray-500); margin-left: 6px; }

            /* ─── Notice ──────────────────────────────────────────── */
            /* Toast notifications (fixed so they don't push admin content) */
            /* Toast notices: offset far enough below the WP admin bar (32px desktop)
               to clear secondary bars like Query Monitor. max-width keeps long messages
               tidy; mobile switches to a full-width strip for readability. */
            #lbx-slider-notices { position: fixed; top: 72px; right: 24px; z-index: 999999; pointer-events: none; max-width: calc(100% - 48px); }
            .lbx-notice { padding: 14px 20px; border-radius: 10px; font-size: 14px; font-weight: 600; line-height: 1.45; margin-bottom: 0; display: flex; align-items: center; gap: 10px; max-width: 420px; animation: lbxSlideIn 0.4s cubic-bezier(0.34,1.56,0.64,1); box-shadow: 0 10px 30px rgba(0,0,0,0.22), 0 2px 6px rgba(0,0,0,0.08); pointer-events: auto; }
            .lbx-notice .dashicons { flex-shrink: 0; }
            .lbx-notice-success { background: #065f46; color: #fff; border: none; }
            .lbx-notice-success .dashicons { color: #6ee7b7; }
            .lbx-notice-error { background: #991b1b; color: #fff; border: none; }
            .lbx-notice-error .dashicons { color: #fca5a5; }
            .lbx-notice-info { background: #1e3a8a; color: #fff; border: none; }
            .lbx-notice-info .dashicons { color: #93c5fd; }
            @media screen and (max-width: 782px) {
                #lbx-slider-notices { top: 60px; right: 12px; left: 12px; max-width: calc(100% - 24px); }
                .lbx-notice { max-width: 100%; }
            }
            @keyframes lbxSlideIn { from { opacity:0; transform:translateY(-12px) scale(0.95); } to { opacity:1; transform:translateY(0) scale(1); } }
            @keyframes lbx-spin { to { transform: rotate(360deg); } }
            .lbx-spin { animation: lbx-spin 0.8s linear infinite; }

            /* ─── Card ────────────────────────────────────────────── */
            .lbx-card { background: #fff; border: 1px solid var(--lbx-gray-200); border-radius: var(--lbx-radius); box-shadow: var(--lbx-shadow); margin-bottom: 24px; overflow: hidden; transition: box-shadow 0.2s; }
            .lbx-card:hover { box-shadow: var(--lbx-shadow-md); }
            .lbx-card-header { display: flex; align-items: center; justify-content: space-between; padding: 20px 24px; border-bottom: 1px solid var(--lbx-gray-100); background: var(--lbx-gray-50); }
            .lbx-card-header h2 { margin: 0; font-size: 16px; font-weight: 700; color: var(--lbx-gray-900); display: flex; align-items: center; gap: 8px; }
            .lbx-card-header h2 .dashicons { color: var(--lbx-primary); font-size: 18px; width: 18px; height: 18px; }
            .lbx-card-body { padding: 28px; }

            /* ─── Form Fields ─────────────────────────────────────── */
            .lbx-field { margin-bottom: 20px; }
            .lbx-field:last-child { margin-bottom: 0; }
            .lbx-label { display: block; font-size: 13px; font-weight: 600; color: var(--lbx-gray-700); margin-bottom: 6px; }
            .lbx-label .lbx-label-hint { font-weight: 400; color: var(--lbx-gray-400); font-size: 12px; margin-left: 4px; }
            /* Compact uppercase label — used in the General/Basic panel to match the
               style of the countdown CTA labels. Same visual contract as
               .lbx-slide-field > label but available outside that wrapper. */
            .lbx-mini-label { display: block; font-size: 12px; font-weight: 600; color: var(--lbx-gray-600); margin-bottom: 4px; text-transform: uppercase; letter-spacing: 0.03em; }
            .lbx-req-star { color: #dc2626; font-weight: 700; margin-left: 2px; }
            .lbx-field-legend { font-size: 12px; color: var(--lbx-gray-500); margin: 0; padding: 8px 12px; background: #fff; border-radius: var(--lbx-radius-sm); border: 1px solid var(--lbx-gray-200); line-height: 1.5; }
            .lbx-field-legend strong { color: var(--lbx-gray-700); }
            /* Using background-color (not background shorthand) avoids wiping out
               background-image / background-repeat on selects that paint a chevron. */
            .lbx-input { width: 100%; padding: 10px 14px; border: 1px solid var(--lbx-gray-300); border-radius: 8px; font-size: 14px; color: var(--lbx-gray-800); background-color: #fff; transition: border-color 0.15s, box-shadow 0.15s, background-color 0.15s; outline: none; font-family: inherit; line-height: 1.35; }
            .lbx-input:hover { border-color: var(--lbx-gray-400); }
            .lbx-input:focus { border-color: var(--lbx-primary); box-shadow: 0 0 0 3px rgba(37,99,235,0.14); background-color: #fff; }
            .lbx-input::placeholder { color: var(--lbx-gray-300); font-style: italic; }
            .lbx-input:disabled { background-color: var(--lbx-gray-50); color: var(--lbx-gray-400); cursor: not-allowed; }
            .lbx-slide-field input::placeholder,
            .lbx-slide-field textarea::placeholder,
            .lbx-oem-modal__body input::placeholder,
            .lbx-oem-modal__body textarea::placeholder { color: var(--lbx-gray-300); font-style: italic; }
            select.lbx-input { -webkit-appearance: none; appearance: none; background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="%236b7280" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>'); background-repeat: no-repeat; background-position: right 14px center; background-size: 14px 14px; padding-right: 38px; cursor: pointer; }
            select.lbx-input:hover { background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="%232563eb" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>'); }
            select.lbx-input:focus { background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="%232563eb" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>'); }
            textarea.lbx-input { resize: vertical; min-height: 72px; }

            /* ─── Toggle Switch ────────────────────────────────────── */
            .lbx-toggle { display: flex; align-items: center; gap: 10px; cursor: pointer; }
            .lbx-toggle input { display: none; }
            .lbx-toggle-track { width: 44px; height: 24px; background: var(--lbx-gray-300); border-radius: 12px; position: relative; transition: background 0.2s; flex-shrink: 0; }
            .lbx-toggle input:checked + .lbx-toggle-track { background: var(--lbx-primary); }
            .lbx-toggle-track::after { content: ''; position: absolute; top: 2px; left: 2px; width: 20px; height: 20px; background: #fff; border-radius: 50%; transition: transform 0.2s; box-shadow: 0 1px 3px rgba(0,0,0,0.15); }
            .lbx-toggle input:checked + .lbx-toggle-track::after { transform: translateX(20px); }
            .lbx-toggle-label { font-size: 13px; color: var(--lbx-gray-600); }

            /* ─── Source Cards (radio-style) ──────────────────────── */
            /* Flex (not a fixed 3-col grid) so the visible cards always fill the row —
               hidden cards (e.g. legacy Custom) don't leave an empty column behind. */
            .lbx-sources { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 8px; }
            .lbx-sources .lbx-source-card { flex: 1 1 240px; min-width: 240px; }
            .lbx-source-card { border: 2px solid var(--lbx-gray-200); border-radius: var(--lbx-radius); padding: 20px; cursor: pointer; transition: all 0.2s; position: relative; }
            .lbx-source-card:hover { border-color: var(--lbx-gray-300); background: var(--lbx-gray-50); box-shadow: var(--lbx-shadow); }
            .lbx-source-card.active { border-color: var(--lbx-primary); background: var(--lbx-primary-light); box-shadow: 0 0 0 3px rgba(37,99,235,0.12); }
            .lbx-source-card input { display: none; }
            .lbx-source-card-icon { width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; margin-bottom: 12px; font-size: 20px; }
            .lbx-source-card-icon .dashicons { font-size: 20px; width: 20px; height: 20px; }
            .lbx-source-card[data-source="minerva"] .lbx-source-card-icon { background: linear-gradient(135deg, #eff6ff, #dbeafe); color: #2563eb; }
            .lbx-source-card[data-source="custom"] .lbx-source-card-icon { background: linear-gradient(135deg, #f0fdf4, #dcfce7); color: #16a34a; }
            .lbx-source-card[data-source="countdown"] .lbx-source-card-icon { background: linear-gradient(135deg, #fefce8, #fef08a); color: #ca8a04; }
            .lbx-source-card h4 { margin: 0 0 4px; font-size: 15px; font-weight: 700; color: var(--lbx-gray-800); }
            .lbx-source-card p { margin: 0; font-size: 12px; color: var(--lbx-gray-500); line-height: 1.6; }
            .lbx-source-card .lbx-source-hint { display: flex; align-items: flex-start; gap: 6px; margin-top: 10px; padding: 8px 10px; background: rgba(0,0,0,0.03); border-radius: var(--lbx-radius-sm); font-size: 11px; color: var(--lbx-gray-500); line-height: 1.4; }
            .lbx-source-card .lbx-source-hint .dashicons { font-size: 14px; width: 14px; height: 14px; flex-shrink: 0; margin-top: 1px; color: var(--lbx-gray-400); }
            .lbx-source-check { position: absolute; top: 12px; right: 12px; width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--lbx-gray-300); display: flex; align-items: center; justify-content: center; transition: all 0.15s; }
            .lbx-source-card.active .lbx-source-check { border-color: var(--lbx-primary); background: var(--lbx-primary); color: #fff; }
            .lbx-source-check::after { content: ''; display: none; }
            .lbx-source-card.active .lbx-source-check::after { display: block; content: '\2713'; font-size: 12px; font-weight: 700; line-height: 1; }
            @media (max-width: 782px) { .lbx-sources { grid-template-columns: 1fr; } }
            /* ─── Type picker modal (shown once on slider creation) ─── */
            .lbx-type-modal-backdrop { display: none; position: fixed; inset: 0; background: rgba(15,23,42,0.55); z-index: 100001; align-items: center; justify-content: center; padding: 24px; }
            .lbx-type-modal-backdrop.is-open { display: flex; }
            .lbx-type-modal { background: #fff; border-radius: 16px; box-shadow: 0 24px 70px rgba(0,0,0,0.3); width: 100%; max-width: 680px; padding: 28px; animation: lbxModalIn 0.2s ease; }
            .lbx-type-modal__head { text-align: center; margin-bottom: 22px; }
            .lbx-type-modal__head h3 { margin: 0 0 6px; font-size: 20px; font-weight: 800; color: var(--lbx-gray-900); }
            .lbx-type-modal__head p { margin: 0; font-size: 13px; color: var(--lbx-gray-500); }
            .lbx-type-modal__choices { display: flex; gap: 16px; }
            .lbx-type-choice { flex: 1; display: flex; flex-direction: column; align-items: flex-start; gap: 8px; text-align: left; padding: 22px; border: 2px solid var(--lbx-gray-200); border-radius: 14px; background: #fff; cursor: pointer; transition: border-color 0.18s, background 0.18s, box-shadow 0.18s, transform 0.18s; }
            .lbx-type-choice:hover { border-color: var(--lbx-primary); background: var(--lbx-primary-light); box-shadow: 0 8px 24px rgba(37,99,235,0.16); transform: translateY(-2px); }
            .lbx-type-choice__icon { width: 46px; height: 46px; border-radius: 12px; display: flex; align-items: center; justify-content: center; }
            .lbx-type-choice__icon .dashicons { font-size: 24px; width: 24px; height: 24px; }
            .lbx-type-choice__icon--slides { background: linear-gradient(135deg, #eff6ff, #dbeafe); color: #2563eb; }
            .lbx-type-choice__icon--countdown { background: linear-gradient(135deg, #fefce8, #fef08a); color: #ca8a04; }
            .lbx-type-choice__title { font-size: 16px; font-weight: 700; color: var(--lbx-gray-900); }
            .lbx-type-choice__desc { font-size: 12px; color: var(--lbx-gray-500); line-height: 1.5; }
            .lbx-type-modal__foot { text-align: center; margin-top: 20px; }
            .lbx-type-modal__cancel { font-size: 12px; color: var(--lbx-gray-500); text-decoration: none; }
            .lbx-type-modal__cancel:hover { color: var(--lbx-gray-700); text-decoration: underline; }
            @media (max-width: 640px) { .lbx-type-modal__choices { flex-direction: column; } }

            /* ─── Custom Slide Builder ────────────────────────────── */
            .lbx-slides-list { margin: 0 0 16px; }
            .lbx-slide { background: #fff; border: 1px solid var(--lbx-gray-200); border-radius: var(--lbx-radius); margin-bottom: 12px; overflow: hidden; transition: box-shadow 0.2s, border-color 0.2s; }
            .lbx-slide:hover { box-shadow: var(--lbx-shadow-md); border-color: var(--lbx-gray-300); }
            .lbx-slide-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; background: var(--lbx-gray-50); border-bottom: 1px solid var(--lbx-gray-100); cursor: pointer; user-select: none; }
            .lbx-slide--collapsed .lbx-slide-header { border-bottom: none; }
            .lbx-slide-header-left { display: flex; align-items: center; gap: 10px; min-width: 0; }
            .lbx-slide-header-left .lbx-slide-thumb { width: 52px; height: 34px; border-radius: 6px; object-fit: cover; flex-shrink: 0; background: var(--lbx-gray-200); border: 1px solid var(--lbx-gray-200); }
            .lbx-slide-chevron { transition: transform 0.2s; color: var(--lbx-gray-400); margin-left: 8px; }
            .lbx-slide--collapsed .lbx-slide-chevron { transform: rotate(-90deg); }
            .lbx-slide-num { width: 28px; height: 28px; background: var(--lbx-primary); color: #fff; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; flex-shrink: 0; }
            .lbx-slide-title-text { font-size: 14px; font-weight: 600; color: var(--lbx-gray-800); }
            .lbx-slide-actions { display: flex; gap: 4px; }
            .lbx-slide-actions button { background: none; border: 1px solid var(--lbx-gray-200); border-radius: var(--lbx-radius-sm); width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--lbx-gray-500); transition: all 0.15s; }
            .lbx-slide-actions button:hover { background: var(--lbx-gray-100); color: var(--lbx-gray-700); border-color: var(--lbx-gray-300); }
            .lbx-slide-actions .lbx-remove-slide:hover { background: var(--lbx-danger-bg); color: var(--lbx-danger); border-color: #fecaca; }
            .lbx-slide-actions button .dashicons { font-size: 16px; width: 16px; height: 16px; }
            .lbx-slide-body { padding: 20px; display: flex; flex-direction: column; gap: 16px; }
            .lbx-slide--collapsed .lbx-slide-body { display: none; }
            .lbx-slide-top-row { display: flex; gap: 16px; align-items: flex-start; }
            .lbx-slide-top-row > div:first-child { width: 220px; flex-shrink: 0; background: var(--lbx-gray-50); border: 1px solid var(--lbx-gray-200); border-radius: var(--lbx-radius-sm); padding: 10px; }
            .lbx-slide-top-row .lbx-slide-fields { flex: 1; min-width: 0; background: #fff; border: 1px solid var(--lbx-gray-200); border-radius: var(--lbx-radius-sm); padding: 12px; }

            /* Slide image upload */
            /* Base: not clickable. The interactive modifier (.lbx-pick-image / .lbx-pick-extra-image)
               is added in JS only when type=image, so video previews don't pretend to be picker
               buttons. */
            .lbx-slide-image { width: 180px; height: 120px; background: var(--lbx-gray-50); border: 2px dashed var(--lbx-gray-300); border-radius: var(--lbx-radius-sm); display: flex; align-items: center; justify-content: center; cursor: default; overflow: hidden; position: relative; transition: all 0.15s; flex-shrink: 0; }
            .lbx-slide-image.lbx-pick-image,
            .lbx-slide-image.lbx-pick-extra-image { cursor: pointer; }
            .lbx-slide-image.lbx-pick-image:hover,
            .lbx-slide-image.lbx-pick-extra-image:hover { border-color: var(--lbx-primary); background: var(--lbx-primary-lighter); }
            .lbx-slide-image img, .lbx-slide-image video { width: 100%; height: 100%; object-fit: cover; border-radius: 4px; display: block; }
            .lbx-video-preview { position: relative; width: 100%; height: 100%; }
            .lbx-video-preview-loader { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: rgba(255,255,255,0.85); border-radius: 4px; transition: opacity 0.25s; }
            .lbx-video-preview-loader.loaded { opacity: 0; pointer-events: none; }
            .lbx-slide-image-ph { text-align: center; color: var(--lbx-gray-400); }
            .lbx-slide-image-ph .dashicons { font-size: 28px; width: 28px; height: 28px; display: block; margin: 0 auto 6px; color: var(--lbx-gray-300); }
            .lbx-slide-image-ph span { font-size: 12px; }

            /* Slide fields */
            .lbx-slide-fields { display: flex; flex-direction: column; gap: 14px; }
            /* EN/FR language tabs for the additional-slide translatable content (title + description). */
            .lbx-ex-langtabs { display: inline-flex; gap: 4px; padding: 3px; margin-bottom: 14px; background: var(--lbx-gray-100); border: 1px solid var(--lbx-gray-200); border-radius: 999px; }
            .lbx-ex-langtab { border: 0; background: transparent; padding: 4px 16px; border-radius: 999px; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--lbx-gray-500); cursor: pointer; transition: background 0.15s, color 0.15s, box-shadow 0.15s; }
            .lbx-ex-langtab:hover { color: var(--lbx-gray-700); }
            .lbx-ex-langtab.is-active { background: #fff; color: var(--lbx-primary); box-shadow: 0 1px 2px rgba(0,0,0,0.08); }
            /* Language badges so it's obvious which language's data you're editing (LBT-1489). */
            .lbx-ex-fldlang { display:inline-block; font-size:9px; font-weight:700; letter-spacing:0.04em; color:var(--lbx-gray-500); background:var(--lbx-gray-100); border:1px solid var(--lbx-gray-200); border-radius:4px; padding:1px 5px; vertical-align:middle; }
            .lbx-ex-fldlang.is-fr { color:#7c3aed; background:#f5f3ff; border-color:#ddd6fe; }
            .lbx-ex-editlang { display:inline-block; font-size:11px; font-weight:700; color:var(--lbx-primary); background:#eff6ff; border:1px solid #bfdbfe; border-radius:999px; padding:2px 10px; vertical-align:middle; }
            .lbx-ex-editlang.is-fr { color:#7c3aed; background:#f5f3ff; border-color:#ddd6fe; }
            /* Slide field top labels — apply only to direct-child labels to avoid steamrolling nested labels (e.g. color chip which wraps everything in a <label>). */
            .lbx-slide-field > label { display: block; font-size: 12px; font-weight: 600; color: var(--lbx-gray-600); margin-bottom: 4px; text-transform: uppercase; letter-spacing: 0.03em; }
            .lbx-slide-field input, .lbx-slide-field textarea, .lbx-slide-field select { width: 100%; padding: 9px 12px; border: 1px solid var(--lbx-gray-300); border-radius: 8px; font-size: 13px; color: var(--lbx-gray-800); outline: none; transition: border-color 0.15s, box-shadow 0.15s, background 0.15s; background-color: #fff; font-family: inherit; line-height: 1.35; }
            .lbx-slide-field input:hover, .lbx-slide-field textarea:hover, .lbx-slide-field select:hover { border-color: var(--lbx-gray-400); }
            .lbx-slide-field input:focus, .lbx-slide-field textarea:focus, .lbx-slide-field select:focus { border-color: var(--lbx-primary); box-shadow: 0 0 0 3px rgba(37,99,235,0.14); }
            .lbx-slide-field select { -webkit-appearance: none; appearance: none; background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="%236b7280" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>'); background-repeat: no-repeat; background-position: right 12px center; background-size: 14px 14px; padding-right: 34px; cursor: pointer; }
            .lbx-slide-field select:hover,
            .lbx-slide-field select:focus { background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="%232563eb" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>'); }
            .lbx-slide-field.has-error input { border-color: #dc2626; box-shadow: 0 0 0 3px rgba(220,38,38,0.12); }
            .lbx-slide-field.has-error input:focus { border-color: #dc2626; box-shadow: 0 0 0 3px rgba(220,38,38,0.18); }
            .lbx-field-error { display: none; font-size: 11px; color: #dc2626; margin-top: 4px; font-weight: 500; }

            /* Title style row */
            /* ─── Formatting toolbar ──────────────────────────────── */
            .lbx-field-toolbar { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
            .lbx-format-toolbar { display: inline-flex; gap: 4px; }
            .lbx-format-btn { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: 1px solid var(--lbx-gray-300); border-radius: 6px; cursor: pointer; background: #fff; font-size: 14px; font-weight: 700; transition: all 0.15s; color: var(--lbx-gray-400); padding: 0; margin: 0; }
            .lbx-format-btn:hover { background: var(--lbx-gray-100); color: var(--lbx-gray-700); border-color: var(--lbx-gray-400); }
            .lbx-format-btn.active { background: var(--lbx-primary); color: #fff; border-color: var(--lbx-primary); box-shadow: 0 1px 3px rgba(37,99,235,.3); }
            .lbx-format-btn input { display: none; }

            /* ─── Color picker chip ──────────────────────────────── */
            /* Color chip component — shows a preview swatch + hex input + label, signaling clearly
               that it's a color picker even when no color is selected (checkerboard fallback). */
            .lbx-color-chip { display: inline-flex; align-items: center; gap: 0; border: 1px solid var(--lbx-gray-300); border-radius: 6px; background: #fff; overflow: hidden; height: 34px; cursor: pointer; transition: border-color .15s, box-shadow .15s; position: relative; flex: 0 0 auto; width: auto; }
            .lbx-color-chip:hover { border-color: var(--lbx-primary); box-shadow: 0 0 0 3px rgba(37,99,235,0.08); }
            .lbx-color-chip:focus-within { border-color: var(--lbx-primary); box-shadow: 0 0 0 3px rgba(37,99,235,0.15); }
            /* Preview column: shows checkerboard pattern behind the fill so "white / no color" is obviously distinct from "no picker here". */
            .lbx-color-chip__preview { width: 34px; height: 34px; flex-shrink: 0; position: relative; border-right: 1px solid var(--lbx-gray-200); display: flex; align-items: center; justify-content: center;
                background-image: linear-gradient(45deg, #d1d5db 25%, transparent 25%), linear-gradient(-45deg, #d1d5db 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #d1d5db 75%), linear-gradient(-45deg, transparent 75%, #d1d5db 75%);
                background-size: 8px 8px; background-position: 0 0, 0 4px, 4px -4px, -4px 0; background-color: #fff; }
            .lbx-color-chip__preview .lbx-cc-swatch { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%; border: none; padding: 0; z-index: 2; }
            .lbx-color-chip__preview .lbx-cc-fill { width: 22px; height: 22px; border-radius: 4px; border: 1px solid rgba(0,0,0,.18); pointer-events: none; box-shadow: inset 0 1px 2px rgba(0,0,0,.05); }
            /* Body hugs its content — chip stays compact and consistent across all usages (CTA toolbar, title/description toolbar, standalone field). */
            .lbx-color-chip__body { display: inline-flex; align-items: center; gap: 8px; padding: 0 10px 0 12px; flex: 0 0 auto; }
            /* Hex input — force reset on width/padding/border because other scoped rules
               (e.g. `.lbx-slide-field input`) otherwise inflate it to 100% and re-add a border,
               breaking the chip layout inside title/description toolbars. */
            .lbx-color-chip .lbx-color-chip__hex,
            .lbx-slide-field .lbx-color-chip .lbx-color-chip__hex { width: 72px; min-width: 72px; max-width: 72px; border: none; outline: none; font-size: 12px; font-family: SFMono-Regular, Consolas, 'Liberation Mono', monospace; color: var(--lbx-gray-800); background: transparent; padding: 0; letter-spacing: 0.02em; box-shadow: none; border-radius: 0; height: auto; }
            .lbx-color-chip .lbx-color-chip__hex:focus,
            .lbx-slide-field .lbx-color-chip .lbx-color-chip__hex:focus { border: none; box-shadow: none; outline: none; }
            .lbx-color-chip .lbx-color-chip__hex::placeholder { color: var(--lbx-gray-400); }
            /* Small palette icon prefix on the label to reinforce "this is a color picker". */
            .lbx-color-chip__label { font-size: 10px; font-weight: 700; color: var(--lbx-gray-500); text-transform: uppercase; letter-spacing: .06em; white-space: nowrap; pointer-events: none; display: inline-flex; align-items: center; gap: 4px; }
            .lbx-color-chip__label::before { content: ''; display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: linear-gradient(135deg, #ef4444 0%, #f59e0b 25%, #10b981 50%, #3b82f6 75%, #8b5cf6 100%); flex-shrink: 0; }

            /* CTA Groups (legacy) */
            .lbx-ctas-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
            .lbx-cta-group { background: var(--lbx-gray-50); border: 1px solid var(--lbx-gray-200); border-radius: var(--lbx-radius-sm); padding: 14px; }
            .lbx-cta-group h5 { margin: 0 0 10px; font-size: 12px; font-weight: 600; color: var(--lbx-gray-600); text-transform: uppercase; letter-spacing: 0.03em; display: flex; align-items: center; flex-wrap: wrap; gap: 4px 6px; }
            .lbx-cta-group h5 .dashicons { font-size: 14px; width: 14px; height: 14px; color: var(--lbx-gray-400); }
            /* Inline hints inside section titles must NOT inherit the h5's uppercase /
               bold / letter-spacing — otherwise "(optional …)" shouts next to the label. */
            .lbx-cta-group h5 .lbx-label-hint { text-transform: none; letter-spacing: normal; font-weight: 400; font-size: 11px; color: var(--lbx-gray-400); }
            /* Help line under a section title (for longer descriptions that shouldn't
               sit on the title row). */
            .lbx-field-hint { margin: -4px 0 10px; font-size: 11.5px; line-height: 1.45; color: var(--lbx-gray-400); }
            .lbx-cta-group .lbx-cta-fields { display: flex; flex-direction: column; gap: 6px; }
            .lbx-cta-group input { width: 100%; padding: 7px 10px; border: 1px solid var(--lbx-gray-300); border-radius: 4px; font-size: 13px; outline: none; transition: border-color 0.15s; }
            .lbx-cta-group input:focus { border-color: var(--lbx-primary); }
            /* Textareas in a CTA group (e.g. per-slide disclaimer EN/FR) match the input look. */
            .lbx-cta-group textarea { width: 100%; padding: 7px 10px; border: 1px solid var(--lbx-gray-300); border-radius: 4px; font-size: 13px; font-family: inherit; line-height: 1.4; outline: none; transition: border-color 0.15s; resize: vertical; }
            .lbx-cta-group textarea:focus { border-color: var(--lbx-primary); }
            /* Checkboxes/radios inside a CTA group must not stretch like text inputs. */
            .lbx-cta-group input[type="checkbox"], .lbx-cta-group input[type="radio"] { width: auto; padding: 0; }
            .lbx-cta-color-row { display: flex; align-items: center; gap: 6px; }
            .lbx-cta-color-row input[type="color"] { width: 28px; height: 28px; padding: 0; border: 2px solid var(--lbx-gray-300); border-radius: 4px; cursor: pointer; }
            .lbx-cta-color-row input[type="text"] { flex: 1; }

            /* CTA Builder */
            .lbx-ctas-builder { margin: 12px 0; background: var(--lbx-gray-50); border: 1px solid var(--lbx-gray-200); border-radius: var(--lbx-radius); padding: 14px; }
            .lbx-ctas-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
            .lbx-ctas-header h5 { margin: 0; font-size: 12px; font-weight: 600; color: var(--lbx-gray-600); text-transform: uppercase; letter-spacing: 0.03em; display: flex; align-items: center; gap: 6px; }
            .lbx-ctas-header h5 .dashicons { font-size: 14px; width: 14px; height: 14px; color: var(--lbx-gray-400); }
            .lbx-ctas-list { display: flex; flex-direction: column; gap: 10px; }
            .lbx-cta-row { background: #fff; border: 1px solid var(--lbx-gray-200); border-radius: var(--lbx-radius-sm); padding: 12px 14px; display: flex; gap: 12px; align-items: flex-start; transition: border-color 0.15s; }
            .lbx-cta-row:hover { border-color: var(--lbx-gray-300); }
            .lbx-cta-row__num { width: 22px; height: 22px; border-radius: 50%; background: var(--lbx-primary); color: #fff; font-size: 11px; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; margin-top: 4px; }
            .lbx-cta-row__fields { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
            .lbx-cta-row__main { display: grid; grid-template-columns: 1fr 1.2fr auto; gap: 8px; align-items: center; }
            .lbx-cta-row__main input[type="text"],
            .lbx-cta-row__main input[type="url"] { width: 100%; padding: 7px 10px; border: 1px solid var(--lbx-gray-300); border-radius: var(--lbx-radius-sm); font-size: 12px; outline: none; transition: border-color 0.15s; }
            .lbx-cta-row__main input:focus { border-color: var(--lbx-primary); }
            .lbx-cta-row__main select { padding: 7px 24px 7px 8px; border: 1px solid var(--lbx-gray-300); border-radius: var(--lbx-radius-sm); font-size: 11px; color: var(--lbx-gray-600); min-width: 90px; -webkit-appearance: none; appearance: none; background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%236b7280' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 8px center; }
            .lbx-cta-row__meta { display: flex; flex-direction: column; gap: 8px; align-items: stretch; }
            .lbx-cta-row__colors { display: flex; gap: 10px; flex-wrap: wrap; }
            .lbx-cta-row__posrow { display: flex; align-items: center; gap: 10px; }
            .lbx-cta-row__color { flex-shrink: 0; }
            /* Dedicated compact color field for CTAs — replaces the generic
               .lbx-color-chip here because that chip's overflow:hidden + label
               positioning were clipping the identifier text in this tight row. */
            .lbx-cta-color { display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 8px 0 4px; border: 1px solid var(--lbx-gray-300); border-radius: 6px; background: #fff; transition: border-color 0.15s, box-shadow 0.15s; flex-shrink: 0; }
            .lbx-cta-color:hover { border-color: var(--lbx-primary); box-shadow: 0 0 0 3px rgba(37,99,235,0.08); }
            .lbx-cta-color:focus-within { border-color: var(--lbx-primary); box-shadow: 0 0 0 3px rgba(37,99,235,0.15); }
            .lbx-cta-color__tag { font-size: 10px; font-weight: 700; letter-spacing: 0.05em; line-height: 1; padding: 4px 6px; border-radius: 4px; white-space: nowrap; }
            .lbx-cta-color__tag--bg { color: #1e40af; background: #dbeafe; }
            .lbx-cta-color__tag--text { color: #374151; background: var(--lbx-gray-100); }
            .lbx-cta-color__picker { position: relative; width: 22px; height: 22px; border-radius: 4px; cursor: pointer; overflow: hidden; border: 1px solid rgba(0,0,0,0.15); display: inline-block; flex-shrink: 0; margin: 0; padding: 0; }
            .lbx-cta-color__picker input[type="color"] { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; padding: 0; border: none; z-index: 2; }
            .lbx-cta-color__fill { position: absolute; inset: 0; z-index: 1; }
            .lbx-cta-color__hex { width: 78px !important; min-width: 78px !important; max-width: 78px !important; border: none !important; outline: none !important; box-shadow: none !important; font-family: SFMono-Regular, Consolas, monospace; font-size: 11px; color: var(--lbx-gray-800); background: transparent !important; padding: 0 !important; height: auto !important; margin: 0 !important; }
            .lbx-cta-color__hex::placeholder { color: var(--lbx-gray-400); }
            .lbx-cta-row__pos { display: flex; align-items: center; gap: 6px; }
            .lbx-cta-row__pos > label { font-size: 11px; color: var(--lbx-gray-500); white-space: nowrap; margin: 0; }
            .lbx-cta-row__pos select { padding: 5px 24px 5px 8px; border: 1px solid var(--lbx-gray-300); border-radius: var(--lbx-radius-sm); font-size: 11px; min-width: 140px; -webkit-appearance: none; appearance: none; background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%236b7280' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 8px center; }
            .lbx-cta-remove-btn { width: 24px; height: 24px; border-radius: 50%; border: 1px solid var(--lbx-gray-300); background: #fff; color: var(--lbx-gray-400); font-size: 16px; line-height: 1; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.15s; flex-shrink: 0; padding: 0; margin-left: auto; }
            .lbx-cta-remove-btn:hover { background: #fee2e2; border-color: #fca5a5; color: #dc2626; }
            .lbx-cta-add-btn.button { font-size: 11px !important; }
            /* Toggle card — makes a standalone per-slide option (e.g. hide watermark) read as
               a deliberate control instead of a checkbox lost between sections. */
            .lbx-toggle-card { display: flex; align-items: center; gap: 10px; margin-top: 12px; padding: 10px 14px; background: var(--lbx-gray-50); border: 1px solid var(--lbx-gray-200); border-radius: var(--lbx-radius-sm); cursor: pointer; transition: background 0.15s, border-color 0.15s; }
            .lbx-toggle-card:hover { border-color: var(--lbx-gray-300); background: #fff; }
            .lbx-toggle-card input[type="checkbox"] { width: 16px; height: 16px; flex-shrink: 0; margin: 0; cursor: pointer; }
            .lbx-toggle-card__icon { color: var(--lbx-gray-400); font-size: 18px; width: 18px; height: 18px; flex-shrink: 0; transition: color 0.15s; }
            .lbx-toggle-card__label { font-size: 13px; font-weight: 600; color: var(--lbx-gray-700); transition: color 0.15s; }
            /* Highlight when the option is enabled so its state is obvious at a glance. */
            .lbx-toggle-card:has(input:checked) { background: var(--lbx-primary-light); border-color: var(--lbx-primary); }
            .lbx-toggle-card:has(input:checked) .lbx-toggle-card__icon,
            .lbx-toggle-card:has(input:checked) .lbx-toggle-card__label { color: var(--lbx-primary); }

            /* Editor empty state (no slide selected) */
            .lbx-editor-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 48px 24px; background: var(--lbx-gray-50); border: 2px dashed var(--lbx-gray-200); border-radius: var(--lbx-radius); color: var(--lbx-gray-500); }
            .lbx-editor-empty .dashicons { font-size: 28px; width: 28px; height: 28px; color: var(--lbx-gray-400); margin-bottom: 8px; }
            .lbx-editor-empty p { margin: 0; font-size: 13px; }

            /* Sortable (drag & drop) */
            .lbx-sortable-hint { display: inline-flex; align-items: center; gap: 6px; margin: 0; padding: 5px 10px; background: #eff6ff; border: 1px solid #bfdbfe; border-radius: var(--lbx-radius-sm); font-size: 11px; color: #1e40af; }
            .lbx-sortable-hint .dashicons { font-size: 14px; width: 14px; height: 14px; color: #3b82f6; }
            .ui-sortable .lbx-cs-card,
            .ui-sortable .lbx-oem-preview__card { cursor: grab; }
            .ui-sortable .lbx-cs-card:active,
            .ui-sortable .lbx-oem-preview__card:active { cursor: grabbing; }
            /* Disable transitions on siblings while dragging to prevent jitter */
            .ui-sortable .lbx-cs-card,
            .ui-sortable .lbx-oem-preview__card { will-change: transform; }
            .ui-sortable-helper { opacity: 0.9; box-shadow: 0 8px 24px rgba(0,0,0,0.2); transition: none !important; }
            .lbx-sortable-placeholder { border: 2px dashed var(--lbx-primary); background: rgba(37,99,235,0.06); border-radius: 8px; box-sizing: border-box; }

            /* Scheduling */
            .lbx-schedule-section { background: #eff6ff; border: 1px solid #bfdbfe; border-radius: var(--lbx-radius); padding: 14px 16px; margin-top: 12px; }
            .lbx-schedule-section h5 { margin: 0 0 10px; font-size: 12px; font-weight: 600; color: #1e40af; text-transform: uppercase; letter-spacing: 0.03em; display: flex; align-items: center; gap: 6px; }
            .lbx-schedule-section h5 .dashicons { font-size: 16px; width: 16px; height: 16px; color: #3b82f6; }
            .lbx-schedule-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
            .lbx-schedule-field label { display: block; font-size: 11px; font-weight: 600; color: var(--lbx-gray-500); text-transform: uppercase; margin-bottom: 4px; }
            .lbx-schedule-field input[type="datetime-local"] { width: 100%; padding: 9px 12px; border: 1px solid var(--lbx-gray-300); border-radius: 8px; font-size: 13px; outline: none; transition: border-color 0.15s, box-shadow 0.15s; background-color: #fff; font-family: inherit; color: var(--lbx-gray-800); }
            .lbx-schedule-field input[type="datetime-local"]:hover { border-color: var(--lbx-gray-400); }
            .lbx-schedule-field input[type="datetime-local"]:focus { border-color: var(--lbx-primary); box-shadow: 0 0 0 3px rgba(37,99,235,0.14); }
            .lbx-schedule-hint { font-size: 11px; color: var(--lbx-gray-400); margin-top: 6px; display: flex; align-items: center; gap: 4px; }
            .lbx-schedule-hint .dashicons { font-size: 14px; width: 14px; height: 14px; }

            @media (max-width: 782px) {
                .lbx-slide-top-row { flex-direction: column; }
                .lbx-slide-top-row > div:first-child { width: 100%; }
                .lbx-slide-image { width: 100%; height: 160px; }
                .lbx-ctas-row { grid-template-columns: 1fr; }
                .lbx-sources { grid-template-columns: 1fr; }
                .lbx-cs-card { min-width: 150px; max-width: 150px; }
                .lbx-cs-card-add { min-width: 130px; max-width: 130px; }
                .lbx-cs-editor-head { flex-direction: column; align-items: flex-start; }
            }

            /* ─── Buttons ─────────────────────────────────────────── */
            .lbx-btn { display: inline-flex; align-items: center; gap: 6px; padding: 10px 20px; border-radius: var(--lbx-radius-sm); font-size: 13px; font-weight: 600; cursor: pointer; transition: all 0.15s; border: none; outline: none; text-decoration: none; }
            .lbx-btn-primary { background: linear-gradient(135deg, var(--lbx-primary), #1d4ed8); color: #fff; box-shadow: 0 1px 3px rgba(37,99,235,0.3); }
            .lbx-btn-primary:hover { background: linear-gradient(135deg, var(--lbx-primary-hover), #1e40af); box-shadow: 0 4px 12px rgba(37,99,235,0.35); }
            .lbx-btn-primary:active { transform: translateY(1px); box-shadow: 0 1px 2px rgba(37,99,235,0.3); }
            .lbx-btn-secondary { background: #fff; color: var(--lbx-gray-700); border: 1px solid var(--lbx-gray-300); }
            .lbx-btn-secondary:hover { background: var(--lbx-gray-50); border-color: var(--lbx-gray-400); }
            .lbx-btn-ghost { background: transparent; color: var(--lbx-gray-600); border: 1px solid transparent; padding: 8px 14px; }
            .lbx-btn-ghost:hover { background: var(--lbx-gray-100); }
            .lbx-btn-danger { background: transparent; color: var(--lbx-danger); border: 1px solid transparent; }
            .lbx-btn-danger:hover { background: var(--lbx-danger-bg); }
            .lbx-btn-sm { padding: 6px 12px; font-size: 12px; }
            .lbx-btn .dashicons { font-size: 16px; width: 16px; height: 16px; }

            /* ─── Add Slide Button ────────────────────────────────── */
            .lbx-add-slide-btn { width: 100%; padding: 16px; border: 2px dashed var(--lbx-gray-300); border-radius: var(--lbx-radius); background: var(--lbx-gray-50); color: var(--lbx-gray-500); font-size: 14px; font-weight: 600; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; transition: all 0.15s; }
            .lbx-add-slide-btn:hover { border-color: var(--lbx-primary); color: var(--lbx-primary); background: var(--lbx-primary-light); }
            .lbx-add-slide-btn .dashicons { font-size: 20px; width: 20px; height: 20px; }

            /* ─── Custom Slides (Smart-strip style) ───────────────── */
            .lbx-cs-strip-wrap { margin-bottom: 16px; }
            .lbx-cs-strip { display: flex; gap: 10px; overflow-x: auto; padding: 2px 2px 10px; }
            .lbx-cs-card { min-width: 180px; max-width: 180px; border: 1px solid var(--lbx-gray-200); border-radius: 8px; background: #fff; cursor: pointer; overflow: hidden; transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s; box-shadow: var(--lbx-shadow-sm); }
            .lbx-cs-card:hover { border-color: var(--lbx-gray-400); box-shadow: var(--lbx-shadow-md); transform: translateY(-1px); }
            .lbx-cs-card--active { border-color: var(--lbx-primary); box-shadow: 0 0 0 2px rgba(37,99,235,0.12), var(--lbx-shadow-md); }
            .lbx-cs-card-thumb { position: relative; height: 86px; background: var(--lbx-gray-100); display: flex; align-items: center; justify-content: center; overflow: hidden; }
            .lbx-cs-card-thumb img, .lbx-cs-card-thumb video { width: 100%; height: 100%; object-fit: cover; }
            .lbx-cs-card-thumb .dashicons { color: var(--lbx-gray-400); }
            .lbx-cs-card-badge { position: absolute; top: 5px; right: 5px; width: 22px; height: 22px; border-radius: 5px; background: rgba(0,0,0,0.55); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 0; line-height: 1; }
            .lbx-cs-card-badge .dashicons { color: #fff; font-size: 14px; width: 14px; height: 14px; }
            .lbx-cs-card-meta { padding: 8px 10px; display: flex; align-items: center; gap: 8px; }
            .lbx-cs-card-num { width: 22px; height: 22px; border-radius: 6px; background: var(--lbx-primary); color: #fff; font-size: 11px; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
            .lbx-cs-card-title { font-size: 12px; font-weight: 600; color: var(--lbx-gray-700); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1; }
            .lbx-cs-card-add { min-width: 160px; max-width: 160px; border: 2px dashed var(--lbx-gray-300); border-radius: 8px; color: var(--lbx-gray-500); background: var(--lbx-gray-50); cursor: pointer; display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 6px; }
            .lbx-cs-card-add:hover { border-color: var(--lbx-primary); color: var(--lbx-primary); background: var(--lbx-primary-light); }
            .lbx-cs-card-add .dashicons { font-size: 20px; width: 20px; height: 20px; }

            /* Empty-state: shown when a custom/extra slider has zero slides. Replaces the tiny
               "+ Add Slide" card with a friendly call-to-action that makes the next step obvious.
               Overrides the inherited `lbx-cs-card-add` width/height constraints. */
            .lbx-cs-empty.lbx-cs-card-add,
            .lbx-cs-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; padding: 48px 24px; background: linear-gradient(135deg, #f8fafc 0%, #eff6ff 100%); border: 2px dashed var(--lbx-gray-300); border-radius: 12px; text-align: center; transition: border-color 0.2s; width: 100%; max-width: 100%; min-width: 0; min-height: auto; }
            .lbx-cs-empty:hover { border-color: var(--lbx-primary); }
            .lbx-cs-empty__icon { width: 64px; height: 64px; border-radius: 50%; background: #fff; border: 1px solid var(--lbx-gray-200); display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 12px rgba(0,0,0,0.05); }
            .lbx-cs-empty__icon .dashicons { font-size: 32px; width: 32px; height: 32px; color: var(--lbx-primary); }
            .lbx-cs-empty__title { margin: 0; font-size: 16px; font-weight: 700; color: var(--lbx-gray-800); }
            .lbx-cs-empty__desc { margin: 0; font-size: 13px; color: var(--lbx-gray-500); max-width: 420px; line-height: 1.45; }
            .lbx-cs-empty__btn { display: inline-flex; align-items: center; gap: 8px; padding: 10px 20px; background: var(--lbx-primary); color: #fff; border: none; border-radius: var(--lbx-radius-sm); font-size: 13px; font-weight: 600; cursor: pointer; transition: background 0.15s, transform 0.1s; margin-top: 4px; }
            .lbx-cs-empty__btn:hover { background: #1e40af; }
            .lbx-cs-empty__btn:active { transform: translateY(1px); }
            .lbx-cs-empty__btn .dashicons { font-size: 16px; width: 16px; height: 16px; }
            /* Compact variant — used for secondary empty states (e.g. Additional Slides
               inside the OEM editor, where the parent section already has a heading and
               description above). Shrinks padding, icon, and typography. */
            .lbx-cs-empty.lbx-cs-empty--sm { padding: 18px 16px; gap: 8px; border-radius: 10px; flex-direction: row; text-align: left; flex-wrap: wrap; }
            .lbx-cs-empty.lbx-cs-empty--sm .lbx-cs-empty__icon { width: 36px; height: 36px; box-shadow: none; flex-shrink: 0; }
            .lbx-cs-empty.lbx-cs-empty--sm .lbx-cs-empty__icon .dashicons { font-size: 18px; width: 18px; height: 18px; }
            .lbx-cs-empty.lbx-cs-empty--sm .lbx-cs-empty__title { font-size: 13px; font-weight: 600; }
            .lbx-cs-empty.lbx-cs-empty--sm .lbx-cs-empty__desc { font-size: 12px; line-height: 1.4; flex: 1 1 220px; max-width: none; }
            .lbx-cs-empty.lbx-cs-empty--sm .lbx-cs-empty__btn { padding: 6px 12px; font-size: 12px; margin-top: 0; margin-left: auto; }
            .lbx-cs-empty.lbx-cs-empty--sm .lbx-cs-empty__btn .dashicons { font-size: 14px; width: 14px; height: 14px; }
            .lbx-cs-editor-wrap { border: 1px solid var(--lbx-gray-200); border-radius: var(--lbx-radius); background: #fff; padding: 14px; }
            .lbx-cs-editor-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 12px; }
            .lbx-cs-editor-title { margin: 0; font-size: 14px; font-weight: 700; color: var(--lbx-gray-800); }
            .lbx-cs-editor-actions { display: inline-flex; gap: 6px; }
            .lbx-cs-editor-actions button { background: #fff; border: 1px solid var(--lbx-gray-300); border-radius: 6px; width: 30px; height: 30px; color: var(--lbx-gray-600); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; }
            .lbx-cs-editor-actions button:hover { border-color: var(--lbx-gray-400); background: var(--lbx-gray-50); }
            .lbx-cs-editor-actions .lbx-cs-remove:hover { border-color: #fecaca; background: var(--lbx-danger-bg); color: var(--lbx-danger); }
            /* Additional-slide remove (trash) reads as destructive: red icon + red border, intensifies on hover. */
            .lbx-cs-editor-actions .lbx-ex-remove { border-color: #fecaca; color: var(--lbx-danger); }
            .lbx-cs-editor-actions .lbx-ex-remove:hover { border-color: var(--lbx-danger); background: var(--lbx-danger-bg); color: var(--lbx-danger); }
            .lbx-cs-editor-body { display: flex; flex-direction: column; gap: 14px; }

            /* ─── Existing Sliders Table ──────────────────────────── */
            .lbx-table { width: 100%; border-collapse: separate; border-spacing: 0; }
            .lbx-table th { padding: 10px 16px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--lbx-gray-500); border-bottom: 2px solid var(--lbx-gray-200); text-align: left; background: var(--lbx-gray-50); }
            .lbx-table td { padding: 14px 16px; font-size: 13px; color: var(--lbx-gray-700); border-bottom: 1px solid var(--lbx-gray-100); vertical-align: middle; }
            .lbx-table tr:last-child td { border-bottom: none; }
            .lbx-table tr:hover td { background: var(--lbx-primary-lighter); }
            .lbx-table-name { font-weight: 600; color: var(--lbx-gray-900); }

            /* Source badge */
            .lbx-badge { display: inline-flex; align-items: center; gap: 4px; padding: 3px 10px; border-radius: 999px; font-size: 11px; font-weight: 600; }
            .lbx-badge-minerva { background: #eff6ff; color: #2563eb; }
            .lbx-badge-oem { background: #eff6ff; color: #2563eb; }
            .lbx-badge-custom { background: #f0fdf4; color: #16a34a; }
            .lbx-badge-countdown { background: #fefce8; color: #ca8a04; }

            /* Status toggle (mini) */
            .lbx-status-toggle { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
            .lbx-status-dot { width: 8px; height: 8px; border-radius: 50%; }
            .lbx-status-dot.on { background: var(--lbx-success); box-shadow: 0 0 0 3px rgba(5,150,105,0.15); }
            .lbx-status-dot.off { background: var(--lbx-gray-400); }
            .lbx-status-text { font-size: 12px; font-weight: 500; }

            .lbx-slider-id { font-family: monospace; font-size: 11px; padding: 3px 8px; background: var(--lbx-gray-100); border-radius: 4px; color: var(--lbx-gray-600); }

            /* Badge count */
            .lbx-count { display: inline-flex; align-items: center; justify-content: center; min-width: 22px; height: 22px; padding: 0 6px; background: var(--lbx-primary); color: #fff; border-radius: 11px; font-size: 11px; font-weight: 700; }
            .lbx-count-gray { background: var(--lbx-gray-200); color: var(--lbx-gray-600); }

            /* ─── Section titles & dividers ──────────────────────── */
            .lbx-section-divider { border: none; border-top: 1px solid var(--lbx-gray-200); margin: 28px 0; }
            .lbx-section-title { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
            .lbx-section-title h3 { margin: 0; font-size: 15px; font-weight: 600; color: var(--lbx-gray-800); }

            /* ─── Empty state ─────────────────────────────────────── */
            .lbx-empty { text-align: center; padding: 40px 20px; color: var(--lbx-gray-400); }
            .lbx-empty .dashicons { font-size: 40px; width: 40px; height: 40px; display: block; margin: 0 auto 12px; }
            .lbx-empty p { margin: 0; font-size: 14px; }

            /* ─── Countdown BG image preview ─────────────────────── */
            .lbx-cd-bg-wrap { max-width: 320px; }

            /* ─── Section pulse (draws attention to the slides area after the user
               picks a slider Type). Adaptive: only auto-scrolls when the section
               isn't already visible enough — see source-card click handler. */
            @keyframes lbx-section-pulse {
                0%   { box-shadow: 0 0 0 0 rgba(37, 99, 235, 0); }
                20%  { box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.22), 0 18px 44px -8px rgba(37, 99, 235, 0.32); }
                100% { box-shadow: 0 0 0 0 rgba(37, 99, 235, 0); }
            }
            .lbx-section-pulse { border-radius: 14px; animation: lbx-section-pulse 1.8s cubic-bezier(0.4, 0, 0.2, 1); }

            /* ─── Panel Group (generic categoriser for settings) ──────────
               Shared visual shell used by the Countdown, Design and General tabs to
               split a long vertical list of fields into labelled cards. Each group
               has a pill-style heading with a dashicon and the rest is up to the
               content — .lbx-panel-grid provides a 2-col responsive grid. */
            .lbx-panel-group { padding: 18px 20px; background: #fff; border: 1px solid var(--lbx-gray-200); border-radius: 12px; box-shadow: 0 1px 2px rgba(0,0,0,0.03); margin-top: 16px; }
            .lbx-panel-group:first-of-type { margin-top: 8px; }
            .lbx-panel-group__title { margin: 0 0 14px; font-size: 12px; font-weight: 700; color: var(--lbx-primary); text-transform: uppercase; letter-spacing: 0.06em; display: inline-flex; align-items: center; gap: 8px; padding: 4px 10px; background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 999px; }
            .lbx-panel-group__title .dashicons { font-size: 14px; width: 14px; height: 14px; color: var(--lbx-primary); }
            .lbx-panel-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
            .lbx-panel-grid--3col { grid-template-columns: 1fr 1fr 1fr; }
            @media (max-width: 782px) { .lbx-panel-grid, .lbx-panel-grid--3col { grid-template-columns: 1fr; } }
            .lbx-cd-bg-thumb { position: relative; border-radius: 8px; overflow: hidden; border: 1px solid var(--lbx-gray-200); }
            .lbx-cd-bg-thumb img { width: 100%; height: 140px; object-fit: cover; display: block; cursor: pointer; }
            .lbx-cd-bg-remove { position: absolute; top: 6px; right: 6px; width: 24px; height: 24px; border-radius: 50%; background: rgba(0,0,0,.6); color: #fff; border: none; cursor: pointer; font-size: 16px; line-height: 24px; text-align: center; padding: 0; transition: background .15s; }
            .lbx-cd-bg-remove:hover { background: var(--lbx-danger); }
            .lbx-cd-bg-empty { padding: 20px 0; }

            /* ─── OEM Preview grid ────────────────────────────────── */
            .lbx-oem-preview { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px; padding: 2px; }
            .lbx-oem-preview__loading { grid-column: 1 / -1; display: flex; align-items: center; justify-content: center; padding: 30px 0; color: var(--lbx-gray-500); font-size: 13px; }
            .lbx-oem-preview__empty { grid-column: 1 / -1; text-align: center; padding: 30px 0; color: var(--lbx-gray-400); font-size: 13px; }
            .lbx-oem-preview__card { border: 1px solid var(--lbx-gray-200); border-radius: 8px; overflow: hidden; background: #fff; transition: box-shadow .15s; display: flex; flex-direction: column; }
            .lbx-oem-preview__card:hover { box-shadow: 0 2px 8px rgba(0,0,0,.08); }
            .lbx-oem-preview__card img,
            .lbx-oem-preview__card video { width: 100%; height: 80px; object-fit: cover; display: block; background: var(--lbx-gray-100); }
            /* Override badge: pinned to the top-left corner so it doesn't overlap the edit pencil (bottom-right) or the visibility/align overlay (top, full-width). */
            .lbx-oem-preview__override-badge { position: absolute; bottom: 6px; left: 6px; background: var(--lbx-primary); color: #fff; width: 22px; height: 22px; border-radius: 50%; display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 6px rgba(0,0,0,0.2); z-index: 3; pointer-events: none; }
            .lbx-oem-preview__override-badge .dashicons { font-size: 12px; width: 12px; height: 12px; }
            .lbx-oem-preview__thumb-col { position: relative; }
            .lbx-oem-preview__card-noimg { width: 100%; height: 80px; display: flex; align-items: center; justify-content: center; background: var(--lbx-gray-100); color: var(--lbx-gray-400); }
            .lbx-oem-preview__card-noimg .dashicons { font-size: 24px; width: 24px; height: 24px; }
            /* Split thumb (EN | FR) + bilingual badge for additional slides with two images (LBT-1489). */
            .lbx-oem-preview__thumb-split { position: relative; display: flex; width: 100%; height: 80px; background: var(--lbx-gray-100); }
            .lbx-oem-preview__half { position: relative; flex: 1 1 50%; min-width: 0; background-size: cover; background-position: center; }
            .lbx-oem-preview__half + .lbx-oem-preview__half { border-left: 2px solid #fff; }
            .lbx-oem-preview__half i { position: absolute; bottom: 3px; left: 3px; font-style: normal; font-size: 8px; font-weight: 700; letter-spacing: 0.04em; color: #fff; background: rgba(0,0,0,0.55); padding: 0 4px; border-radius: 3px; line-height: 1.5; }
            .lbx-oem-preview__half:last-child i { left: auto; right: 3px; }
            .lbx-oem-preview__lang-badge { position: absolute; top: 5px; right: 5px; z-index: 3; font-size: 8px; font-weight: 700; letter-spacing: 0.03em; color: #fff; background: rgba(124,58,237,0.92); padding: 2px 6px; border-radius: 999px; box-shadow: 0 1px 3px rgba(0,0,0,0.3); pointer-events: none; }
            .lbx-oem-preview__card-body { padding: 6px 10px 8px; display: flex; flex-direction: column; flex: 1 1 auto; }
            .lbx-oem-preview__card-title { font-size: 11px; font-weight: 600; color: var(--lbx-gray-800); margin: 0 0 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.3; }
            .lbx-oem-preview__card-sub { font-size: 10px; color: var(--lbx-gray-500); margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.3; }

            /* ─── Footer actions ──────────────────────────────────── */
            .lbx-card-footer { padding: 16px 24px; border-top: 1px solid var(--lbx-gray-200); background: var(--lbx-gray-50); display: flex; align-items: center; gap: 12px; }
            /* Sticky footer for the slider editor form — keeps Save accessible while scrolling. */
            .lbx-card--editor { overflow: visible; }
            /* Trim the top breathing room in the editor so the first section sits higher. */
            .lbx-card--editor > .lbx-card-body { padding-top: 16px; }
            .lbx-card--editor > .lbx-card-footer { display: grid; grid-template-columns: 3fr 1fr; position: sticky; bottom: 0; z-index: 20; background: rgba(249,250,251,0.92); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); box-shadow: 0 -4px 12px -4px rgba(0,0,0,0.08); border-bottom-left-radius: var(--lbx-radius); border-bottom-right-radius: var(--lbx-radius); }
            /* Full-width save/cancel: Save dominant (3fr), Cancel a real bordered+shadowed button (1fr). */
            .lbx-card--editor > .lbx-card-footer .lbx-btn { width: 100%; justify-content: center; padding-top: 11px; padding-bottom: 11px; }
            .lbx-card--editor > .lbx-card-footer .lbx-btn-secondary { box-shadow: 0 1px 2px rgba(0,0,0,0.06); }
            .lbx-card--editor > .lbx-card-footer .lbx-btn-secondary:hover { box-shadow: 0 2px 5px rgba(0,0,0,0.10); }

            /* ─── Dashboard Grid ──────────────────────────────────── */
            .lbx-dashboard { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 20px; }
            .lbx-dash-card { background: #fff; border: 1px solid var(--lbx-gray-200); border-radius: var(--lbx-radius); overflow: hidden; cursor: pointer; transition: all 0.2s; position: relative; box-shadow: var(--lbx-shadow-md); display: flex; flex-direction: column; }
            .lbx-dash-card:hover { box-shadow: 0 12px 24px -4px rgba(0,0,0,0.12), 0 4px 8px -2px rgba(0,0,0,0.06); border-color: var(--lbx-primary); transform: translateY(-2px); }
            .lbx-dash-card__thumb { width: 100%; height: 170px; background: linear-gradient(135deg, var(--lbx-gray-100), var(--lbx-gray-50)); display: flex; align-items: center; justify-content: center; position: relative; overflow: hidden; }
            .lbx-dash-card__thumb-icon { width: 64px; height: 64px; border-radius: 16px; display: flex; align-items: center; justify-content: center; font-size: 28px; }
            .lbx-dash-card__thumb-icon .dashicons { font-size: 28px; width: 28px; height: 28px; }
            .lbx-dash-card__thumb-icon--minerva { background: linear-gradient(135deg, #dbeafe, #bfdbfe); color: #2563eb; }
            .lbx-dash-card__thumb-icon--custom { background: linear-gradient(135deg, #dcfce7, #bbf7d0); color: #16a34a; }
            .lbx-dash-card__thumb-icon--countdown { background: linear-gradient(135deg, #fef08a, #fde68a); color: #ca8a04; }
            .lbx-dash-card__status { position: absolute; top: 12px; right: 12px; padding: 4px 10px 4px 8px; border-radius: 999px; font-size: 11px; font-weight: 600; display: inline-flex; align-items: center; gap: 4px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
            .lbx-dash-card__status .dashicons { font-size: 13px; width: 13px; height: 13px; }
            .lbx-dash-card__status--on { background: rgba(5,150,105,0.95); color: #fff; }
            .lbx-dash-card__status--off { background: rgba(107,114,128,0.95); color: #fff; }
            .lbx-dash-card__status--expired { background: rgba(220,38,38,0.95); color: #fff; }
            .lbx-dash-card__status--scheduled { background: rgba(37,99,235,0.95); color: #fff; }
            /* Schedule info line under meta badges */
            .lbx-dash-card__schedule { display: inline-flex; align-items: center; gap: 5px; margin-top: 8px; padding: 5px 10px; border-radius: var(--lbx-radius-sm); font-size: 11px; font-weight: 500; }
            .lbx-dash-card__schedule .dashicons { font-size: 13px; width: 13px; height: 13px; }
            .lbx-dash-card__schedule--expires { background: #fef3c7; color: #92400e; }
            .lbx-dash-card__schedule--expired { background: #fee2e2; color: #991b1b; }
            .lbx-dash-card__schedule--scheduled { background: #dbeafe; color: #1e40af; }
            .lbx-dash-card__body { padding: 16px 18px; flex: 1; }
            .lbx-dash-card__name { margin: 0 0 6px; font-size: 15px; font-weight: 700; color: var(--lbx-gray-900); }
            .lbx-dash-card__meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 12px; color: var(--lbx-gray-500); }
            .lbx-badge-layout { background: #f3e8ff; color: #7c3aed; }
            .lbx-badge-slides { background: #fef3c7; color: #b45309; }
            .lbx-dash-card__id { font-family: monospace; font-size: 11px; color: var(--lbx-gray-400); margin-top: 6px; display: block; }
            .lbx-dash-card__actions { display: flex; gap: 6px; padding: 0 18px 16px; }
            .lbx-dash-card__actions .lbx-btn { flex: 1; justify-content: center; font-size: 12px; padding: 7px 10px; }
            /* ─── Fan Stack Preview ───────────────────────────────── */
            .lbx-fan { position: relative; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; }
            .lbx-fan__img { position: absolute; width: 110px; height: 74px; object-fit: cover; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.18), 0 0 0 1px rgba(255,255,255,0.8); transition: transform 0.3s ease, box-shadow 0.3s ease; }
            /* 1 image — centered */
            .lbx-fan--1 .lbx-fan__img--0 { transform: rotate(0deg); z-index: 3; width: 140px; height: 94px; }
            /* 2 images */
            .lbx-fan--2 .lbx-fan__img--0 { transform: rotate(-8deg) translateX(-50px); z-index: 2; }
            .lbx-fan--2 .lbx-fan__img--1 { transform: rotate(8deg) translateX(50px); z-index: 3; }
            /* 3 images — horizontal fan, pivot from bottom center */
            .lbx-fan--3 .lbx-fan__img--0 { transform: rotate(-14deg) translateX(-72px) translateY(2px); z-index: 1; }
            .lbx-fan--3 .lbx-fan__img--1 { transform: rotate(0deg); z-index: 3; width: 118px; height: 80px; }
            .lbx-fan--3 .lbx-fan__img--2 { transform: rotate(14deg) translateX(72px) translateY(2px); z-index: 2; }
            /* 4 images — wide horizontal fan */
            .lbx-fan--4 .lbx-fan__img--0 { transform: rotate(-16deg) translateX(-90px) translateY(4px); z-index: 1; width: 100px; height: 68px; }
            .lbx-fan--4 .lbx-fan__img--1 { transform: rotate(-5deg) translateX(-32px); z-index: 2; }
            .lbx-fan--4 .lbx-fan__img--2 { transform: rotate(5deg) translateX(32px); z-index: 3; }
            .lbx-fan--4 .lbx-fan__img--3 { transform: rotate(16deg) translateX(90px) translateY(4px); z-index: 1; width: 100px; height: 68px; }
            /* Hover: slight lift on top card */
            .lbx-dash-card:hover .lbx-fan__img { box-shadow: 0 4px 14px rgba(0,0,0,0.22), 0 0 0 1px rgba(255,255,255,0.9); }
            .lbx-dash-card:hover .lbx-fan--3 .lbx-fan__img--1,
            .lbx-dash-card:hover .lbx-fan--2 .lbx-fan__img--1,
            .lbx-dash-card:hover .lbx-fan--1 .lbx-fan__img--0 { transform: rotate(0deg) translateY(-8px) scale(1.05); }
            .lbx-dash-card:hover .lbx-fan--4 .lbx-fan__img--2 { transform: rotate(5deg) translateX(32px) translateY(-8px) scale(1.04); }

            .lbx-dash-new { background: #fff; border: 2px dashed var(--lbx-gray-300); border-radius: var(--lbx-radius); display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 260px; cursor: pointer; transition: all 0.2s; color: var(--lbx-gray-400); text-decoration: none; }
            .lbx-dash-new:hover { border-color: var(--lbx-primary); color: var(--lbx-primary); background: var(--lbx-primary-light); }
            .lbx-dash-new .dashicons { font-size: 36px; width: 36px; height: 36px; margin-bottom: 10px; }
            .lbx-dash-new span { font-size: 14px; font-weight: 600; }

            /* ─── Layout & Design Section ─────────────────────────── */
            .lbx-design-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 20px; }
            /* Sub-section heading inside a panel (e.g. Behavior / Appearance) — quietly sections the form. */
            .lbx-subgroup-title { margin: 22px 0 14px; padding-bottom: 7px; border-bottom: 1px solid var(--lbx-gray-200); font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--lbx-gray-400); }
            .lbx-design-grid--2col { grid-template-columns: 1fr 1fr; }
            @media (max-width: 782px) { .lbx-design-grid, .lbx-design-grid--2col { grid-template-columns: 1fr; } }

            /* ─── Modern per-breakpoint stepper widget ──────────────────
               Replaces the raw number inputs for Media Columns and Slides per
               view. Each card groups a device icon + label + stepper controls. */
            .lbx-bp-heading { display: block; font-size: 13px; font-weight: 600; color: var(--lbx-gray-700); margin-bottom: 12px; }
            .lbx-bp-heading .lbx-label-hint { font-weight: 400; color: var(--lbx-gray-400); font-size: 12px; margin-left: 4px; }
            .lbx-bp-fields { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
            .lbx-bp-field { padding: 14px 16px; background: var(--lbx-gray-50); border: 1px solid var(--lbx-gray-200); border-radius: 10px; display: flex; align-items: center; justify-content: space-between; gap: 12px; transition: border-color 0.15s, box-shadow 0.15s, background 0.15s; }
            .lbx-bp-field:hover { border-color: var(--lbx-primary); box-shadow: 0 0 0 3px rgba(37,99,235,0.08); background: #fff; }
            .lbx-bp-field__head { display: flex; align-items: center; gap: 10px; min-width: 0; }
            .lbx-bp-field__head .dashicons { font-size: 20px; width: 20px; height: 20px; color: var(--lbx-primary); flex-shrink: 0; }
            .lbx-bp-field__meta { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
            .lbx-bp-field__name { font-size: 12px; font-weight: 700; color: var(--lbx-gray-800); text-transform: uppercase; letter-spacing: 0.04em; }
            .lbx-bp-field__range { font-size: 11px; color: var(--lbx-gray-500); margin-top: 2px; }

            .lbx-stepper { display: inline-flex; align-items: stretch; border: 1px solid var(--lbx-gray-300); border-radius: 8px; background: #fff; overflow: hidden; flex-shrink: 0; }
            .lbx-stepper:focus-within { border-color: var(--lbx-primary); box-shadow: 0 0 0 3px rgba(37,99,235,0.12); }
            .lbx-stepper__btn { width: 30px; height: 32px; border: none; background: transparent; color: var(--lbx-gray-600); cursor: pointer; font-size: 18px; font-weight: 600; line-height: 1; padding: 0; transition: background 0.15s, color 0.15s; display: inline-flex; align-items: center; justify-content: center; font-family: inherit; }
            .lbx-stepper__btn:hover:not(:disabled) { background: #eff6ff; color: var(--lbx-primary); }
            .lbx-stepper__btn:disabled { opacity: 0.35; cursor: not-allowed; }
            .lbx-stepper__input { width: 42px; height: 32px; border: none; border-left: 1px solid var(--lbx-gray-200); border-right: 1px solid var(--lbx-gray-200); text-align: center; font-size: 14px; font-weight: 600; color: var(--lbx-gray-800); background: transparent; padding: 0; -moz-appearance: textfield; font-family: inherit; box-shadow: none !important; outline: none; border-radius: 0; }
            .lbx-stepper__input::-webkit-outer-spin-button,
            .lbx-stepper__input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
            .lbx-stepper__input:focus { background: #eff6ff; border-color: var(--lbx-gray-200); outline: none; box-shadow: none !important; }

            @media (max-width: 782px) {
                .lbx-bp-fields { grid-template-columns: 1fr; gap: 10px; }
            }
            .lbx-radio-cards { display: flex; gap: 8px; flex-wrap: wrap; }
            .lbx-radio-card { display: flex; align-items: center; gap: 6px; padding: 8px 14px; border: 1px solid var(--lbx-gray-300); border-radius: var(--lbx-radius-sm); cursor: pointer; transition: all 0.15s; font-size: 13px; color: var(--lbx-gray-600); }
            .lbx-radio-card:hover { border-color: var(--lbx-gray-400); background: var(--lbx-gray-50); }
            .lbx-radio-card.active { border-color: var(--lbx-primary); background: var(--lbx-primary-light); color: var(--lbx-primary); font-weight: 600; }
            .lbx-radio-card input { display: none; }
            .lbx-radio-card .dashicons { font-size: 16px; width: 16px; height: 16px; }

            /* ─── Per-Slide Controls (OEM) ────────────────────────── */
            .lbx-oem-preview__card { position: relative; }
            .lbx-oem-preview__card--hidden { opacity: 0.45; }
            .lbx-oem-preview__card--hidden img { filter: grayscale(0.6); }
            /* Per-slide responsive visibility toggles (📱 / 📑 / 🖥) in the card body. */
            .lbx-oem-preview__res { display: inline-flex; gap: 3px; margin-top: auto; padding-top: 6px; }
            .lbx-res-toggle { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; padding: 0; border: 1px solid var(--lbx-gray-300, #d1d5db); border-radius: 5px; background: var(--lbx-gray-100, #f3f4f6); color: var(--lbx-gray-400, #9ca3af); cursor: pointer; transition: all 0.12s; }
            .lbx-res-toggle .dashicons { font-size: 14px; width: 14px; height: 14px; line-height: 1; }
            .lbx-res-toggle:hover { border-color: var(--lbx-primary, #2563eb); }
            .lbx-res-toggle.is-on { background: var(--lbx-primary, #2563eb); border-color: var(--lbx-primary, #2563eb); color: #fff; }
            .lbx-res-toggle:not(.is-on) { text-decoration: line-through; opacity: 0.7; }
            .lbx-oem-preview__card--res-off { opacity: 0.5; }
            .lbx-oem-preview__card--res-off img { filter: grayscale(0.5); }
            .lbx-oem-preview__overlay { position: absolute; top: 0; left: 0; right: 0; display: flex; align-items: center; justify-content: space-between; gap: 6px; padding: 8px 9px; z-index: 2; opacity: 0; transition: opacity 0.2s; }
            .lbx-oem-preview__card:hover .lbx-oem-preview__overlay { opacity: 1; }
            .lbx-oem-preview__vis { display: inline-flex; align-items: center; gap: 4px; cursor: pointer; background: rgba(255,255,255,0.92); backdrop-filter: blur(6px); padding: 2px 6px 2px 3px; border-radius: 12px; box-shadow: 0 1px 4px rgba(0,0,0,0.15); transition: box-shadow 0.15s; line-height: 1; }
            .lbx-oem-preview__vis:hover { box-shadow: 0 2px 8px rgba(0,0,0,0.2); }
            /* Visibility checkbox — uses an SVG checkmark as background-image when
               checked. Sub-pixel rendering of CSS-only checkmarks gets unreliable at
               14px and below, so the SVG (drawn inside the box's own viewBox) is
               the cleanest way to guarantee perfect centering across browsers. */
            .lbx-oem-preview__vis input[type="checkbox"] { appearance: none; -webkit-appearance: none; width: 14px; height: 14px; border: 1px solid var(--lbx-gray-400); border-radius: 3px; cursor: pointer; margin: 0; padding: 0; background: #fff; background-repeat: no-repeat; background-position: center center; background-size: 100% 100%; position: relative; flex-shrink: 0; transition: background-color 0.15s, border-color 0.15s; box-sizing: border-box; min-height: 0; vertical-align: middle; }
            .lbx-oem-preview__vis input[type="checkbox"]:checked { background-color: var(--lbx-primary); border-color: var(--lbx-primary); background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14' fill='none' stroke='%23ffffff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='3 7.2 5.8 10 11 4'/%3E%3C/svg%3E"); }
            .lbx-oem-preview__vis input[type="checkbox"]:focus-visible { outline: 2px solid rgba(37,99,235,0.4); outline-offset: 1px; }
            .lbx-oem-preview__vis-label { font-size: 9px; line-height: 1; color: var(--lbx-gray-700); font-weight: 600; user-select: none; }
            /* Bumped specificity (`.lbx-oem-preview__overlay select.lbx-oem-preview__align`)
               so we win over WP admin's `.wp-core-ui select` rule that otherwise inflates
               the height (line-height: 2) and font-size (14px). */
            .lbx-oem-preview__overlay select.lbx-oem-preview__align,
            select.lbx-oem-preview__align { appearance: none; -webkit-appearance: none; -moz-appearance: none; box-sizing: border-box; height: 18px; min-height: 0; line-height: 1; margin: 0; background: rgba(255,255,255,0.92); backdrop-filter: blur(6px); border: none; border-radius: 12px; padding: 2px 16px 2px 6px; font-size: 9px; font-weight: 600; color: var(--lbx-gray-700); font-family: inherit; cursor: pointer; outline: none; box-shadow: 0 1px 4px rgba(0,0,0,0.15); transition: box-shadow 0.15s; background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 24 24" fill="none" stroke="%236b7280" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>'); background-repeat: no-repeat; background-position: right 5px center; background-size: 8px 8px; vertical-align: middle; }
            .lbx-oem-preview__overlay select.lbx-oem-preview__align:hover,
            select.lbx-oem-preview__align:hover { box-shadow: 0 2px 8px rgba(0,0,0,0.2); }
            .lbx-oem-preview__overlay select.lbx-oem-preview__align:focus,
            select.lbx-oem-preview__align:focus { box-shadow: 0 0 0 2px rgba(37,99,235,0.3); }
            .lbx-oem-preview__card--hidden .lbx-oem-preview__overlay { opacity: 1; }

            /* ─── OEM Edit Button on Card ────────────────────────── */
            .lbx-oem-preview__thumb-col { position: relative; }
            .lbx-oem-preview__edit-btn { position: absolute; bottom: 6px; right: 6px; width: 24px; height: 24px; background: rgba(255,255,255,0.92); backdrop-filter: blur(6px); border: none; border-radius: 50%; cursor: pointer; box-shadow: 0 1px 4px rgba(0,0,0,0.15); display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.2s, box-shadow 0.15s; z-index: 2; }
            .lbx-oem-preview__card:hover .lbx-oem-preview__edit-btn { opacity: 1; }
            .lbx-oem-preview__edit-btn:hover { box-shadow: 0 2px 8px rgba(0,0,0,0.2); }
            .lbx-oem-preview__edit-btn .dashicons { font-size: 14px; width: 14px; height: 14px; color: var(--lbx-gray-600); }

            /* ─── Slide Type Badge (OEM vs Additional in unified list) ─
               Rendered as a corner ribbon: flat top-left edge that hugs the card
               corner (clipped to the card's border-radius via overflow:hidden),
               rounded only on the bottom-right so it reads as a label flag rather
               than another pill chip stacked on top of the visibility control. */
            .lbx-oem-preview__type-badge { position: absolute; top: 0; left: 0; z-index: 3; padding: 3px 9px 3px 7px; border-radius: 0 0 10px 0; font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: #fff; box-shadow: 1px 1px 4px rgba(0,0,0,0.18); pointer-events: none; line-height: 1.3; }
            .lbx-oem-preview__type-badge--minerva { background: rgba(37, 99, 235, 0.95); }
            .lbx-oem-preview__type-badge--extra { background: rgba(217, 119, 6, 0.95); }
            .lbx-oem-preview__type-badge--custom { background: rgba(124, 58, 237, 0.95); }
            .lbx-oem-preview__type-badge--static { background: rgba(13, 148, 136, 0.95); }

            /* Auto-populate switches bar (LBT-1472) — two independent toggles */
            .lbx-autopop-bar { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 14px; padding: 12px 16px; margin: 0 0 16px; background: var(--lbx-primary-light, #eff6ff); border: 1px solid #bfdbfe; border-radius: var(--lbx-radius, 10px); }
            /* Each source is a compact white pill chip, centered in the bar. */
            .lbx-autopop-bar .lbx-toggle { background: #fff; border: 1px solid #dbeafe; border-radius: 9999px; padding: 8px 18px 8px 12px; gap: 10px; box-shadow: 0 1px 2px rgba(0,0,0,0.04); transition: border-color 0.15s, box-shadow 0.15s; }
            .lbx-autopop-bar .lbx-toggle:hover { border-color: #93c5fd; box-shadow: 0 2px 8px rgba(37,99,235,0.10); }
            .lbx-autopop-bar .lbx-toggle-icon { font-size: 16px; width: 16px; height: 16px; color: var(--lbx-primary); flex-shrink: 0; }
            .lbx-autopop-bar .lbx-toggle-label { font-size: 13px; font-weight: 600; color: var(--lbx-gray-800); }
            /* Make filter (multi-make dealers) — pill select matching the autopop toggles */
            .lbx-autopop-bar .lbx-make-filter { display: inline-flex; align-items: center; gap: 6px; background: #fff; border: 1px solid #dbeafe; border-radius: 9999px; padding: 6px 10px 6px 12px; box-shadow: 0 1px 2px rgba(0,0,0,0.04); transition: border-color 0.15s, box-shadow 0.15s; cursor: pointer; }
            .lbx-autopop-bar .lbx-make-filter:hover { border-color: #93c5fd; box-shadow: 0 2px 8px rgba(37,99,235,0.10); }
            .lbx-autopop-bar .lbx-make-filter .dashicons { color: var(--lbx-primary); font-size: 16px; width: 16px; height: 16px; }
            .lbx-autopop-bar .lbx-make-filter select { border: 0; background: transparent; font-size: 13px; font-weight: 600; color: var(--lbx-gray-800); cursor: pointer; box-shadow: none; min-height: 0; padding: 0 24px 0 0; }
            .lbx-autopop-bar .lbx-make-filter select:focus { outline: none; box-shadow: none; }
            /* "N hidden by make filter" pill next to the Slides count */
            .lbx-make-filter-note { font-size: 12px; font-weight: 600; color: #92400e; background: #fef3c7; border: 1px solid #fde68a; border-radius: 9999px; padding: 2px 10px; margin-left: 8px; }
            .lbx-autopop-bar .lbx-label-hint { font-weight: 400; color: var(--lbx-gray-500); }
            .lbx-autopop-bar__hint { font-size: 11px; color: var(--lbx-gray-500); display: inline-flex; align-items: center; }

            /* Busy overlay for OEM/Static fetches — sits over the grid while loading. */
            .lbx-oem-preview-wrap { position: relative; min-height: 90px; }
            .lbx-oem-preview__busy {
                position: absolute; inset: 0; z-index: 6;
                display: none; align-items: center; justify-content: center; gap: 10px;
                background: rgba(255, 255, 255, 0.78);
                -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
                border-radius: var(--lbx-radius, 10px);
                font-size: 13px; font-weight: 500; color: var(--lbx-gray-600);
                animation: lbxBusyFade 0.12s ease;
            }
            .lbx-oem-preview__busy.is-on { display: flex; }
            @keyframes lbxBusyFade { from { opacity: 0; } to { opacity: 1; } }
            /* Push the overlay row below the ribbon so the visibility chip + alignment
               selector sit on their own line. Ribbon ends ~18px from top; +8px gap. */
            .lbx-oem-preview__card[data-type="minerva"] .lbx-oem-preview__overlay,
            .lbx-oem-preview__card[data-type="static"] .lbx-oem-preview__overlay { padding-top: 26px; }

            /* ─── Remove button on extra cards (no hide/align — extras are removed, not hidden) ─ */
            .lbx-oem-preview__remove-btn { position: absolute; bottom: 6px; right: 36px; width: 24px; height: 24px; background: rgba(239, 68, 68, 0.92); backdrop-filter: blur(6px); border: none; border-radius: 50%; cursor: pointer; box-shadow: 0 1px 4px rgba(0,0,0,0.15); display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.2s, box-shadow 0.15s; z-index: 2; color: #fff; }
            .lbx-oem-preview__card:hover .lbx-oem-preview__remove-btn { opacity: 1; }
            .lbx-oem-preview__remove-btn:hover { background: rgba(220, 38, 38, 1); box-shadow: 0 2px 8px rgba(0,0,0,0.2); }
            .lbx-oem-preview__remove-btn .dashicons { font-size: 14px; width: 14px; height: 14px; }

            /* Style for the custom-mode badge inside the .lbx-cs-card strip — same
               corner-ribbon treatment as the OEM/Additional badge for consistency. */
            .lbx-cs-card { position: relative; }
            .lbx-cs-card-type-badge { position: absolute; top: 0; left: 0; z-index: 3; padding: 2px 8px 2px 6px; border-radius: 0 0 8px 0; font-size: 8px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: #fff; background: rgba(124, 58, 237, 0.95); box-shadow: 1px 1px 3px rgba(0,0,0,0.18); pointer-events: none; line-height: 1.4; }

            /* ─── OEM Edit Modal ──────────────────────────────────── */
            .lbx-oem-modal-backdrop { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.45); z-index: 100000; align-items: center; justify-content: center; padding: 30px; }
            .lbx-oem-modal-backdrop.is-open { display: flex; }

            /* ─── Slide Edit Modal (Custom + Additional) ─────────────
               Hosts the per-slide editor so the user can close it instead of having
               a persistent inline panel. Same size/scroll contract as the OEM modal. */
            .lbx-edit-modal-backdrop { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.45); z-index: 100000; align-items: center; justify-content: center; padding: 30px; }
            .lbx-edit-modal-backdrop.is-open { display: flex; }
            /* Animated Frame Line — modal mini-preview (same two-half-paths technique as the frontend) */
            /* The frame-line modal opens ON TOP of the slide editors (which are modals
               themselves, z-index 100000) — stack it above them. */
            #lbx-fl-modal { z-index: 100001; }
            /* Card badge — flags slides that have their own animated frame line. */
            .lbx-fl-badge { position: absolute; top: 8px; right: 8px; z-index: 3; display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 50%; background: rgba(37, 99, 235, 0.92); color: #fff; font-size: 12px; line-height: 1; box-shadow: 0 1px 4px rgba(0,0,0,0.25); pointer-events: none; }
            /* Split light/dark backdrop so line color, glow and the contrast outline
               can all be judged against BOTH background kinds at once. */
            .lbx-fl-preview { position: relative; height: 160px; border-radius: 10px; overflow: hidden; background: linear-gradient(105deg, #eef1f5 0%, #e6eaef 46%, #2c3743 54%, #232b34 100%); }
            .lbx-fl-preview svg { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
            .lbx-fl-preview path { fill: none; stroke: var(--flp-color, #fff); stroke-width: var(--flp-width, 4px); stroke-linecap: round; vector-effect: non-scaling-stroke; animation: lbx-fl-travel var(--flp-speed, 9s) linear infinite; }
            /* Draw once & stay — the stroke draws itself in and keeps the full frame. */
            .lbx-fl-preview.lbx-fl-preview--draw path { animation: lbx-fl-draw var(--flp-speed, 9s) ease-out 1 forwards; }
            @keyframes lbx-fl-draw { from { stroke-dashoffset: 100; } to { stroke-dashoffset: 0; } }
            .lbx-fl-preview.lbx-fl-preview--glow path { filter: drop-shadow(0 0 6px var(--flp-color, #fff)); }
            .lbx-fl-preview.lbx-fl-preview--outline path { filter: drop-shadow(0 0 1.5px rgba(0,0,0,0.6)); }
            .lbx-fl-preview.lbx-fl-preview--glow.lbx-fl-preview--outline path { filter: drop-shadow(0 0 1.5px rgba(0,0,0,0.6)) drop-shadow(0 0 6px var(--flp-color, #fff)); }
            @keyframes lbx-fl-travel { to { stroke-dashoffset: -100; } }
            .lbx-fl-swatch { width: 28px; height: 28px; padding: 0; border-radius: 50%; border: 1px solid var(--lbx-gray-300); cursor: pointer; }
            .lbx-fl-swatch:hover { box-shadow: 0 0 0 3px rgba(37,99,235,0.25); }
            /* Custom CSS — CodeMirror shell (replaces the plain textarea visually) */
            .lbx-css-toolbar + .CodeMirror,
            #lbx-slider-custom-css + .CodeMirror {
                border: 1px solid var(--lbx-gray-300);
                border-radius: var(--lbx-radius-sm, 8px);
                min-height: 280px;
                font-size: 12.5px;
                line-height: 1.6;
            }
            .CodeMirror-lint-tooltip { z-index: 100002; } /* above stacked modals */
            /* Quick-format toolbar (B/I/U) floating inside HTML-enabled text fields */
            .lbx-rte-wrap { position: relative; }
            .lbx-rte-wrap textarea { padding-right: 96px; width: 100%; box-sizing: border-box; }
            .lbx-rte-tools { position: absolute; top: 6px; right: 6px; display: inline-flex; gap: 4px; z-index: 2; }
            .lbx-rte-btn { width: 26px; height: 24px; padding: 0; border: 1px solid var(--lbx-gray-300); background: #fff; border-radius: 5px; cursor: pointer; font-size: 12px; line-height: 1; color: var(--lbx-gray-600); display: inline-flex; align-items: center; justify-content: center; box-shadow: 0 1px 2px rgba(0,0,0,0.04); }
            .lbx-rte-btn:hover { border-color: var(--lbx-primary); color: var(--lbx-primary); }
            .lbx-rte-btn strong, .lbx-rte-btn em, .lbx-rte-btn u { pointer-events: none; }
            /* Plain checkbox label inside the modal — the .lbx-slide-field uppercase/tiny
               label rule breaks the .lbx-toggle switch layout here (same rationale as
               .lbx-oem-checkbox-label), so the Glow control uses a native checkbox. */
            #lbx-fl-modal label.lbx-fl-check { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font-size: 13px; font-weight: 500; color: var(--lbx-gray-700); text-transform: none; letter-spacing: normal; margin: 26px 0 0; }
            #lbx-fl-modal label.lbx-fl-check input[type="checkbox"] { width: 16px; height: 16px; margin: 0; flex-shrink: 0; }
            #lbx-fl-modal label.lbx-fl-check .lbx-label-hint { text-transform: none; letter-spacing: normal; }
            .lbx-edit-modal { background: #fff; border-radius: var(--lbx-radius); box-shadow: 0 20px 60px rgba(0,0,0,0.25); width: 100%; max-width: 900px; max-height: 90vh; overflow-y: auto; overscroll-behavior: contain; animation: lbxModalIn 0.2s ease; display: flex; flex-direction: column; }
            .lbx-edit-modal__header { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 18px 24px; border-bottom: 1px solid var(--lbx-gray-200); background: var(--lbx-gray-50); position: sticky; top: 0; z-index: 2; }
            .lbx-edit-modal__header-info { min-width: 0; }
            .lbx-edit-modal__header-info h3 { margin: 0 0 2px; font-size: 15px; font-weight: 700; color: var(--lbx-gray-900); }
            .lbx-edit-modal__header-info p { margin: 0; font-size: 11px; color: var(--lbx-gray-500); }
            .lbx-edit-modal__close { background: var(--lbx-primary); border: 1px solid var(--lbx-primary); border-radius: var(--lbx-radius-sm); padding: 7px 16px; font-size: 13px; font-weight: 600; cursor: pointer; color: #fff; transition: all 0.15s; flex-shrink: 0; }
            .lbx-edit-modal__close:hover { background: #1d4ed8; border-color: #1d4ed8; }
            .lbx-edit-modal__body { padding: 20px 24px 32px; flex: 1; min-height: 0; }
            /* The editor wrap inside the modal has no background/border of its own —
               the modal provides the chrome, so reset the wrap's own shell. */
            .lbx-edit-modal__body .lbx-cs-editor-wrap { background: transparent; border: none; padding: 0; box-shadow: none; }
            /* Extra bottom breathing room for the scheduling pill that lives at the
               bottom of the editor — keeps it from kissing the modal's lower edge. */
            .lbx-edit-modal__body .lbx-schedule-section:last-child { margin-bottom: 8px; }
            /* Reuse the same body-scroll-lock rule the OEM modal uses so we don't
               duplicate state — both modals share this class. */
            body.lbx-edit-modal-open { overflow: hidden; }
            @media (max-width: 600px) {
                .lbx-edit-modal { max-width: 100%; margin: 10px; }
                .lbx-edit-modal__body { padding: 16px; }
            }

            /* ─── Custom confirm dialog — drop-in replacement for native confirm() ─── */
            .lbx-confirm-backdrop { position: fixed; inset: 0; background: rgba(15, 23, 42, 0.55); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); z-index: 100100; display: flex; align-items: center; justify-content: center; padding: 24px; opacity: 0; visibility: hidden; transition: opacity 0.2s ease, visibility 0.2s; }
            .lbx-confirm-backdrop.is-open { opacity: 1; visibility: visible; }
            .lbx-confirm-dialog { position: relative; width: 100%; max-width: 440px; background: #fff; border-radius: 14px; box-shadow: 0 25px 50px -12px rgba(0,0,0,0.4), 0 0 0 1px rgba(0,0,0,0.05); padding: 28px 28px 20px; text-align: center; transform: scale(0.92) translateY(8px); opacity: 0; transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.2s ease; }
            .lbx-confirm-backdrop.is-open .lbx-confirm-dialog { transform: scale(1) translateY(0); opacity: 1; }
            /* Icon circle — color depends on severity via `.lbx-confirm-dialog--danger` modifier */
            .lbx-confirm-dialog__icon { width: 60px; height: 60px; border-radius: 50%; margin: 0 auto 16px; display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%); position: relative; }
            .lbx-confirm-dialog__icon::before { content: ''; position: absolute; inset: -6px; border-radius: 50%; background: rgba(220, 38, 38, 0.08); z-index: 0; animation: lbx-confirm-pulse 2s ease-in-out infinite; }
            .lbx-confirm-dialog__icon .dashicons { font-size: 30px; width: 30px; height: 30px; color: #dc2626; position: relative; z-index: 1; }
            .lbx-confirm-dialog--info .lbx-confirm-dialog__icon { background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%); }
            .lbx-confirm-dialog--info .lbx-confirm-dialog__icon::before { background: rgba(37, 99, 235, 0.08); }
            .lbx-confirm-dialog--info .lbx-confirm-dialog__icon .dashicons { color: #2563eb; }
            .lbx-confirm-dialog__title { margin: 0 0 8px; font-size: 18px; font-weight: 700; color: #0f172a; line-height: 1.3; }
            .lbx-confirm-dialog__message { margin: 0 0 24px; font-size: 14px; color: #475569; line-height: 1.5; padding: 0 8px; }
            .lbx-confirm-dialog__actions { display: flex; gap: 10px; justify-content: center; }
            .lbx-confirm-btn { flex: 1; max-width: 160px; padding: 11px 20px; border: 1px solid transparent; border-radius: 9px; font-size: 13px; font-weight: 600; cursor: pointer; transition: transform 0.1s, box-shadow 0.15s, background 0.15s; font-family: inherit; }
            .lbx-confirm-btn:active { transform: translateY(1px); }
            .lbx-confirm-btn--cancel { background: #fff; color: #475569; border-color: var(--lbx-gray-300); }
            .lbx-confirm-btn--cancel:hover { background: var(--lbx-gray-50); border-color: var(--lbx-gray-400); }
            .lbx-confirm-btn--confirm { background: #dc2626; color: #fff; box-shadow: 0 4px 12px -2px rgba(220, 38, 38, 0.4); }
            .lbx-confirm-btn--confirm:hover { background: #b91c1c; box-shadow: 0 6px 16px -2px rgba(220, 38, 38, 0.5); }
            .lbx-confirm-btn--confirm:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.3), 0 4px 12px -2px rgba(220, 38, 38, 0.4); }
            .lbx-confirm-dialog--info .lbx-confirm-btn--confirm { background: var(--lbx-primary); box-shadow: 0 4px 12px -2px rgba(37, 99, 235, 0.4); }
            .lbx-confirm-dialog--info .lbx-confirm-btn--confirm:hover { background: #1e40af; box-shadow: 0 6px 16px -2px rgba(37, 99, 235, 0.5); }
            .lbx-confirm-dialog--info .lbx-confirm-btn--confirm:focus-visible { box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.3), 0 4px 12px -2px rgba(37, 99, 235, 0.4); }
            @keyframes lbx-confirm-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.4; transform: scale(1.15); } }
            .lbx-oem-modal { background: #fff; border-radius: var(--lbx-radius); box-shadow: 0 20px 60px rgba(0,0,0,0.25); width: 100%; max-width: 820px; max-height: 90vh; overflow-y: auto; overscroll-behavior: contain; animation: lbxModalIn 0.2s ease; }
            /* Lock page scroll while the OEM modal is open so the wheel/trackpad
               can't bleed into the body when the modal reaches its scroll limit. */
            body.lbx-oem-modal-open { overflow: hidden; }
            @keyframes lbxModalIn { from { opacity: 0; transform: translateY(12px) scale(0.97); } to { opacity: 1; transform: none; } }
            .lbx-oem-modal__header { display: flex; align-items: center; gap: 16px; padding: 20px 24px; border-bottom: 1px solid var(--lbx-gray-200); }
            .lbx-oem-modal__thumb { width: 100px; height: 68px; object-fit: cover; border-radius: 8px; flex-shrink: 0; background: var(--lbx-gray-100); }
            .lbx-oem-modal__header-info { flex: 1; min-width: 0; }
            .lbx-oem-modal__header-info h3 { margin: 0 0 2px; font-size: 16px; font-weight: 700; color: var(--lbx-gray-900); }
            .lbx-oem-modal__header-info p { margin: 0; font-size: 12px; color: var(--lbx-gray-500); }
            .lbx-oem-modal__close { background: none; border: 1px solid var(--lbx-gray-300); border-radius: var(--lbx-radius-sm); padding: 6px 14px; font-size: 13px; font-weight: 600; cursor: pointer; color: var(--lbx-gray-700); transition: all 0.15s; flex-shrink: 0; }
            .lbx-oem-modal__close:hover { background: var(--lbx-gray-100); border-color: var(--lbx-gray-400); }
            .lbx-oem-modal__body { padding: 20px 24px; }
            .lbx-oem-modal__section { background: var(--lbx-gray-50); border: 1px solid var(--lbx-gray-200); border-radius: var(--lbx-radius); padding: 18px 20px 10px; margin-bottom: 16px; }
            .lbx-oem-modal__section .lbx-edit-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 14px; }
            .lbx-oem-modal__section .lbx-edit-row--3col { grid-template-columns: 1fr 1fr 1fr; }
            .lbx-oem-modal__section label { display: block; font-size: 10px; font-weight: 600; color: var(--lbx-gray-500); text-transform: uppercase; letter-spacing: 0.03em; margin-bottom: 4px; }
            .lbx-oem-modal__section input:not([type="checkbox"]):not([type="radio"]),
            .lbx-oem-modal__section select,
            .lbx-oem-modal__section textarea { width: 100%; padding: 9px 12px; border: 1px solid var(--lbx-gray-300); border-radius: 8px; font-size: 13px; color: var(--lbx-gray-800); outline: none; transition: border-color 0.15s, box-shadow 0.15s, background 0.15s; background-color: #fff; font-family: inherit; line-height: 1.35; }
            .lbx-oem-modal__section input:hover,
            .lbx-oem-modal__section select:hover,
            .lbx-oem-modal__section textarea:hover { border-color: var(--lbx-gray-400); }
            .lbx-oem-modal__section input:focus,
            .lbx-oem-modal__section select:focus,
            .lbx-oem-modal__section textarea:focus { border-color: var(--lbx-primary); box-shadow: 0 0 0 3px rgba(37,99,235,0.14); }
            .lbx-oem-modal__section select { -webkit-appearance: none; appearance: none; background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="%236b7280" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>'); background-repeat: no-repeat; background-position: right 12px center; background-size: 14px 14px; padding-right: 34px; cursor: pointer; }
            .lbx-oem-modal__section select:hover,
            .lbx-oem-modal__section select:focus { background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="%232563eb" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>'); }
            /* Inline checkbox labels (Hide Watermark) — prevent the section's uppercase tiny-font label rule from styling these */
            .lbx-oem-modal__section label.lbx-oem-checkbox-label { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font-size: 13px; font-weight: 500; color: var(--lbx-gray-700); text-transform: none; letter-spacing: normal; margin-bottom: 0; }
            .lbx-oem-modal__section label.lbx-oem-checkbox-label input[type="checkbox"] { width: 16px; height: 16px; margin: 0; flex-shrink: 0; }
            .lbx-oem-modal__section textarea { resize: vertical; min-height: 56px; }
            .lbx-oem-modal__lang-label { display: flex; align-items: center; gap: 8px; font-size: 12px; font-weight: 700; color: var(--lbx-gray-700); text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 14px; }
            .lbx-oem-modal__lang-label span { display: inline-flex; align-items: center; justify-content: center; min-width: 28px; height: 20px; padding: 0 6px; background: var(--lbx-primary); color: #fff; border-radius: 4px; font-size: 10px; font-weight: 800; letter-spacing: 0.05em; }
            .lbx-oem-modal__footer { padding: 14px 24px; border-top: 1px solid var(--lbx-gray-200); display: flex; justify-content: flex-end; }

            /* ─── EN/FR tabs inside the OEM modal ────────────────────────
               Replaces the previous stacked EN-then-FR sections with a single
               tabbed area. Cuts vertical scroll roughly in half and keeps
               translations side-by-side (one tab away) instead of pages apart. */
            .lbx-oem-modal__lang-tabs { display: flex; gap: 2px; margin: 0 0 14px; padding: 4px; background: var(--lbx-gray-100); border-radius: 10px; }
            .lbx-oem-modal__lang-tab { flex: 1; padding: 8px 12px; background: transparent; border: none; border-radius: 8px; font-size: 12px; font-weight: 600; color: var(--lbx-gray-500); cursor: pointer; transition: background 0.15s, color 0.15s, box-shadow 0.15s; display: inline-flex; align-items: center; justify-content: center; gap: 8px; }
            .lbx-oem-modal__lang-tab:hover { color: var(--lbx-gray-700); }
            .lbx-oem-modal__lang-tab.is-active { background: #fff; color: var(--lbx-primary); box-shadow: 0 1px 3px rgba(15, 23, 42, 0.08); }
            .lbx-oem-modal__lang-tab-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 24px; height: 18px; padding: 0 5px; background: var(--lbx-gray-300); color: #fff; border-radius: 4px; font-size: 9px; font-weight: 800; letter-spacing: 0.05em; transition: background 0.15s; }
            .lbx-oem-modal__lang-tab.is-active .lbx-oem-modal__lang-tab-badge { background: var(--lbx-primary); }
            .lbx-oem-modal__lang-panel { display: none; }
            .lbx-oem-modal__lang-panel.is-active { display: block; }

            /* Field group inside a language panel — a labeled cluster of related
               inputs (e.g. "Rates" with Finance + Lease side-by-side sub-cards). */
            .lbx-oem-modal__group { margin: 0 0 16px; }
            .lbx-oem-modal__group:last-child { margin-bottom: 0; }
            .lbx-oem-modal__group-title { font-size: 10px; font-weight: 700; color: var(--lbx-gray-500); text-transform: uppercase; letter-spacing: 0.06em; margin: 0 0 8px; padding-left: 2px; }
            .lbx-oem-modal__sub-card { background: #fff; border: 1px solid var(--lbx-gray-200); border-radius: 8px; padding: 12px 14px; }
            .lbx-oem-modal__sub-card-title { display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 700; color: var(--lbx-gray-700); text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 10px; }
            .lbx-oem-modal__sub-card-title .dashicons { font-size: 14px; width: 14px; height: 14px; color: var(--lbx-primary); }
            .lbx-oem-modal__sub-card .lbx-edit-row { margin-bottom: 10px; }
            .lbx-oem-modal__sub-card .lbx-edit-row:last-child { margin-bottom: 0; }

            @media (max-width: 600px) { .lbx-oem-modal__body .lbx-edit-row { grid-template-columns: 1fr; } .lbx-oem-modal { max-width: 100%; margin: 10px; } }

            /* ─── Slider Edit Form Tabs (Ultra Pro) ──────────────────────
               Full-width glassmorphism toolbar with an animated "magic pill"
               that slides between tabs. The pill lives on `::before` and is
               positioned via `--lbx-tab-index` (0–N), so the same CSS works for
               any number of tabs as long as `--lbx-tab-count` is set on the nav. */
            .lbx-tabs-nav { display: flex; width: 100%; gap: 0; padding: 6px; margin: 16px 0 30px; position: sticky; top: 32px; z-index: 5;
                /* Solid segmented-control "track" so the bar reads as a menu, not floating text. */
                background: var(--lbx-gray-100);
                border: 1px solid var(--lbx-gray-200);
                border-radius: 14px;
                box-shadow: inset 0 1px 2px rgba(15, 23, 42, 0.06), 0 1px 2px rgba(15, 23, 42, 0.04);
                transition: box-shadow 0.25s ease, border-color 0.25s ease, background 0.25s ease;
                --lbx-tab-count: 4;
                --lbx-tab-index: 0;
            }
            /* Sticky "lifted" state — kicks in once the bar is scrolled to the top. */
            .lbx-tabs-nav.is-stuck { box-shadow: 0 10px 32px -8px rgba(15, 23, 42, 0.18), 0 2px 6px -2px rgba(15, 23, 42, 0.06); border-color: var(--lbx-gray-300); }
            /* Magic pill — gradient that slides between tab positions on click. */
            .lbx-tabs-nav::before {
                content: '';
                position: absolute;
                top: 6px;
                bottom: 6px;
                left: 6px;
                width: calc((100% - 12px) / var(--lbx-tab-count));
                background: linear-gradient(135deg, var(--lbx-primary) 0%, #1d4ed8 70%, #7c3aed 130%);
                border-radius: 10px;
                box-shadow: 0 6px 16px -4px rgba(37, 99, 235, 0.50), 0 1px 2px rgba(37, 99, 235, 0.20), 0 0 0 1px rgba(255, 255, 255, 0.12) inset;
                transform: translateX(calc(var(--lbx-tab-index) * 100%));
                transition: transform 0.4s cubic-bezier(0.34, 1.36, 0.64, 1);
                z-index: 0;
                pointer-events: none;
            }
            /* Thin gradient accent below the bar — primary→violet, tapered ends. */
            .lbx-tabs-nav::after {
                content: '';
                position: absolute;
                bottom: -3px;
                left: 10%;
                right: 10%;
                height: 1px;
                background: linear-gradient(90deg, transparent, rgba(37, 99, 235, 0.35) 30%, rgba(124, 58, 237, 0.35) 70%, transparent);
                pointer-events: none;
            }
            .lbx-tabs-nav .lbx-tab { position: relative; z-index: 1; flex: 1; background: transparent; border: none; padding: 10px 14px; font-size: 13px; font-weight: 600; color: var(--lbx-gray-600); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: 8px; border-radius: 10px; transition: color 0.25s ease, background 0.2s ease; font-family: inherit; line-height: 1; letter-spacing: 0.01em; white-space: nowrap; }
            /* Ghost pill on hover for inactive tabs — a white chip on the grey track. */
            .lbx-tabs-nav .lbx-tab:not(.is-active):hover { color: var(--lbx-gray-900); background: rgba(255, 255, 255, 0.75); box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08); }
            .lbx-tabs-nav .lbx-tab.is-active { color: #fff; }
            .lbx-tabs-nav .lbx-tab .dashicons { font-size: 16px; width: 16px; height: 16px; transition: transform 0.3s cubic-bezier(0.34, 1.36, 0.64, 1); }
            .lbx-tabs-nav .lbx-tab:not(.is-active):hover .dashicons { transform: scale(1.15) rotate(-4deg); }
            .lbx-tabs-nav .lbx-tab.is-active .dashicons { color: #fff; transform: scale(1.05); }
            .lbx-tabs-nav .lbx-tab:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.28); }
            .lbx-tab-panel { display: none; }
            .lbx-tab-panel.is-active { display: block; animation: lbxTabFade 0.18s ease; }
            /* Slides section lives above the tabs (Smart Slider pattern) so the slides
               are always the primary visible content, with tab settings below. */
            .lbx-slides-always { margin: 0 0 20px; padding-bottom: 20px; border-bottom: 1px dashed var(--lbx-gray-300); }
            .lbx-slides-always > [id$="-section"]:first-child { margin-top: 0; }
            @keyframes lbxTabFade { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: translateY(0); } }
            @media (max-width: 782px) {
                .lbx-tabs-nav { position: static; padding: 5px; border-radius: 14px; }
                .lbx-tabs-nav::before { top: 5px; bottom: 5px; left: 5px; width: calc((100% - 10px) / var(--lbx-tab-count)); border-radius: 9px; }
                .lbx-tabs-nav .lbx-tab { padding: 9px 6px; font-size: 12px; gap: 4px; }
                .lbx-tabs-nav .lbx-tab .dashicons { font-size: 15px; width: 15px; height: 15px; }
            }

            /* ═══════════════════════════════════════════════════════════
               Header actions + Live Preview Modal
               ═══════════════════════════════════════════════════════════ */
            .lbx-header-actions { display: flex; align-items: center; gap: 10px; }

            /* Backdrop — full-screen glass overlay. */
            .lbx-preview-backdrop {
                display: none; position: fixed; inset: 0; z-index: 100050;
                background: rgba(15, 23, 42, 0.55);
                -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
                padding: 24px;
                animation: lbxPreviewFade 0.2s ease;
            }
            .lbx-preview-backdrop.is-open { display: flex; }
            body.lbx-preview-open { overflow: hidden; }
            @keyframes lbxPreviewFade { from { opacity: 0; } to { opacity: 1; } }

            /* Shell — the white rounded container holding the toolbar + stage. */
            .lbx-preview-shell {
                display: flex; flex-direction: column;
                width: 100%; height: 100%;
                background: var(--lbx-gray-100, #f3f4f6);
                border-radius: 16px; overflow: hidden;
                box-shadow: 0 24px 60px -12px rgba(0, 0, 0, 0.45);
                animation: lbxPreviewPop 0.22s cubic-bezier(0.16, 1, 0.3, 1);
            }
            @keyframes lbxPreviewPop { from { transform: scale(0.97); opacity: 0; } to { transform: scale(1); opacity: 1; } }

            /* Toolbar — three groups: title/dims · devices+width · actions. */
            .lbx-preview-toolbar {
                display: flex; align-items: center; gap: 16px;
                padding: 10px 14px; background: #fff;
                border-bottom: 1px solid var(--lbx-gray-200);
                flex-wrap: wrap;
            }
            .lbx-preview-toolbar__group { display: flex; align-items: center; gap: 10px; }
            .lbx-preview-toolbar__left { flex: 1; min-width: 140px; }
            .lbx-preview-toolbar__center { flex: 0 0 auto; justify-content: center; flex-wrap: wrap; }
            .lbx-preview-toolbar__right { flex: 1; justify-content: flex-end; min-width: 120px; }
            .lbx-preview-title { display: inline-flex; align-items: center; gap: 7px; font-size: 14px; font-weight: 700; color: var(--lbx-gray-800); }
            .lbx-preview-title .dashicons { color: var(--lbx-primary); }
            .lbx-preview-dims { font-size: 11px; font-weight: 600; color: var(--lbx-gray-500); background: var(--lbx-gray-100); padding: 3px 9px; border-radius: 20px; font-variant-numeric: tabular-nums; }

            /* EN / FR language segmented control. */
            .lbx-preview-lang { display: inline-flex; gap: 2px; background: var(--lbx-gray-100); padding: 3px; border-radius: 9px; }
            .lbx-preview-lang-btn { padding: 4px 11px; font-size: 11px; font-weight: 700; border: none; background: transparent; color: var(--lbx-gray-500); border-radius: 6px; cursor: pointer; transition: background 0.15s, color 0.15s; }
            .lbx-preview-lang-btn:hover { color: var(--lbx-gray-700); }
            .lbx-preview-lang-btn.is-active { background: var(--lbx-primary); color: #fff; }
            /* Device preset segmented control. */
            .lbx-preview-devices { display: inline-flex; gap: 2px; background: var(--lbx-gray-100); padding: 3px; border-radius: 10px; }
            .lbx-preview-device {
                display: inline-flex; align-items: center; justify-content: center;
                width: 34px; height: 30px; padding: 0; border: none; background: transparent;
                border-radius: 7px; cursor: pointer; color: var(--lbx-gray-500);
                transition: background 0.15s, color 0.15s, box-shadow 0.15s;
            }
            .lbx-preview-device:hover { color: var(--lbx-gray-800); }
            .lbx-preview-device.is-active { background: #fff; color: var(--lbx-primary); box-shadow: 0 1px 3px rgba(15,23,42,0.12); }
            .lbx-preview-device .dashicons { font-size: 17px; width: 17px; height: 17px; }

            /* Width control — range + numeric field. */
            .lbx-preview-width { display: inline-flex; align-items: center; gap: 10px; }
            #lbx-preview-width-range { width: 140px; cursor: pointer; accent-color: var(--lbx-primary); }
            .lbx-preview-width-field { display: inline-flex; align-items: center; gap: 3px; background: var(--lbx-gray-100); border-radius: 8px; padding: 4px 8px; }
            #lbx-preview-width-num { width: 52px; border: none; background: transparent; font-size: 12px; font-weight: 600; color: var(--lbx-gray-800); text-align: right; padding: 0; -moz-appearance: textfield; outline: none; box-shadow: none !important; }
            #lbx-preview-width-num::-webkit-outer-spin-button, #lbx-preview-width-num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
            .lbx-preview-width-field span { font-size: 11px; color: var(--lbx-gray-400); font-weight: 600; }

            /* Icon action buttons. */
            .lbx-preview-iconbtn {
                display: inline-flex; align-items: center; justify-content: center;
                width: 34px; height: 34px; padding: 0; border: 1px solid var(--lbx-gray-200);
                background: #fff; border-radius: 9px; cursor: pointer; color: var(--lbx-gray-600);
                transition: background 0.15s, color 0.15s, border-color 0.15s;
            }
            .lbx-preview-iconbtn:hover { background: var(--lbx-gray-50); color: var(--lbx-gray-900); border-color: var(--lbx-gray-300); }
            .lbx-preview-iconbtn .dashicons { font-size: 18px; width: 18px; height: 18px; }
            .lbx-preview-iconbtn--close:hover { background: var(--lbx-danger, #ef4444); color: #fff; border-color: var(--lbx-danger, #ef4444); }

            /* Stage — neutral backdrop the resizable viewport sits on.
               Centering is done with `margin:auto` on the frame-wrap (NOT
               justify/align-center) so a viewport wider than the stage scrolls
               instead of getting its left edge clipped. */
            .lbx-preview-stage {
                flex: 1; min-height: 0; display: flex; padding: 22px; overflow: auto;
                /* `safe center` centers the viewport on both axes, and falls back to
                   start-alignment (instead of clipping) if the content ever exceeds the
                   stage — so the slider is ALWAYS centered and never cut off. The plain
                   `center` line is a fallback for engines that don't parse the `safe` kw. */
                justify-content: center; justify-content: safe center;
                align-items: center; align-items: safe center;
                background:
                    linear-gradient(45deg, rgba(0,0,0,0.025) 25%, transparent 25%, transparent 75%, rgba(0,0,0,0.025) 75%),
                    linear-gradient(45deg, rgba(0,0,0,0.025) 25%, transparent 25%, transparent 75%, rgba(0,0,0,0.025) 75%);
                background-size: 22px 22px; background-position: 0 0, 11px 11px;
            }
            .lbx-preview-frame-wrap { display: flex; align-items: center; flex: 0 0 auto; }

            /* Drag handles flanking the viewport. */
            .lbx-preview-handle {
                flex: 0 0 auto; width: 12px; align-self: center; height: 64px; margin: 0 3px;
                border-radius: 8px; background: var(--lbx-gray-300); cursor: ew-resize;
                transition: background 0.15s; position: relative;
            }
            .lbx-preview-handle:hover, .lbx-preview-handle.is-dragging { background: var(--lbx-primary); }
            .lbx-preview-handle::after { content: ''; position: absolute; inset: 0; margin: auto; width: 2px; height: 26px; background: rgba(255,255,255,0.7); border-radius: 2px; }
            /* When in full-width (desktop) mode the handles hide — nothing to drag against. */
            .lbx-preview-frame-wrap.is-full .lbx-preview-handle { display: none; }

            /* Viewport — outer box sized to the SCALED dimensions (JS). Clips the canvas. */
            .lbx-preview-viewport {
                position: relative; background: #fff; flex: 0 0 auto;
                box-shadow: 0 8px 28px -6px rgba(0,0,0,0.22);
                border-radius: 8px; overflow: hidden;
                width: 1280px; height: 480px;
            }
            /* Canvas — holds the iframe at LOGICAL size; transform-scaled to fit the stage
               without horizontal scroll. JS sets width/height/transform. */
            .lbx-preview-canvas { position: absolute; top: 0; left: 0; transform-origin: top left; background: #fff; width: 1280px; height: 480px; }
            #lbx-preview-iframe { display: block; width: 100%; height: 100%; border: 0; background: #fff; }

            .lbx-preview-loading, .lbx-preview-error {
                position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; gap: 10px;
                background: #fff; color: var(--lbx-gray-500); font-size: 13px; z-index: 2;
            }
            .lbx-preview-error { flex-direction: column; color: var(--lbx-danger, #ef4444); text-align: center; padding: 20px; }
            .lbx-preview-loading .spinner { float: none; margin: 0; }

            @media (max-width: 900px) {
                .lbx-preview-backdrop { padding: 0; }
                .lbx-preview-shell { border-radius: 0; }
                .lbx-preview-toolbar { gap: 10px; }
                .lbx-preview-toolbar__left .lbx-preview-dims { display: none; }
                #lbx-preview-width-range { width: 90px; }
                .lbx-preview-handle { display: none; }
            }
        </style>

        <div class="wrap lbx-admin">
            <!-- Header -->
            <div class="lbx-header">
                <div class="lbx-header-left">
                    <div class="lbx-header-icon lbx-header-icon--brand"><img src="<?php echo esc_url(plugins_url('assets/leadbox-mark.png', __DIR__)); ?>" alt="Leadbox"></div>
                    <div>
                        <h1><?php _e('LBX Slider', 'leadbox'); ?><span class="lbx-header-version">v1.0</span></h1>
                        <p><?php _e('Create and manage sliders for your pages.', 'leadbox'); ?></p>
                    </div>
                </div>
                <?php if ($editing_slider || $editing_id === 'new') : ?>
                    <div class="lbx-header-actions">
                        <button type="button" id="lbx-refresh-minerva" class="lbx-btn lbx-btn-secondary" style="display:none;" title="<?php esc_attr_e('Refresh offer data — re-fetch from Minerva. Keeps your custom edits; updates the editor list, the preview and the live site.', 'leadbox'); ?>">
                            <span class="dashicons dashicons-cloud"></span> <?php _e('Refresh data', 'leadbox'); ?>
                        </button>
                        <button type="button" id="lbx-preview-btn" class="lbx-btn lbx-btn-primary" title="<?php esc_attr_e('Preview the slider as it will appear on the site (⇧⌘P)', 'leadbox'); ?>">
                            <span class="dashicons dashicons-visibility"></span> <?php _e('Preview', 'leadbox'); ?>
                        </button>
                        <a href="<?php echo admin_url('admin.php?page=lbx-slider'); ?>" class="lbx-btn lbx-btn-secondary"><span class="dashicons dashicons-arrow-left-alt2"></span> <?php _e('All Sliders', 'leadbox'); ?></a>
                    </div>
                <?php endif; ?>
            </div>

            <div id="lbx-slider-notices"></div>

            <?php if ($editing_slider || $editing_id === 'new') : ?>
            <!-- ═══ Slider Form Card ═══ -->
            <div class="lbx-card lbx-card--editor">
                <div class="lbx-card-body">
                    <input type="hidden" id="edit-slider-id" value="<?php echo esc_attr($editing_id); ?>">

                    <!-- Type picker modal — asked once when creating a new slider. Type is fixed afterwards. -->
                    <div id="lbx-type-modal" class="lbx-type-modal-backdrop" role="dialog" aria-modal="true" aria-hidden="true">
                        <div class="lbx-type-modal" role="document">
                            <div class="lbx-type-modal__head">
                                <h3><?php _e('What kind of slider?', 'leadbox'); ?></h3>
                                <p><?php _e('Pick one to get started — this is set once and stays fixed for this slider.', 'leadbox'); ?></p>
                            </div>
                            <div class="lbx-type-modal__choices">
                                <button type="button" class="lbx-type-choice" data-type="minerva">
                                    <span class="lbx-type-choice__icon lbx-type-choice__icon--slides"><span class="dashicons dashicons-images-alt2"></span></span>
                                    <span class="lbx-type-choice__title"><?php _e('Slides', 'leadbox'); ?></span>
                                    <span class="lbx-type-choice__desc"><?php _e('Build your own slides, and optionally auto-populate the dealer\'s OEM &amp; Static offers.', 'leadbox'); ?></span>
                                </button>
                                <button type="button" class="lbx-type-choice" data-type="countdown">
                                    <span class="lbx-type-choice__icon lbx-type-choice__icon--countdown"><span class="dashicons dashicons-clock"></span></span>
                                    <span class="lbx-type-choice__title"><?php _e('Countdown', 'leadbox'); ?></span>
                                    <span class="lbx-type-choice__desc"><?php _e('Single slide with a live countdown timer — perfect for sales, launches &amp; events.', 'leadbox'); ?></span>
                                </button>
                            </div>
                            <div class="lbx-type-modal__foot">
                                <a href="<?php echo esc_url(admin_url('admin.php?page=lbx-slider')); ?>" class="lbx-type-modal__cancel"><?php _e('Cancel', 'leadbox'); ?></a>
                            </div>
                        </div>
                    </div>

                    <!-- ═══ Always-visible Slides section (Smart Slider style — slides on top, tabs below) ═══ -->
                    <div class="lbx-slides-always" aria-label="<?php esc_attr_e('Slides', 'leadbox'); ?>">
                    <!-- ─── Unified OEM + Additional Slides ─────────────────── -->
                    <!-- Both OEM offers (Minerva) and Additional Slides (extras) render here as
                         a single sortable grid. Each card carries a type badge so the user can
                         tell them apart. The legacy two-list layout has been retired. -->
                    <div id="lbx-oem-preview-section" <?php echo $editing_source !== 'minerva' ? 'style="display:none;"' : ''; ?>>
                        <!-- Auto-populate switches (LBT-1472) — two independent data sources.
                             Each can be toggled on/off separately and they coexist with the
                             dealer's manual (Additional) slides in the same unified list. -->
                        <div class="lbx-autopop-bar">
                            <label class="lbx-toggle">
                                <input type="checkbox" id="lbx-auto-oem" <?php checked($ed_auto_oem); ?>>
                                <span class="lbx-toggle-track"></span>
                                <span class="lbx-toggle-icon dashicons dashicons-tag" aria-hidden="true"></span>
                                <span class="lbx-toggle-label">
                                    <?php _e('Add OEM Offers', 'leadbox'); ?>
                                    <span class="lbx-label-hint"><?php _e('(per-model finance &amp; lease)', 'leadbox'); ?></span>
                                </span>
                            </label>
                            <label class="lbx-toggle">
                                <input type="checkbox" id="lbx-auto-static" <?php checked($ed_auto_static); ?>>
                                <span class="lbx-toggle-track"></span>
                                <span class="lbx-toggle-icon dashicons dashicons-format-image" aria-hidden="true"></span>
                                <span class="lbx-toggle-label">
                                    <?php _e('Add OEM Static Offers', 'leadbox'); ?>
                                    <span class="lbx-label-hint"><?php _e('(promo images)', 'leadbox'); ?></span>
                                </span>
                            </label>
                            <?php if (count($dealer_makes) > 1 || $ed_make_filter !== '') : ?>
                            <!-- Make filter — multi-make dealers can pin this slider to ONE make.
                                 Presentation-only: entries stay in the unified list, so flipping
                                 back to All Makes restores everything (order + settings intact). -->
                            <label class="lbx-make-filter" for="lbx-make-filter" title="<?php esc_attr_e('Only auto-populated OEM & Static offers are filtered — your own slides always show.', 'leadbox'); ?>" <?php echo (!$ed_auto_oem && !$ed_auto_static) ? 'style="display:none;"' : ''; ?>>
                                <span class="dashicons dashicons-filter" aria-hidden="true"></span>
                                <select id="lbx-make-filter">
                                    <option value=""><?php _e('All Makes', 'leadbox'); ?></option>
                                    <?php foreach ($dealer_makes as $mk) :
                                        $mk_val = strtolower($mk);
                                        // Keep the configured spelling (e.g. "GMC"); prettify pure-lowercase values.
                                        $mk_label = ($mk === $mk_val) ? ucfirst($mk) : $mk; ?>
                                    <option value="<?php echo esc_attr($mk_val); ?>" <?php selected($ed_make_filter, $mk_val); ?>>
                                        <?php echo esc_html(sprintf(__('Show Only %s Slides', 'leadbox'), $mk_label)); ?>
                                    </option>
                                    <?php endforeach; ?>
                                    <?php if ($ed_make_filter !== '' && !isset($seen_makes[$ed_make_filter])) : ?>
                                    <!-- Saved filter no longer among the dealer's makes — keep it selectable so it can be cleared. -->
                                    <option value="<?php echo esc_attr($ed_make_filter); ?>" selected>
                                        <?php echo esc_html(sprintf(__('Show Only %s Slides', 'leadbox'), ucfirst($ed_make_filter))); ?>
                                    </option>
                                    <?php endif; ?>
                                </select>
                            </label>
                            <?php endif; ?>
                        </div>
                        <div class="lbx-section-title">
                            <h3><?php _e('Slides', 'leadbox'); ?></h3>
                            <span id="lbx-oem-count" class="lbx-count">—</span>
                            <span id="lbx-make-filter-note" class="lbx-make-filter-note" style="display:none;"></span>
                            <span class="lbx-sortable-hint">
                                <span class="dashicons dashicons-move"></span>
                                <?php _e('Drag and drop any card to reorder. The order applies to every layout on the frontend.', 'leadbox'); ?>
                            </span>
                            <span style="margin-left:auto; display:inline-flex; gap:8px;">
                                <button type="button" id="lbx-add-extra-slide" class="lbx-btn lbx-btn-primary lbx-btn-sm">
                                    <span class="dashicons dashicons-plus-alt2"></span> <?php _e('Add Slide', 'leadbox'); ?>
                                </button>
                            </span>
                        </div>
                        <!-- Wrapper holds the grid + a busy overlay so we can show a loader
                             ON TOP of existing cards while OEM/Static offers are being fetched
                             (the overlay is a sibling, so re-rendering the grid never wipes it). -->
                        <div class="lbx-oem-preview-wrap">
                            <div id="lbx-oem-preview" class="lbx-oem-preview">
                                <div class="lbx-oem-preview__loading">
                                    <span class="spinner is-active" style="float:none;margin:0 8px 0 0;"></span>
                                    <?php _e('Loading slides...', 'leadbox'); ?>
                                </div>
                            </div>
                            <div id="lbx-oem-preview-busy" class="lbx-oem-preview__busy" aria-live="polite">
                                <span class="spinner is-active" style="float:none;margin:0;"></span>
                                <span class="lbx-busy-text"><?php _e('Loading offers…', 'leadbox'); ?></span>
                            </div>
                        </div>
                        <!-- Editor for Additional slides lives inside #lbx-ex-modal (declared at
                             the end of this file). #lbx-extra-editor stays the same so delegated
                             handlers and syncExtraData keep working. -->
                    </div>

                    <!-- ─── Countdown Settings ──────────────────────────── -->
                    <div id="lbx-countdown-section" <?php echo $editing_source !== 'countdown' ? 'style="display:none;"' : ''; ?>>
                        <div class="lbx-section-title"><h3><?php _e('Countdown Settings', 'leadbox'); ?></h3></div>
                        <?php
                        $ed_cd_style = $editing_slider['countdown_style'] ?? 'flip';
                        $ed_cd_overlay = (int) ($editing_slider['countdown_overlay_opacity'] ?? 60);
                        $cd_bg = $editing_slider['countdown_bg_image'] ?? '';
                        ?>

                        <!-- Schedule -->
                        <div class="lbx-panel-group">
                            <h4 class="lbx-panel-group__title"><span class="dashicons dashicons-calendar-alt"></span> <?php _e('Schedule', 'leadbox'); ?></h4>
                            <div class="lbx-panel-grid">
                                <div class="lbx-slide-field" id="lbx-cd-start-wrap">
                                    <label><?php _e('Start Date & Time', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(optional)', 'leadbox'); ?></span></label>
                                    <input type="datetime-local" id="lbx-cd-start-date" value="<?php echo esc_attr($editing_slider['countdown_start_date'] ?? ''); ?>">
                                    <span class="lbx-field-error" id="lbx-cd-start-error"></span>
                                </div>
                                <div class="lbx-slide-field" id="lbx-cd-end-wrap">
                                    <label><?php _e('End Date & Time', 'leadbox'); ?> <span class="lbx-req-star" title="<?php esc_attr_e('Required', 'leadbox'); ?>">*</span></label>
                                    <input type="datetime-local" id="lbx-cd-end-date" value="<?php echo esc_attr($editing_slider['countdown_end_date'] ?? ''); ?>">
                                    <span class="lbx-field-error" id="lbx-cd-end-error"></span>
                                </div>
                            </div>
                        </div>

                        <!-- Content -->
                        <div class="lbx-panel-group">
                            <h4 class="lbx-panel-group__title"><span class="dashicons dashicons-edit"></span> <?php _e('Content', 'leadbox'); ?></h4>
                            <div class="lbx-panel-grid">
                                <div class="lbx-slide-field">
                                    <label><?php _e('Title', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(optional)', 'leadbox'); ?></span></label>
                                    <input type="text" id="lbx-cd-title" value="<?php echo esc_attr($editing_slider['countdown_title'] ?? ''); ?>" placeholder="<?php echo esc_attr__('e.g. Spring Sale Ends In...', 'leadbox'); ?>">
                                </div>
                                <div class="lbx-slide-field">
                                    <label><?php _e('Description', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(optional)', 'leadbox'); ?></span></label>
                                    <input type="text" id="lbx-cd-desc" value="<?php echo esc_attr($editing_slider['countdown_desc'] ?? ''); ?>" placeholder="<?php echo esc_attr__('e.g. Don\'t miss out!', 'leadbox'); ?>">
                                </div>
                            </div>
                            <div class="lbx-panel-grid" style="margin-top:14px;">
                                <div class="lbx-slide-field">
                                    <label><?php _e('Expired Message', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(shown when the timer reaches zero)', 'leadbox'); ?></span></label>
                                    <input type="text" id="lbx-cd-expired-msg" value="<?php echo esc_attr($editing_slider['countdown_expired_msg'] ?? ''); ?>" placeholder="<?php echo esc_attr__('This offer has ended.', 'leadbox'); ?>">
                                </div>
                            </div>
                        </div>

                        <!-- Appearance -->
                        <div class="lbx-panel-group">
                            <h4 class="lbx-panel-group__title"><span class="dashicons dashicons-art"></span> <?php _e('Appearance', 'leadbox'); ?></h4>
                            <div class="lbx-panel-grid">
                                <div class="lbx-slide-field">
                                    <label><?php _e('Countdown Style', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(visual theme for the digits)', 'leadbox'); ?></span></label>
                                    <select id="lbx-cd-style" class="lbx-input">
                                        <option value="flip" <?php selected($ed_cd_style, 'flip'); ?>><?php _e('Flip Clock', 'leadbox'); ?></option>
                                        <option value="minimal" <?php selected($ed_cd_style, 'minimal'); ?>><?php _e('Minimal', 'leadbox'); ?></option>
                                        <option value="boxed" <?php selected($ed_cd_style, 'boxed'); ?>><?php _e('Boxed', 'leadbox'); ?></option>
                                        <option value="neon" <?php selected($ed_cd_style, 'neon'); ?>><?php _e('Neon Glow', 'leadbox'); ?></option>
                                    </select>
                                </div>
                                <div class="lbx-slide-field">
                                    <label><?php _e('Overlay Opacity', 'leadbox'); ?> <span class="lbx-label-hint">(<span id="lbx-cd-overlay-val"><?php echo esc_html($ed_cd_overlay); ?></span>%)</span></label>
                                    <input type="range" id="lbx-cd-overlay-opacity" min="0" max="100" step="5" value="<?php echo esc_attr($ed_cd_overlay); ?>" style="width:100%;">
                                </div>
                            </div>
                            <div class="lbx-panel-grid" style="margin-top:14px;">
                                <div class="lbx-slide-field">
                                    <label><?php _e('Background Image', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(optional, recommended)', 'leadbox'); ?></span></label>
                                    <div id="lbx-cd-bg-wrap" class="lbx-cd-bg-wrap">
                                        <div id="lbx-cd-bg-thumb" class="lbx-cd-bg-thumb" <?php echo $cd_bg ? '' : 'style="display:none;"'; ?>>
                                            <img id="lbx-cd-bg-thumb-img" src="<?php echo esc_attr($cd_bg); ?>" alt="">
                                            <button type="button" id="lbx-cd-remove-bg" class="lbx-cd-bg-remove" title="<?php esc_attr_e('Remove image', 'leadbox'); ?>">&times;</button>
                                        </div>
                                        <div id="lbx-cd-bg-empty" class="lbx-cd-bg-empty" <?php echo $cd_bg ? 'style="display:none;"' : ''; ?>>
                                            <button type="button" id="lbx-cd-pick-bg" class="lbx-btn lbx-btn-secondary lbx-btn-sm">
                                                <span class="dashicons dashicons-format-image"></span> <?php _e('Select Image', 'leadbox'); ?>
                                            </button>
                                        </div>
                                        <input type="hidden" id="lbx-cd-bg-image" value="<?php echo esc_attr($cd_bg); ?>">
                                    </div>
                                </div>
                            </div>
                        </div>

                        <!-- Call to Action -->
                        <div class="lbx-panel-group">
                            <h4 class="lbx-panel-group__title"><span class="dashicons dashicons-admin-links"></span> <?php _e('Call to Action', 'leadbox'); ?></h4>
                            <div class="lbx-panel-grid">
                                <div class="lbx-slide-field">
                                    <label><?php _e('CTA Label', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(optional)', 'leadbox'); ?></span></label>
                                    <input type="text" id="lbx-cd-cta-label" value="<?php echo esc_attr($editing_slider['countdown_cta_label'] ?? ''); ?>" placeholder="<?php echo esc_attr__('e.g. SHOP NOW', 'leadbox'); ?>">
                                </div>
                                <div class="lbx-slide-field">
                                    <label><?php _e('CTA URL', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(optional)', 'leadbox'); ?></span></label>
                                    <input type="text" id="lbx-cd-cta-url" value="<?php echo esc_attr($editing_slider['countdown_cta_url'] ?? ''); ?>" placeholder="https://">
                                </div>
                            </div>
                            <div class="lbx-panel-grid" style="margin-top:14px;">
                                <div class="lbx-slide-field">
                                    <label><?php _e('Image Link URL', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(optional — makes the background clickable)', 'leadbox'); ?></span></label>
                                    <input type="text" id="lbx-cd-image-link-url" value="<?php echo esc_attr($editing_slider['countdown_image_link_url'] ?? ''); ?>" placeholder="https://">
                                </div>
                                <div class="lbx-slide-field">
                                    <label><?php _e('Image Link Target', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(where the link opens)', 'leadbox'); ?></span></label>
                                    <select id="lbx-cd-image-link-target" class="lbx-input">
                                        <option value="_self" <?php selected($editing_slider['countdown_image_link_target'] ?? '_self', '_self'); ?>><?php _e('Same tab', 'leadbox'); ?></option>
                                        <option value="_blank" <?php selected($editing_slider['countdown_image_link_target'] ?? '_self', '_blank'); ?>><?php _e('New tab', 'leadbox'); ?></option>
                                    </select>
                                </div>
                            </div>
                        </div>

                        <!-- Options -->
                        <div class="lbx-panel-group">
                            <h4 class="lbx-panel-group__title"><span class="dashicons dashicons-admin-generic"></span> <?php _e('Options', 'leadbox'); ?></h4>
                            <label style="display:flex;align-items:center;gap:8px;cursor:pointer;">
                                <input type="checkbox" id="lbx-cd-hide-watermark" <?php checked(!empty($editing_slider['countdown_hide_watermark'])); ?>>
                                <span style="font-size:13px;"><?php _e('Hide watermark on countdown', 'leadbox'); ?></span>
                            </label>
                        </div>
                    </div>

                    <!-- ─── Custom Slides Builder ─────────────────────── -->
                    <div id="lbx-custom-section" <?php echo $editing_source !== 'custom' ? 'style="display:none;"' : ''; ?>>
                        <div class="lbx-section-title">
                            <h3><?php _e('Slides', 'leadbox'); ?> <span class="lbx-req-star" title="<?php esc_attr_e('Required', 'leadbox'); ?>">*</span></h3>
                            <span id="lbx-custom-count" class="lbx-count"><?php echo count($editing_custom_slides); ?></span>
                        </div>
                        <p style="font-size:12px;color:var(--lbx-gray-500);margin:-6px 0 8px;"><?php _e('Custom sliders require at least one slide to save. Individual slide fields below are optional unless noted.', 'leadbox'); ?></p>
                        <p class="lbx-sortable-hint" id="lbx-custom-sortable-hint">
                            <span class="dashicons dashicons-move"></span>
                            <?php _e('Drag and drop slides to reorder. The order applies to every layout on the frontend.', 'leadbox'); ?>
                        </p>
                        <div id="lbx-custom-slides" class="lbx-cs-strip-wrap"></div>
                        <!-- Editor for Custom slides lives inside #lbx-cs-modal (declared at the end
                             of this file). Kept the same #lbx-custom-editor id so delegated handlers
                             and syncSlideData keep working without changes. -->
                    </div>
                    </div><!-- /lbx-slides-always -->

                    <!-- ═══ Tabs Navigation ═══ -->
                    <div class="lbx-tabs-nav" role="tablist" aria-label="<?php esc_attr_e('Slider settings sections', 'leadbox'); ?>">
                        <button type="button" class="lbx-tab is-active" role="tab" aria-selected="true" data-tab="general">
                            <span class="dashicons dashicons-admin-generic"></span> <?php _e('General', 'leadbox'); ?>
                        </button>
                        <button type="button" class="lbx-tab" role="tab" aria-selected="false" data-tab="design">
                            <span class="dashicons dashicons-art"></span> <?php _e('Design', 'leadbox'); ?>
                        </button>
                        <button type="button" class="lbx-tab" role="tab" aria-selected="false" data-tab="developer">
                            <span class="dashicons dashicons-editor-code"></span> <?php _e('Developer', 'leadbox'); ?>
                        </button>
                    </div>
                    <div class="lbx-tabs-content">
                    <div class="lbx-tab-panel is-active" data-tab="general" role="tabpanel">
                    <!-- Basic -->
                    <div class="lbx-panel-group">
                        <h4 class="lbx-panel-group__title"><span class="dashicons dashicons-info"></span> <?php _e('Basic', 'leadbox'); ?></h4>
                        <div style="display: flex; gap: 20px; align-items: flex-end; flex-wrap: wrap;">
                            <!-- .lbx-slide-field gives the input the same treatment as the
                                 Countdown CTA / Image Link inputs above (uppercase tiny label,
                                 9x12 padding, 13px font, 8px radius). Visually consistent. -->
                            <div class="lbx-slide-field" style="flex: 1; min-width: 240px;">
                                <label for="new-slider-name"><?php _e('Slider Name', 'leadbox'); ?> <span class="lbx-req-star" title="<?php esc_attr_e('Required', 'leadbox'); ?>">*</span></label>
                                <input type="text" id="new-slider-name" value="<?php echo esc_attr($editing_slider['name'] ?? ''); ?>" placeholder="<?php esc_attr_e('e.g. Homepage Offers', 'leadbox'); ?>">
                            </div>
                            <!-- Toggle column kept outside .lbx-slide-field on purpose — that
                                 wrapper's `> label` selector would clobber the toggle's flex
                                 layout. Instead we hand-roll the matching label style. -->
                            <div style="flex: 0 0 auto;">
                                <span class="lbx-mini-label"><?php _e('Status', 'leadbox'); ?></span>
                                <label class="lbx-toggle" style="min-height:38px;padding:0 2px;">
                                    <input type="checkbox" id="new-slider-enabled" <?php checked($editing_slider ? $editing_slider['enabled'] : true); ?>>
                                    <span class="lbx-toggle-track"></span>
                                    <span class="lbx-toggle-label"><?php _e('Enabled', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(optional)', 'leadbox'); ?></span></span>
                                </label>
                            </div>
                        </div>
                    </div>

                    <!-- Scheduling -->
                    <?php
                    $ed_start_date = $editing_slider['start_date'] ?? '';
                    $ed_end_date = $editing_slider['end_date'] ?? '';
                    ?>
                    <div class="lbx-schedule-section">
                        <h5><span class="dashicons dashicons-calendar-alt"></span> <?php _e('Scheduling', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(optional)', 'leadbox'); ?></span></h5>
                        <div class="lbx-schedule-row">
                            <div class="lbx-schedule-field">
                                <label for="lbx-slider-start-date"><?php _e('Publish Date', 'leadbox'); ?></label>
                                <input type="datetime-local" id="lbx-slider-start-date" value="<?php echo esc_attr($ed_start_date); ?>">
                            </div>
                            <div class="lbx-schedule-field">
                                <label for="lbx-slider-end-date"><?php _e('Unpublish Date', 'leadbox'); ?></label>
                                <input type="datetime-local" id="lbx-slider-end-date" value="<?php echo esc_attr($ed_end_date); ?>">
                            </div>
                        </div>
                        <div class="lbx-schedule-hint">
                            <span class="dashicons dashicons-info"></span>
                            <?php _e('Leave empty for immediate/permanent. Uses your WordPress timezone setting.', 'leadbox'); ?>
                        </div>
                    </div>

                    <!-- Type — chosen once via the modal on creation, then fixed; the inline picker stays hidden so it doesn't clutter the editor. -->
                    <div class="lbx-panel-group" id="lbx-type-section" style="display:none;">
                        <h4 class="lbx-panel-group__title"><span class="dashicons dashicons-category"></span> <?php _e('Type', 'leadbox'); ?> <span class="lbx-label-hint" style="text-transform:none;letter-spacing:normal;color:var(--lbx-gray-400);"><?php _e('(required — pick one)', 'leadbox'); ?></span></h4>
                        <div class="lbx-sources">
                            <label class="lbx-source-card <?php echo $editing_source === 'minerva' ? 'active' : ''; ?>" data-source="minerva">
                                <input type="radio" name="slider_source" value="minerva" <?php checked($editing_source, 'minerva'); ?>>
                                <span class="lbx-source-check"></span>
                                <div class="lbx-source-card-icon"><span class="dashicons dashicons-images-alt2"></span></div>
                                <h4><?php _e('Slides', 'leadbox'); ?></h4>
                                <p><?php _e('Build your own slides, and optionally auto-populate the dealer\'s OEM &amp; Static offers.', 'leadbox'); ?></p>
                                <div class="lbx-source-hint">
                                    <span class="dashicons dashicons-info"></span>
                                    <span><?php _e('Turn on <strong>Auto-populate</strong> to pull OEM &amp; Static offers for this dealer\'s make &amp; province.', 'leadbox'); ?></span>
                                </div>
                            </label>
                            <?php // Legacy "Custom Slides" type — only offered when editing an existing custom slider. ?>
                            <label class="lbx-source-card <?php echo $editing_source === 'custom' ? 'active' : ''; ?>" data-source="custom" <?php echo $is_legacy_custom ? '' : 'style="display:none;"'; ?>>
                                <input type="radio" name="slider_source" value="custom" <?php checked($editing_source, 'custom'); ?>>
                                <span class="lbx-source-check"></span>
                                <div class="lbx-source-card-icon"><span class="dashicons dashicons-images-alt2"></span></div>
                                <h4><?php _e('Custom Slides', 'leadbox'); ?> <span class="lbx-label-hint">(<?php _e('legacy', 'leadbox'); ?>)</span></h4>
                                <p><?php _e('Build your own slides with custom images, titles, descriptions & call-to-action buttons.', 'leadbox'); ?></p>
                                <div class="lbx-source-hint">
                                    <span class="dashicons dashicons-info"></span>
                                    <span><?php _e('Full control over every slide — ideal for promotions, events & seasonal campaigns.', 'leadbox'); ?></span>
                                </div>
                            </label>
                            <label class="lbx-source-card <?php echo $editing_source === 'countdown' ? 'active' : ''; ?>" data-source="countdown">
                                <input type="radio" name="slider_source" value="countdown" <?php checked($editing_source, 'countdown'); ?>>
                                <span class="lbx-source-check"></span>
                                <div class="lbx-source-card-icon"><span class="dashicons dashicons-clock"></span></div>
                                <h4><?php _e('Countdown', 'leadbox'); ?></h4>
                                <p><?php _e('Single slide with a live countdown timer — perfect for sales, launches & events.', 'leadbox'); ?></p>
                                <div class="lbx-source-hint">
                                    <span class="dashicons dashicons-info"></span>
                                    <span><?php _e('Configure dates, background & style. Timer runs live on the frontend.', 'leadbox'); ?></span>
                                </div>
                            </label>
                        </div>
                    </div>

                    </div><!-- /lbx-tab-panel general -->
                    <div class="lbx-tab-panel" data-tab="design" role="tabpanel">
                    <?php
                    $ed_layout = $editing_slider['layout'] ?? 'carousel';
                    $ed_transition = $editing_slider['transition'] ?? 'slide';
                    $ed_hero_intro = $editing_slider['hero_intro_animation'] ?? 'none';
                    $ed_content_align = $editing_slider['content_align'] ?? 'left';
                    $ed_height = $editing_slider['slider_height'] ?? '';
                    $ed_show_inv = $editing_slider['show_view_inventory'] ?? false;
                    $ed_hide_offer_data = !empty($editing_slider['hide_offer_data']);
                    $ed_boxed_offer_text = !empty($editing_slider['boxed_offer_text']);
                    $ed_gradient = $editing_slider['gradient_color'] ?? '';
                    $ed_video_autoplay = !empty($editing_slider['video_autoplay']);
                    $ed_video_hover = !empty($editing_slider['video_hover_play']);
                    $ed_video_loop = !empty($editing_slider['video_loop']);
                    $ed_wm_image = $editing_slider['watermark_image'] ?? '';
                    $ed_wm_position = $editing_slider['watermark_position'] ?? 'bottom-right';
                    $ed_wm_size = (int) ($editing_slider['watermark_size'] ?? 50);
                    $ed_disclaimer_position = $editing_slider ? ($editing_slider['disclaimer_position'] ?? 'hidden') : 'top-right'; // new sliders default to Top Right (visible)
                    $ed_mc_mobile = (int) ($editing_slider['media_columns_mobile'] ?? 1);
                    $ed_mc_tablet = (int) ($editing_slider['media_columns_tablet'] ?? 2);
                    $ed_mc_desktop = (int) ($editing_slider['media_columns_desktop'] ?? 3);
                    $ed_cc_mobile = (int) ($editing_slider['carousel_columns_mobile'] ?? 1);
                    $ed_cc_tablet = (int) ($editing_slider['carousel_columns_tablet'] ?? 2);
                    $ed_cc_desktop = (int) ($editing_slider['carousel_columns_desktop'] ?? 3);
                    ?>

                    <!-- Layout & Style -->
                    <div class="lbx-panel-group">
                        <h4 class="lbx-panel-group__title"><span class="dashicons dashicons-art"></span> <?php _e('Layout & Style', 'leadbox'); ?></h4>
                            <!-- Layout — full width: the primary choice gets its own row. -->
                            <div class="lbx-field">
                                <label class="lbx-label"><?php _e('Layout', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(has default)', 'leadbox'); ?></span></label>
                                <div class="lbx-radio-cards" id="lbx-layout-cards">
                                    <label class="lbx-radio-card <?php echo $ed_layout === 'carousel' ? 'active' : ''; ?>">
                                        <input type="radio" name="slider_layout" value="carousel" <?php checked($ed_layout, 'carousel'); ?>>
                                        <span class="dashicons dashicons-slides"></span> <?php _e('Carousel', 'leadbox'); ?>
                                    </label>
                                    <label class="lbx-radio-card <?php echo $ed_layout === 'offers-template' ? 'active' : ''; ?>" id="lbx-layout-hero">
                                        <input type="radio" name="slider_layout" value="offers-template" <?php checked($ed_layout, 'offers-template'); ?>>
                                        <span class="dashicons dashicons-cover-image"></span> <?php _e('Hero Banner', 'leadbox'); ?>
                                    </label>
                                    <label class="lbx-radio-card <?php echo $ed_layout === 'split' ? 'active' : ''; ?>" id="lbx-layout-split">
                                        <input type="radio" name="slider_layout" value="split" <?php checked($ed_layout, 'split'); ?>>
                                        <span class="dashicons dashicons-columns"></span> <?php _e('Split Panel', 'leadbox'); ?>
                                    </label>
                                    <label class="lbx-radio-card <?php echo $ed_layout === 'media-columns' ? 'active' : ''; ?>" id="lbx-layout-media-columns">
                                        <input type="radio" name="slider_layout" value="media-columns" <?php checked($ed_layout, 'media-columns'); ?>>
                                        <span class="dashicons dashicons-screenoptions"></span> <?php _e('Media Columns', 'leadbox'); ?>
                                    </label>
                                    <label class="lbx-radio-card <?php echo $ed_layout === 'oem-offer-hero' ? 'active' : ''; ?>" id="lbx-layout-oem-offer-hero">
                                        <input type="radio" name="slider_layout" value="oem-offer-hero" <?php checked($ed_layout, 'oem-offer-hero'); ?>>
                                        <span class="dashicons dashicons-megaphone"></span> <?php _e('OEM Offer Hero', 'leadbox'); ?>
                                    </label>
                                </div>
                            </div>
                            <div class="lbx-subgroup-title"><?php _e('Behavior', 'leadbox'); ?></div>
                            <div class="lbx-design-grid lbx-design-grid--2col">
                                <!-- Transition -->
                                <div class="lbx-field">
                                    <label class="lbx-label"><?php _e('Transition Effect', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(has default)', 'leadbox'); ?></span></label>
                                    <select class="lbx-input" id="lbx-transition" style="max-width: 220px;">
                                        <option value="slide" <?php selected($ed_transition, 'slide'); ?>><?php _e('Slide', 'leadbox'); ?></option>
                                        <option value="fade" <?php selected($ed_transition, 'fade'); ?>><?php _e('Fade', 'leadbox'); ?></option>
                                        <option value="creative" <?php selected($ed_transition, 'creative'); ?>><?php _e('Creative', 'leadbox'); ?></option>
                                        <option value="flip" <?php selected($ed_transition, 'flip'); ?>><?php _e('Flip', 'leadbox'); ?></option>
                                        <option value="coverflow" <?php selected($ed_transition, 'coverflow'); ?>><?php _e('Coverflow', 'leadbox'); ?></option>
                                    </select>
                                </div>
                                <!-- Default Content Align -->
                                <div class="lbx-field">
                                    <label class="lbx-label"><?php _e('Content Alignment', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(default for all slides)', 'leadbox'); ?></span></label>
                                    <div class="lbx-radio-cards" id="lbx-align-cards">
                                        <label class="lbx-radio-card <?php echo $ed_content_align === 'left' ? 'active' : ''; ?>">
                                            <input type="radio" name="slider_content_align" value="left" <?php checked($ed_content_align, 'left'); ?>>
                                            <span class="dashicons dashicons-align-pull-left"></span> <?php _e('Left', 'leadbox'); ?>
                                        </label>
                                        <label class="lbx-radio-card <?php echo $ed_content_align === 'center' ? 'active' : ''; ?>">
                                            <input type="radio" name="slider_content_align" value="center" <?php checked($ed_content_align, 'center'); ?>>
                                            <span class="dashicons dashicons-align-center"></span> <?php _e('Center', 'leadbox'); ?>
                                        </label>
                                        <label class="lbx-radio-card <?php echo $ed_content_align === 'right' ? 'active' : ''; ?>">
                                            <input type="radio" name="slider_content_align" value="right" <?php checked($ed_content_align, 'right'); ?>>
                                            <span class="dashicons dashicons-align-pull-right"></span> <?php _e('Right', 'leadbox'); ?>
                                        </label>
                                    </div>
                                </div>
                                <!-- OEM Offer Hero — Intro Animation (offer text entrance) -->
                                <div class="lbx-field">
                                    <label class="lbx-label" for="lbx-hero-intro-animation"><?php _e('Intro Animation', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(OEM Offer Hero — offer text)', 'leadbox'); ?></span></label>
                                    <select id="lbx-hero-intro-animation" class="lbx-input" style="max-width: 220px;">
                                        <option value="none" <?php selected($ed_hero_intro, 'none'); ?>><?php _e('None', 'leadbox'); ?></option>
                                        <option value="fade-in" <?php selected($ed_hero_intro, 'fade-in'); ?>><?php _e('Fade in', 'leadbox'); ?></option>
                                        <option value="fade-up" <?php selected($ed_hero_intro, 'fade-up'); ?>><?php _e('Fade up', 'leadbox'); ?></option>
                                        <option value="fade-down" <?php selected($ed_hero_intro, 'fade-down'); ?>><?php _e('Fade down', 'leadbox'); ?></option>
                                        <option value="slide-in" <?php selected($ed_hero_intro, 'slide-in'); ?>><?php _e('Slide in', 'leadbox'); ?></option>
                                        <option value="zoom-in" <?php selected($ed_hero_intro, 'zoom-in'); ?>><?php _e('Zoom in', 'leadbox'); ?></option>
                                        <option value="blur-in" <?php selected($ed_hero_intro, 'blur-in'); ?>><?php _e('Blur in', 'leadbox'); ?></option>
                                    </select>
                                </div>
                            </div>
                            <?php $ed_image_fit = $editing_slider['image_fit'] ?? ''; ?>
                            <div class="lbx-subgroup-title"><?php _e('Appearance', 'leadbox'); ?></div>
                            <div class="lbx-design-grid">
                                <!-- Slider Height -->
                                <div class="lbx-field">
                                    <label class="lbx-label" for="lbx-slider-height"><?php _e('Slider Height', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(e.g. 500px or 70vh)', 'leadbox'); ?></span></label>
                                    <input type="text" id="lbx-slider-height" class="lbx-input" value="<?php echo esc_attr($ed_height); ?>" placeholder="<?php esc_attr_e('auto', 'leadbox'); ?>">
                                </div>
                                <!-- Gradient Color -->
                                <div class="lbx-field">
                                    <label class="lbx-label"><?php _e('Gradient / Overlay Color', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(optional)', 'leadbox'); ?></span></label>
                                    <label class="lbx-color-chip" style="margin-top: 2px;">
                                        <span class="lbx-color-chip__preview"><input type="color" class="lbx-cc-swatch" id="lbx-gradient-picker" value="<?php echo esc_attr($ed_gradient ?: '#000000'); ?>"><span class="lbx-cc-fill" style="background:<?php echo esc_attr($ed_gradient ?: '#000000'); ?>"></span></span>
                                        <span class="lbx-color-chip__body"><input type="text" class="lbx-color-chip__hex" id="lbx-gradient-hex" value="<?php echo esc_attr($ed_gradient); ?>" placeholder="#000000"><span class="lbx-color-chip__label"><?php _e('Gradient', 'leadbox'); ?></span></span>
                                    </label>
                                </div>
                                <!-- Image Fit -->
                                <div class="lbx-field">
                                    <label class="lbx-label" for="lbx-slider-image-fit"><?php _e('Image Fit — all slides', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(overrides every slide)', 'leadbox'); ?></span></label>
                                    <select id="lbx-slider-image-fit" class="lbx-input">
                                        <option value="" <?php selected($ed_image_fit, ''); ?>><?php _e('Per slide (default)', 'leadbox'); ?></option>
                                        <option value="cover" <?php selected($ed_image_fit, 'cover'); ?>><?php _e('Cover — fill the area (crop edges)', 'leadbox'); ?></option>
                                        <option value="contain" <?php selected($ed_image_fit, 'contain'); ?>><?php _e('Contain — show the whole image', 'leadbox'); ?></option>
                                        <option value="fill" <?php selected($ed_image_fit, 'fill'); ?>><?php _e('Fill — stretch to the area', 'leadbox'); ?></option>
                                        <option value="scale-down" <?php selected($ed_image_fit, 'scale-down'); ?>><?php _e('Scale down', 'leadbox'); ?></option>
                                    </select>
                                </div>
                            </div>
                            <?php $ed_show_overlay = $editing_slider ? ($editing_slider['show_overlay'] ?? true) : true; ?>
                            <div style="margin-top:20px;">
                                <label class="lbx-toggle">
                                    <input type="checkbox" id="lbx-slider-show-overlay" <?php checked($ed_show_overlay); ?>>
                                    <span class="lbx-toggle-track"></span>
                                    <span class="lbx-toggle-label"><?php _e('Show gradient overlay', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(off = no overlay; ideal for logos)', 'leadbox'); ?></span></span>
                                </label>
                            </div>
                    </div><!-- /panel Layout & Style -->

                    <!-- Responsive Columns (media / carousel — only the one matching the current layout is visible) -->
                    <div class="lbx-panel-group" id="lbx-responsive-columns-panel">
                        <h4 class="lbx-panel-group__title"><span class="dashicons dashicons-screenoptions"></span> <?php _e('Responsive Columns', 'leadbox'); ?></h4>
                            <?php
                                $ed_bp_mobile = (int) ($editing_slider['breakpoint_mobile'] ?? 640);
                                $ed_bp_tablet = (int) ($editing_slider['breakpoint_tablet'] ?? 1024);
                            ?>
                            <div class="lbx-bp-custom" style="margin-bottom:18px;padding-bottom:16px;border-bottom:1px solid var(--lbx-gray-200);">
                                <label class="lbx-bp-heading"><?php _e('Breakpoints', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(px — where columns &amp; per-slide visibility switch device. Default 640 / 1024)', 'leadbox'); ?></span></label>
                                <div class="lbx-bp-fields">
                                    <div class="lbx-bp-field">
                                        <div class="lbx-bp-field__head"><span class="dashicons dashicons-smartphone"></span> <?php _e('Mobile up to', 'leadbox'); ?></div>
                                        <input type="number" id="lbx-bp-mobile" class="lbx-input" min="200" max="2000" step="1" value="<?php echo esc_attr($ed_bp_mobile); ?>" style="max-width:120px;"> <span class="lbx-label-hint">px</span>
                                    </div>
                                    <div class="lbx-bp-field">
                                        <div class="lbx-bp-field__head"><span class="dashicons dashicons-tablet"></span> <?php _e('Tablet up to', 'leadbox'); ?></div>
                                        <input type="number" id="lbx-bp-tablet" class="lbx-input" min="200" max="3000" step="1" value="<?php echo esc_attr($ed_bp_tablet); ?>" style="max-width:120px;"> <span class="lbx-label-hint">px</span>
                                    </div>
                                    <div class="lbx-bp-field">
                                        <div class="lbx-bp-field__head"><span class="dashicons dashicons-desktop"></span> <?php _e('Desktop', 'leadbox'); ?></div>
                                        <span class="lbx-label-hint" style="display:inline-block;padding-top:7px;"><?php _e('the rest', 'leadbox'); ?></span>
                                    </div>
                                </div>
                                <p class="lbx-label-hint" style="margin-top:6px;"><?php _e('Controls column counts and per-slide device visibility. Fine CSS styling keeps the theme defaults.', 'leadbox'); ?></p>
                            </div>
                            <div id="lbx-media-columns-breakpoints">
                                <label class="lbx-bp-heading"><?php _e('Media columns', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(optional — Media Columns layout)', 'leadbox'); ?></span></label>
                                <div class="lbx-bp-fields">
                                    <div class="lbx-bp-field">
                                        <div class="lbx-bp-field__head">
                                            <span class="dashicons dashicons-smartphone" aria-hidden="true"></span>
                                            <div class="lbx-bp-field__meta">
                                                <span class="lbx-bp-field__name"><?php _e('Mobile', 'leadbox'); ?></span>
                                                <span class="lbx-bp-field__range lbx-bp-range--mobile">≤ <?php echo (int) $ed_bp_mobile; ?>px</span>
                                            </div>
                                        </div>
                                        <div class="lbx-stepper">
                                            <button type="button" class="lbx-stepper__btn" data-step="-1" aria-label="<?php esc_attr_e('Decrease', 'leadbox'); ?>">−</button>
                                            <input type="number" id="lbx-mc-mobile" class="lbx-stepper__input" min="1" max="3" value="<?php echo esc_attr($ed_mc_mobile); ?>">
                                            <button type="button" class="lbx-stepper__btn" data-step="1" aria-label="<?php esc_attr_e('Increase', 'leadbox'); ?>">+</button>
                                        </div>
                                    </div>
                                    <div class="lbx-bp-field">
                                        <div class="lbx-bp-field__head">
                                            <span class="dashicons dashicons-tablet" aria-hidden="true"></span>
                                            <div class="lbx-bp-field__meta">
                                                <span class="lbx-bp-field__name"><?php _e('Tablet', 'leadbox'); ?></span>
                                                <span class="lbx-bp-field__range lbx-bp-range--tablet">≤ <?php echo (int) $ed_bp_tablet; ?>px</span>
                                            </div>
                                        </div>
                                        <div class="lbx-stepper">
                                            <button type="button" class="lbx-stepper__btn" data-step="-1" aria-label="<?php esc_attr_e('Decrease', 'leadbox'); ?>">−</button>
                                            <input type="number" id="lbx-mc-tablet" class="lbx-stepper__input" min="1" max="4" value="<?php echo esc_attr($ed_mc_tablet); ?>">
                                            <button type="button" class="lbx-stepper__btn" data-step="1" aria-label="<?php esc_attr_e('Increase', 'leadbox'); ?>">+</button>
                                        </div>
                                    </div>
                                    <div class="lbx-bp-field">
                                        <div class="lbx-bp-field__head">
                                            <span class="dashicons dashicons-desktop" aria-hidden="true"></span>
                                            <div class="lbx-bp-field__meta">
                                                <span class="lbx-bp-field__name"><?php _e('Desktop', 'leadbox'); ?></span>
                                                <span class="lbx-bp-field__range lbx-bp-range--desktop">&gt; <?php echo (int) $ed_bp_tablet; ?>px</span>
                                            </div>
                                        </div>
                                        <div class="lbx-stepper">
                                            <button type="button" class="lbx-stepper__btn" data-step="-1" aria-label="<?php esc_attr_e('Decrease', 'leadbox'); ?>">−</button>
                                            <input type="number" id="lbx-mc-desktop" class="lbx-stepper__input" min="1" max="6" value="<?php echo esc_attr($ed_mc_desktop); ?>">
                                            <button type="button" class="lbx-stepper__btn" data-step="1" aria-label="<?php esc_attr_e('Increase', 'leadbox'); ?>">+</button>
                                        </div>
                                    </div>
                                </div>
                            </div>
                            <div id="lbx-carousel-columns-breakpoints">
                                <label class="lbx-bp-heading"><?php _e('Slides per view', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(optional — Carousel layout)', 'leadbox'); ?></span></label>
                                <div class="lbx-bp-fields">
                                    <div class="lbx-bp-field">
                                        <div class="lbx-bp-field__head">
                                            <span class="dashicons dashicons-smartphone" aria-hidden="true"></span>
                                            <div class="lbx-bp-field__meta">
                                                <span class="lbx-bp-field__name"><?php _e('Mobile', 'leadbox'); ?></span>
                                                <span class="lbx-bp-field__range lbx-bp-range--mobile">≤ <?php echo (int) $ed_bp_mobile; ?>px</span>
                                            </div>
                                        </div>
                                        <div class="lbx-stepper">
                                            <button type="button" class="lbx-stepper__btn" data-step="-1" aria-label="<?php esc_attr_e('Decrease', 'leadbox'); ?>">−</button>
                                            <input type="number" id="lbx-cc-mobile" class="lbx-stepper__input" min="1" max="3" value="<?php echo esc_attr($ed_cc_mobile); ?>">
                                            <button type="button" class="lbx-stepper__btn" data-step="1" aria-label="<?php esc_attr_e('Increase', 'leadbox'); ?>">+</button>
                                        </div>
                                    </div>
                                    <div class="lbx-bp-field">
                                        <div class="lbx-bp-field__head">
                                            <span class="dashicons dashicons-tablet" aria-hidden="true"></span>
                                            <div class="lbx-bp-field__meta">
                                                <span class="lbx-bp-field__name"><?php _e('Tablet', 'leadbox'); ?></span>
                                                <span class="lbx-bp-field__range lbx-bp-range--tablet">≤ <?php echo (int) $ed_bp_tablet; ?>px</span>
                                            </div>
                                        </div>
                                        <div class="lbx-stepper">
                                            <button type="button" class="lbx-stepper__btn" data-step="-1" aria-label="<?php esc_attr_e('Decrease', 'leadbox'); ?>">−</button>
                                            <input type="number" id="lbx-cc-tablet" class="lbx-stepper__input" min="1" max="4" value="<?php echo esc_attr($ed_cc_tablet); ?>">
                                            <button type="button" class="lbx-stepper__btn" data-step="1" aria-label="<?php esc_attr_e('Increase', 'leadbox'); ?>">+</button>
                                        </div>
                                    </div>
                                    <div class="lbx-bp-field">
                                        <div class="lbx-bp-field__head">
                                            <span class="dashicons dashicons-desktop" aria-hidden="true"></span>
                                            <div class="lbx-bp-field__meta">
                                                <span class="lbx-bp-field__name"><?php _e('Desktop', 'leadbox'); ?></span>
                                                <span class="lbx-bp-field__range lbx-bp-range--desktop">&gt; <?php echo (int) $ed_bp_tablet; ?>px</span>
                                            </div>
                                        </div>
                                        <div class="lbx-stepper">
                                            <button type="button" class="lbx-stepper__btn" data-step="-1" aria-label="<?php esc_attr_e('Decrease', 'leadbox'); ?>">−</button>
                                            <input type="number" id="lbx-cc-desktop" class="lbx-stepper__input" min="1" max="6" value="<?php echo esc_attr($ed_cc_desktop); ?>">
                                            <button type="button" class="lbx-stepper__btn" data-step="1" aria-label="<?php esc_attr_e('Increase', 'leadbox'); ?>">+</button>
                                        </div>
                                    </div>
                                </div>
                            </div>
                    </div><!-- /panel Responsive Columns -->

                    <!-- OEM Options (only rendered visible for minerva source; toggled by updateLayoutVisibility) -->
                    <div class="lbx-panel-group" id="lbx-oem-options-panel">
                        <h4 class="lbx-panel-group__title"><span class="dashicons dashicons-car"></span> <?php _e('OEM Options', 'leadbox'); ?></h4>
                            <div id="lbx-show-inventory-wrap">
                                <label class="lbx-toggle">
                                    <input type="checkbox" id="lbx-show-inventory" <?php checked($ed_show_inv); ?>>
                                    <span class="lbx-toggle-track"></span>
                                    <span class="lbx-toggle-label"><?php _e('Show "View Inventory" button', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(only applies to OEM Offers)', 'leadbox'); ?></span></span>
                                </label>
                            </div>
                            <div id="lbx-hide-offer-data-wrap" style="margin-top: 10px;">
                                <label class="lbx-toggle">
                                    <input type="checkbox" id="lbx-hide-offer-data" <?php checked($ed_hide_offer_data); ?>>
                                    <span class="lbx-toggle-track"></span>
                                    <span class="lbx-toggle-label"><?php _e('Hide offer details (show image only)', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(OEM titles, APRs, payment & offer lines — CTAs stay visible; override per slide from the edit modal)', 'leadbox'); ?></span></span>
                                </label>
                            </div>
                            <div id="lbx-boxed-offer-text-wrap" style="margin-top: 10px;">
                                <label class="lbx-toggle">
                                    <input type="checkbox" id="lbx-boxed-offer-text" <?php checked($ed_boxed_offer_text); ?>>
                                    <span class="lbx-toggle-track"></span>
                                    <span class="lbx-toggle-label"><?php _e('Boxed offer text (pill style)', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(wraps APRs, payment and text-only offers in rounded pills with a semi-transparent background — override per slide from the edit modal)', 'leadbox'); ?></span></span>
                                </label>
                            </div>
                    </div><!-- /panel OEM Options -->

                    <!-- Slide Behavior -->
                    <div class="lbx-panel-group">
                        <h4 class="lbx-panel-group__title"><span class="dashicons dashicons-controls-play"></span> <?php _e('Slide Behavior', 'leadbox'); ?></h4>
                            <?php
                            $ed_autoplay = $editing_slider['autoplay'] ?? false;
                            $ed_autoplay_speed = $editing_slider['autoplay_speed'] ?? 5000;
                            $ed_loop = $editing_slider['loop'] ?? false;
                            ?>
                            <div class="lbx-design-grid">
                                <div class="lbx-field">
                                    <label class="lbx-toggle">
                                        <input type="checkbox" id="lbx-slider-autoplay" <?php checked($ed_autoplay); ?>>
                                        <span class="lbx-toggle-track"></span>
                                        <span class="lbx-toggle-label"><?php _e('Autoplay slides', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(advance automatically)', 'leadbox'); ?></span></span>
                                    </label>
                                </div>
                                <div class="lbx-field">
                                    <label class="lbx-toggle">
                                        <input type="checkbox" id="lbx-slider-loop" <?php checked($ed_loop); ?>>
                                        <span class="lbx-toggle-track"></span>
                                        <span class="lbx-toggle-label"><?php _e('Infinite Loop', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(restart from first slide)', 'leadbox'); ?></span></span>
                                    </label>
                                </div>
                            </div>
                            <div id="lbx-autoplay-speed-wrap" style="margin-top:12px;<?php echo $ed_autoplay ? '' : 'display:none;'; ?>">
                                <div class="lbx-field">
                                    <label class="lbx-label"><?php _e('Autoplay Speed', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(milliseconds between slides — 1000 = 1s)', 'leadbox'); ?></span></label>
                                    <input type="number" id="lbx-slider-autoplay-speed" class="lbx-input" min="500" max="20000" step="500" value="<?php echo esc_attr($ed_autoplay_speed); ?>" style="max-width:160px;">
                                </div>
                            </div>
                            <p style="margin:10px 0 0;padding:8px 12px;background:#eff6ff;border:1px solid #bfdbfe;border-radius:var(--lbx-radius-sm);font-size:11px;color:#1e40af;">
                                <span class="dashicons dashicons-info" style="font-size:14px;width:14px;height:14px;vertical-align:text-bottom;"></span>
                                <?php _e('Tip: Combine Autoplay + Infinite Loop for a continuously rotating carousel — perfect for partner logos or testimonial strips.', 'leadbox'); ?>
                            </p>
                    </div><!-- /panel Slide Behavior -->

                    <!-- Navigation -->
                    <div class="lbx-panel-group">
                        <h4 class="lbx-panel-group__title"><span class="dashicons dashicons-arrow-right-alt"></span> <?php _e('Navigation', 'leadbox'); ?></h4>
                            <?php
                            $ed_show_arrows = $editing_slider ? ($editing_slider['show_arrows'] ?? true) : true;
                            $ed_arrow_style = $editing_slider['arrow_style'] ?? 'circle';
                            $ed_arrow_color = $editing_slider['arrow_color'] ?? '';
                            $ed_arrow_opacity = (int) ($editing_slider['arrow_opacity'] ?? 90);
                            $ed_arrow_visibility = $editing_slider['arrow_visibility'] ?? 'always';
                            $ed_show_pagination = $editing_slider ? ($editing_slider['show_pagination'] ?? true) : true;
                            $ed_pagination_color = $editing_slider['pagination_color'] ?? '';
                            ?>
                            <div class="lbx-design-grid">
                                <div class="lbx-field">
                                    <label class="lbx-toggle">
                                        <input type="checkbox" id="lbx-slider-show-arrows" <?php checked($ed_show_arrows); ?>>
                                        <span class="lbx-toggle-track"></span>
                                        <span class="lbx-toggle-label"><?php _e('Show navigation arrows', 'leadbox'); ?></span>
                                    </label>
                                </div>
                                <div class="lbx-field">
                                    <label class="lbx-toggle">
                                        <input type="checkbox" id="lbx-slider-show-pagination" <?php checked($ed_show_pagination); ?>>
                                        <span class="lbx-toggle-track"></span>
                                        <span class="lbx-toggle-label"><?php _e('Show pagination dots', 'leadbox'); ?></span>
                                    </label>
                                </div>
                            </div>

                            <!-- Arrow customization (visible when arrows on) -->
                            <div id="lbx-arrows-options-wrap" style="margin-top:14px;<?php echo $ed_show_arrows ? '' : 'display:none;'; ?>">
                                <div class="lbx-design-grid">
                                    <div class="lbx-field">
                                        <label class="lbx-label"><?php _e('Arrow Style', 'leadbox'); ?></label>
                                        <select id="lbx-slider-arrow-style" class="lbx-input">
                                            <option value="circle" <?php selected($ed_arrow_style, 'circle'); ?>><?php _e('Circle', 'leadbox'); ?></option>
                                            <option value="square" <?php selected($ed_arrow_style, 'square'); ?>><?php _e('Square', 'leadbox'); ?></option>
                                            <option value="pill" <?php selected($ed_arrow_style, 'pill'); ?>><?php _e('Pill', 'leadbox'); ?></option>
                                            <option value="minimal" <?php selected($ed_arrow_style, 'minimal'); ?>><?php _e('Minimal (no background)', 'leadbox'); ?></option>
                                            <option value="dark" <?php selected($ed_arrow_style, 'dark'); ?>><?php _e('Dark', 'leadbox'); ?></option>
                                        </select>
                                    </div>
                                    <div class="lbx-field">
                                        <label class="lbx-label"><?php _e('Visibility', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(when to display)', 'leadbox'); ?></span></label>
                                        <select id="lbx-slider-arrow-visibility" class="lbx-input">
                                            <option value="always" <?php selected($ed_arrow_visibility, 'always'); ?>><?php _e('Always visible', 'leadbox'); ?></option>
                                            <option value="hover" <?php selected($ed_arrow_visibility, 'hover'); ?>><?php _e('Show on hover only', 'leadbox'); ?></option>
                                        </select>
                                    </div>
                                </div>
                                <div class="lbx-design-grid" style="margin-top:14px;">
                                    <div class="lbx-field">
                                        <label class="lbx-label"><?php _e('Arrow Color', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(optional, default per style)', 'leadbox'); ?></span></label>
                                        <label class="lbx-color-chip">
                                            <span class="lbx-color-chip__preview"><input type="color" class="lbx-cc-swatch" id="lbx-slider-arrow-color-picker" value="<?php echo esc_attr($ed_arrow_color ?: '#1d4ed8'); ?>"><span class="lbx-cc-fill" style="background:<?php echo esc_attr($ed_arrow_color ?: '#1d4ed8'); ?>"></span></span>
                                            <span class="lbx-color-chip__body"><input type="text" class="lbx-color-chip__hex" id="lbx-slider-arrow-color" value="<?php echo esc_attr($ed_arrow_color); ?>" placeholder="#1d4ed8"><span class="lbx-color-chip__label"><?php _e('Color', 'leadbox'); ?></span></span>
                                        </label>
                                    </div>
                                    <div class="lbx-field">
                                        <label class="lbx-label"><?php _e('Arrow Opacity', 'leadbox'); ?> <span class="lbx-label-hint">(<span id="lbx-slider-arrow-opacity-val"><?php echo esc_html($ed_arrow_opacity); ?></span>%)</span></label>
                                        <input type="range" id="lbx-slider-arrow-opacity" min="10" max="100" step="5" value="<?php echo esc_attr($ed_arrow_opacity); ?>" style="width:100%;">
                                    </div>
                                </div>
                            </div>

                            <!-- Pagination customization (visible when pagination on) -->
                            <div id="lbx-pagination-options-wrap" style="margin-top:14px;<?php echo $ed_show_pagination ? '' : 'display:none;'; ?>">
                                <div class="lbx-field">
                                    <label class="lbx-label"><?php _e('Pagination Dot Color', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(optional)', 'leadbox'); ?></span></label>
                                    <label class="lbx-color-chip">
                                        <span class="lbx-color-chip__preview"><input type="color" class="lbx-cc-swatch" id="lbx-slider-pagination-color-picker" value="<?php echo esc_attr($ed_pagination_color ?: '#ffffff'); ?>"><span class="lbx-cc-fill" style="background:<?php echo esc_attr($ed_pagination_color ?: '#ffffff'); ?>"></span></span>
                                        <span class="lbx-color-chip__body"><input type="text" class="lbx-color-chip__hex" id="lbx-slider-pagination-color" value="<?php echo esc_attr($ed_pagination_color); ?>" placeholder="#ffffff"><span class="lbx-color-chip__label"><?php _e('Color', 'leadbox'); ?></span></span>
                                    </label>
                                </div>
                            </div>

                    </div><!-- /panel Navigation -->

                    <!-- Watermark -->
                    <div class="lbx-panel-group">
                        <h4 class="lbx-panel-group__title"><span class="dashicons dashicons-format-image"></span> <?php _e('Watermark', 'leadbox'); ?> <span class="lbx-label-hint" style="text-transform:none;letter-spacing:normal;color:var(--lbx-gray-400);"><?php _e('(optional)', 'leadbox'); ?></span></h4>
                            <div class="lbx-design-grid">
                                <div class="lbx-field">
                                    <label class="lbx-label"><?php _e('Watermark Image', 'leadbox'); ?></label>
                                    <input type="hidden" id="lbx-watermark-image" value="<?php echo esc_attr($ed_wm_image); ?>">
                                    <div style="display:flex;align-items:center;gap:10px;margin-top:4px;">
                                        <img id="lbx-watermark-preview" src="<?php echo esc_url($ed_wm_image); ?>" alt="" style="max-width:80px;max-height:60px;object-fit:contain;border:1px solid var(--lbx-gray-200);border-radius:var(--lbx-radius-sm);background:var(--lbx-gray-50);padding:4px;<?php echo empty($ed_wm_image) ? 'display:none;' : ''; ?>">
                                        <button type="button" class="lbx-btn lbx-btn-secondary lbx-btn-sm" id="lbx-watermark-upload"><span class="dashicons dashicons-upload"></span> <?php _e('Upload', 'leadbox'); ?></button>
                                        <button type="button" class="lbx-btn lbx-btn-secondary lbx-btn-sm" id="lbx-watermark-remove" style="<?php echo empty($ed_wm_image) ? 'display:none;' : ''; ?>"><?php _e('Remove', 'leadbox'); ?></button>
                                    </div>
                                </div>
                                <div class="lbx-field">
                                    <label class="lbx-label"><?php _e('Position', 'leadbox'); ?></label>
                                    <select id="lbx-watermark-position" class="lbx-input" style="max-width:200px;">
                                        <option value="top-left" <?php selected($ed_wm_position, 'top-left'); ?>><?php _e('Top Left', 'leadbox'); ?></option>
                                        <option value="top-center" <?php selected($ed_wm_position, 'top-center'); ?>><?php _e('Top Center', 'leadbox'); ?></option>
                                        <option value="top-right" <?php selected($ed_wm_position, 'top-right'); ?>><?php _e('Top Right', 'leadbox'); ?></option>
                                        <option value="bottom-left" <?php selected($ed_wm_position, 'bottom-left'); ?>><?php _e('Bottom Left', 'leadbox'); ?></option>
                                        <option value="bottom-center" <?php selected($ed_wm_position, 'bottom-center'); ?>><?php _e('Bottom Center', 'leadbox'); ?></option>
                                        <option value="bottom-right" <?php selected($ed_wm_position, 'bottom-right'); ?>><?php _e('Bottom Right', 'leadbox'); ?></option>
                                    </select>
                                </div>
                                <div class="lbx-field">
                                    <label class="lbx-label"><?php _e('Size (px)', 'leadbox'); ?></label>
                                    <input type="number" id="lbx-watermark-size" class="lbx-input" value="<?php echo esc_attr($ed_wm_size); ?>" min="20" max="300" step="5" style="max-width:120px;">
                                    <span style="font-size:11px;color:var(--lbx-gray-400);"><?php _e('Height scales automatically', 'leadbox'); ?></span>
                                </div>
                            </div>
                    </div><!-- /panel Watermark -->

                    <!-- Disclaimer Icon (Minerva legal text) -->
                    <div class="lbx-panel-group" id="lbx-disclaimer-panel">
                        <h4 class="lbx-panel-group__title"><span class="dashicons dashicons-info-outline"></span> <?php _e('Disclaimer Icon', 'leadbox'); ?> <span class="lbx-label-hint" style="text-transform:none;letter-spacing:normal;color:var(--lbx-gray-400);"><?php _e('(legal text popup)', 'leadbox'); ?></span></h4>
                        <div style="display:flex;align-items:center;gap:24px;flex-wrap:wrap;">
                            <div class="lbx-field" style="margin-bottom:0;flex-shrink:0;">
                                <label class="lbx-label"><?php _e('Position', 'leadbox'); ?></label>
                                <select id="lbx-disclaimer-position" class="lbx-input" style="max-width:200px;">
                                    <option value="hidden"       <?php selected($ed_disclaimer_position, 'hidden'); ?>><?php _e('Hidden', 'leadbox'); ?></option>
                                    <option value="top-left"     <?php selected($ed_disclaimer_position, 'top-left'); ?>><?php _e('Top Left', 'leadbox'); ?></option>
                                    <option value="top-right"    <?php selected($ed_disclaimer_position, 'top-right'); ?>><?php _e('Top Right', 'leadbox'); ?></option>
                                    <option value="bottom-left"  <?php selected($ed_disclaimer_position, 'bottom-left'); ?>><?php _e('Bottom Left', 'leadbox'); ?></option>
                                    <option value="bottom-right" <?php selected($ed_disclaimer_position, 'bottom-right'); ?>><?php _e('Bottom Right', 'leadbox'); ?></option>
                                </select>
                            </div>
                            <p style="flex:1;min-width:240px;font-size:12px;color:var(--lbx-gray-500);line-height:1.5;margin:0;"><?php _e('A small (i) icon on each slide opens the offer\'s legal text. Hidden on slides that have none.', 'leadbox'); ?></p>
                        </div>
                    </div><!-- /panel Disclaimer -->

                    <!-- Video Playback -->
                    <div class="lbx-panel-group" id="lbx-video-playback-section">
                        <h4 class="lbx-panel-group__title"><span class="dashicons dashicons-format-video"></span> <?php _e('Video Playback', 'leadbox'); ?></h4>
                            <div style="display:flex;flex-direction:column;gap:10px;">
                                <label class="lbx-toggle">
                                    <input type="checkbox" id="lbx-video-autoplay" <?php checked($ed_video_autoplay); ?>>
                                    <span class="lbx-toggle-track"></span>
                                    <span class="lbx-toggle-label"><?php _e('Autoplay videos', 'leadbox'); ?></span>
                                </label>
                                <label class="lbx-toggle">
                                    <input type="checkbox" id="lbx-video-hover-play" <?php checked($ed_video_hover); ?>>
                                    <span class="lbx-toggle-track"></span>
                                    <span class="lbx-toggle-label"><?php _e('Play on hover only', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(desktop — overrides autoplay)', 'leadbox'); ?></span></span>
                                </label>
                                <label class="lbx-toggle">
                                    <input type="checkbox" id="lbx-video-loop" <?php checked($ed_video_loop); ?>>
                                    <span class="lbx-toggle-track"></span>
                                    <span class="lbx-toggle-label"><?php _e('Loop videos', 'leadbox'); ?></span>
                                </label>
                                <p style="font-size:11px;color:var(--lbx-gray-400);margin:4px 0 0;"><?php _e('Videos are always muted (browser autoplay policy). Keep files under 10 MB for best performance.', 'leadbox'); ?></p>
                            </div>
                    </div><!-- /panel Video Playback -->

                    </div><!-- /lbx-tab-panel design -->
                    <div class="lbx-tab-panel" data-tab="developer" role="tabpanel">
                    <!-- ─── Advanced (Custom CSS) ──────────────────────────────── -->
                    <?php
                    $ed_custom_css = $editing_slider['custom_css'] ?? '';
                    // Build a unique CSS scoping class from the slider ID.
                    $css_scope_id = $editing_id && $editing_id !== 'new' ? $editing_id : '';
                    $css_scope_class = $css_scope_id ? '.lbx-slider-id-' . preg_replace('/[^a-z0-9_-]/i', '', str_replace('slider_', '', $css_scope_id)) : '';
                    // If empty and we have a scope class, pre-fill a scaffold so the user can start typing right away.
                    $textarea_value = $ed_custom_css;
                    if (empty($textarea_value) && $css_scope_class) {
                        $textarea_value = $css_scope_class . " {\n    /* " . esc_html__('Your styles here', 'leadbox') . " */\n}\n";
                    }
                    ?>
                    <div class="lbx-card" style="margin: 24px 0 0; box-shadow: none; border: 1px solid var(--lbx-gray-200);">
                        <div class="lbx-card-header">
                            <h2><span class="dashicons dashicons-editor-code"></span> <?php _e('Advanced — Custom CSS', 'leadbox'); ?></h2>
                        </div>
                        <div class="lbx-card-body">
                            <p style="margin:0 0 12px;font-size:13px;color:var(--lbx-gray-500);">
                                <?php _e('Add custom CSS rules that will be applied only to this slider on the frontend. The scoping class is pre-filled — add more selectors as needed.', 'leadbox'); ?>
                            </p>
                            <?php if ($css_scope_class) : ?>
                            <div class="lbx-css-toolbar" style="display:flex;align-items:center;gap:10px;margin-bottom:8px;flex-wrap:wrap;">
                                <span style="font-size:11px;color:var(--lbx-gray-500);text-transform:uppercase;letter-spacing:0.04em;font-weight:600;"><?php _e('Scope class:', 'leadbox'); ?></span>
                                <code id="lbx-css-scope-class" style="background:#eff6ff;color:#1e40af;padding:4px 10px;border-radius:999px;font-size:11px;border:1px solid #bfdbfe;user-select:all;cursor:copy;" title="<?php esc_attr_e('Click to select — copy to paste in new rules', 'leadbox'); ?>"><?php echo esc_html($css_scope_class); ?></code>
                                <button type="button" id="lbx-css-insert-scope" class="button button-small" style="font-size:11px;">
                                    <span class="dashicons dashicons-plus-alt2" style="font-size:13px;width:13px;height:13px;vertical-align:text-bottom;"></span>
                                    <?php _e('Insert new rule', 'leadbox'); ?>
                                </button>
                                <button type="button" id="lbx-css-format" class="button button-small" style="font-size:11px;">
                                    <span class="dashicons dashicons-editor-alignleft" style="font-size:13px;width:13px;height:13px;vertical-align:text-bottom;"></span>
                                    <?php _e('Format CSS', 'leadbox'); ?>
                                </button>
                            </div>
                            <?php else : ?>
                            <div style="margin-bottom:12px;padding:10px 12px;background:#fef3c7;border:1px solid #fde68a;border-radius:var(--lbx-radius-sm);font-size:12px;color:#92400e;">
                                <span class="dashicons dashicons-info" style="font-size:14px;width:14px;height:14px;vertical-align:text-bottom;"></span>
                                <?php _e('Save the slider first to get its unique CSS scoping class.', 'leadbox'); ?>
                            </div>
                            <?php endif; ?>
                            <textarea id="lbx-slider-custom-css" data-scope-class="<?php echo esc_attr($css_scope_class); ?>" rows="10" spellcheck="false" style="width:100%;padding:12px;border:1px solid var(--lbx-gray-300);border-radius:var(--lbx-radius-sm);font-family:SFMono-Regular,Consolas,monospace;font-size:12px;line-height:1.6;background:#f9fafb;color:#1e293b;"><?php echo esc_textarea($textarea_value); ?></textarea>
                        </div>
                    </div>
                    </div><!-- /lbx-tab-panel developer -->
                    </div><!-- /lbx-tabs-content -->

                    <p class="lbx-field-legend" style="margin-top: 20px;">
                        <?php _e('Required fields are marked with', 'leadbox'); ?> <span class="lbx-req-star">*</span>. <?php _e('Custom sliders need at least one slide to save.', 'leadbox'); ?>
                    </p>
                </div>

                <div class="lbx-card-footer">
                    <button type="button" id="lbx-create-slider" class="lbx-btn lbx-btn-primary">
                        <span class="dashicons dashicons-saved"></span>
                        <span class="lbx-save-label"><?php echo $editing_slider ? __('Save Changes', 'leadbox') : __('Create Slider', 'leadbox'); ?></span>
                    </button>
                    <a href="<?php echo admin_url('admin.php?page=lbx-slider'); ?>" class="lbx-btn lbx-btn-secondary"><?php _e('Cancel', 'leadbox'); ?></a>
                </div>
            </div>
            <?php endif; ?>

            <?php if (!$editing_slider && $editing_id !== 'new') : ?>
            <!-- ═══ Dashboard Grid ═══ -->
            <div class="lbx-dashboard" id="lbx-dashboard">
                <?php foreach ($sliders as $id => $slider) :
                    $src = $slider['source'] ?? 'minerva';
                    $source_labels = ['minerva' => 'OEM Offers', 'custom' => 'Custom', 'countdown' => 'Countdown'];
                    $source_label = $source_labels[$src] ?? $src;
                    $layout_labels = ['carousel' => 'Carousel', 'offers-template' => 'Hero Banner', 'split' => 'Split Panel', 'media-columns' => 'Media Columns', 'oem-offer-hero' => 'OEM Offer Hero', 'countdown' => 'Countdown'];
                    $layout_val = $slider['layout'] ?? 'carousel';
                    $layout_label = $layout_labels[$layout_val] ?? $layout_val;

                    // Gather preview images for fan stack
                    $preview_imgs = [];
                    if ($src === 'custom') {
                        $slide_count = count($slider['custom_slides'] ?? []);
                        foreach (($slider['custom_slides'] ?? []) as $cs) {
                            if (!empty($cs['image_url'])) $preview_imgs[] = $cs['image_url'];
                            if (count($preview_imgs) >= 4) break;
                        }
                    } elseif ($src === 'countdown') {
                        $slide_count = 1;
                        if (!empty($slider['countdown_bg_image'])) {
                            $preview_imgs[] = $slider['countdown_bg_image'];
                        }
                    } else {
                        // Minerva: build the preview from the slider's REAL content — OEM offers
                        // (only when the OEM auto-populate switch is on), additional slides, and
                        // static offers (only when their switch is on), in the saved unified order
                        // when present. Mirrors what the frontend actually renders.
                        $slide_count = '—';
                        $has_admin = class_exists('LBX_Slider_Admin');
                        $auto_oem = !$has_admin || LBX_Slider_Admin::is_auto_populate_oem($slider);
                        $auto_static = $has_admin && LBX_Slider_Admin::is_auto_populate_static($slider);

                        // OEM offers by key (respect per-slide visibility), only if OEM is on.
                        $oem_by_key = [];
                        if ($auto_oem && $has_admin) {
                            $ss = $slider['slide_settings'] ?? [];
                            foreach (LBX_Slider_Admin::get_cached_minerva_offers() as $node) {
                                $o = $node['offers'] ?? [];
                                $key = preg_replace('/\s+/', '-', strtolower(implode('_', array_filter([$o['year'] ?? '', $o['make'] ?? '', $o['model'] ?? '']))));
                                if ($key === '') continue;
                                if (isset($ss[$key]) && ($ss[$key]['visible'] ?? true) === false) continue;
                                $oem_by_key[$key] = $o['mainImage']['sourceUrl'] ?? ($o['desktopImage']['sourceUrl'] ?? '');
                            }
                        }
                        // Additional slides by id.
                        $extra_by_id = [];
                        foreach (($slider['extra_slides'] ?? []) as $es) {
                            if (!empty($es['id'])) $extra_by_id[(string) $es['id']] = $es['image_url'] ?? '';
                        }
                        // Static offers by id (respect visibility), only if static is on.
                        $static_by_id = [];
                        if ($auto_static && $has_admin) {
                            $st = $slider['static_settings'] ?? [];
                            foreach (LBX_Slider_Admin::get_cached_static_offers() as $so) {
                                $sid = (string) ($so['id'] ?? '');
                                if ($sid === '') continue;
                                if (isset($st[$sid]['visible']) && $st[$sid]['visible'] === false) continue;
                                $static_by_id[$sid] = $so['image'] ?? ($so['image_mobile'] ?? '');
                            }
                        }
                        // Walk the unified order (if any), then append leftovers.
                        $ordered = [];
                        $unified = $slider['unified_slides'] ?? [];
                        if (is_array($unified)) {
                            foreach ($unified as $u) {
                                $t = $u['type'] ?? '';
                                if ($t === 'minerva' && array_key_exists($u['key'] ?? '', $oem_by_key)) { $ordered[] = $oem_by_key[$u['key']]; unset($oem_by_key[$u['key']]); }
                                elseif ($t === 'extra' && array_key_exists($u['id'] ?? '', $extra_by_id)) { $ordered[] = $extra_by_id[$u['id']]; unset($extra_by_id[$u['id']]); }
                                elseif ($t === 'static' && array_key_exists($u['key'] ?? '', $static_by_id)) { $ordered[] = $static_by_id[$u['key']]; unset($static_by_id[$u['key']]); }
                            }
                        }
                        $ordered = array_merge($ordered, array_values($extra_by_id), array_values($oem_by_key), array_values($static_by_id));

                        if (!empty($ordered)) {
                            $slide_count = count($ordered);
                            foreach ($ordered as $img) {
                                if (!empty($img)) $preview_imgs[] = $img;
                                if (count($preview_imgs) >= 4) break;
                            }
                        }
                    }
                    $preview_imgs = array_slice($preview_imgs, 0, 4);
                    $img_count = count($preview_imgs);
                ?>
                    <div class="lbx-dash-card" data-slider-id="<?php echo esc_attr($id); ?>">
                        <div class="lbx-dash-card__thumb">
                            <?php if ($img_count > 0) : ?>
                                <div class="lbx-fan lbx-fan--<?php echo $img_count; ?>">
                                    <?php foreach ($preview_imgs as $pi => $pimg) : ?>
                                        <img class="lbx-fan__img lbx-fan__img--<?php echo $pi; ?>" src="<?php echo esc_attr($pimg); ?>" alt="" loading="lazy">
                                    <?php endforeach; ?>
                                </div>
                            <?php else : ?>
                                <div class="lbx-dash-card__thumb-icon lbx-dash-card__thumb-icon--<?php echo esc_attr($src); ?>">
                                    <span class="dashicons dashicons-<?php echo $src === 'minerva' ? 'car' : ($src === 'countdown' ? 'clock' : 'images-alt2'); ?>"></span>
                                </div>
                            <?php endif; ?>
                            <?php
                            // Resolve schedule status (priority: disabled > expired > scheduled > active)
                            $now_ts = strtotime(current_time('mysql'));
                            $start_ts = !empty($slider['start_date']) ? strtotime(str_replace('T', ' ', $slider['start_date'])) : 0;
                            $end_ts = !empty($slider['end_date']) ? strtotime(str_replace('T', ' ', $slider['end_date'])) : 0;
                            $status_key = 'on'; $status_label = __('Active', 'leadbox'); $status_icon = 'yes-alt';
                            if (!$slider['enabled']) {
                                $status_key = 'off'; $status_label = __('Inactive', 'leadbox'); $status_icon = 'hidden';
                            } elseif ($end_ts && $now_ts > $end_ts) {
                                $status_key = 'expired'; $status_label = __('Expired', 'leadbox'); $status_icon = 'warning';
                            } elseif ($start_ts && $now_ts < $start_ts) {
                                $status_key = 'scheduled'; $status_label = __('Scheduled', 'leadbox'); $status_icon = 'calendar-alt';
                            }
                            ?>
                            <span class="lbx-dash-card__status lbx-dash-card__status--<?php echo $status_key; ?>">
                                <span class="dashicons dashicons-<?php echo $status_icon; ?>"></span>
                                <?php echo $status_label; ?>
                            </span>
                        </div>
                        <div class="lbx-dash-card__body">
                            <h3 class="lbx-dash-card__name"><?php echo esc_html($slider['name']); ?></h3>
                            <div class="lbx-dash-card__meta">
                                <span class="lbx-badge lbx-badge-<?php echo esc_attr($src); ?>"><?php echo esc_html($source_label); ?></span>
                                <span class="lbx-badge lbx-badge-layout"><?php echo esc_html($layout_label); ?></span>
                                <span class="lbx-badge lbx-badge-slides"><?php printf(__('%s slides', 'leadbox'), $slide_count); ?></span>
                            </div>
                            <?php
                            // Schedule info line — only when relevant
                            $schedule_msg = '';
                            $schedule_class = '';
                            if ($slider['enabled']) {
                                if ($status_key === 'expired') {
                                    $schedule_msg = sprintf(__('Expired on %s', 'leadbox'), date_i18n('M j, Y · g:i A', $end_ts));
                                    $schedule_class = 'lbx-dash-card__schedule--expired';
                                } elseif ($status_key === 'scheduled') {
                                    $schedule_msg = sprintf(__('Publishes on %s', 'leadbox'), date_i18n('M j, Y · g:i A', $start_ts));
                                    $schedule_class = 'lbx-dash-card__schedule--scheduled';
                                } elseif ($end_ts) {
                                    $schedule_msg = sprintf(__('Expires on %s', 'leadbox'), date_i18n('M j, Y · g:i A', $end_ts));
                                    $schedule_class = 'lbx-dash-card__schedule--expires';
                                }
                            }
                            if ($schedule_msg) : ?>
                                <div class="lbx-dash-card__schedule <?php echo $schedule_class; ?>">
                                    <span class="dashicons dashicons-clock"></span>
                                    <?php echo esc_html($schedule_msg); ?>
                                </div>
                            <?php endif; ?>
                            <span class="lbx-dash-card__id"><?php echo esc_html($id); ?></span>
                        </div>
                        <div class="lbx-dash-card__actions">
                            <a href="<?php echo admin_url('admin.php?page=lbx-slider&edit_slider=' . urlencode($id)); ?>" class="lbx-btn lbx-btn-primary lbx-btn-sm" onclick="event.stopPropagation();"><span class="dashicons dashicons-edit"></span> <?php _e('Edit', 'leadbox'); ?></a>
                            <button type="button" class="lbx-btn lbx-btn-secondary lbx-btn-sm lbx-dash-toggle" onclick="event.stopPropagation();"><span class="dashicons dashicons-<?php echo $slider['enabled'] ? 'visibility' : 'hidden'; ?>"></span> <?php echo $slider['enabled'] ? __('Disable', 'leadbox') : __('Enable', 'leadbox'); ?></button>
                            <button type="button" class="lbx-btn lbx-btn-secondary lbx-btn-sm lbx-dash-duplicate" onclick="event.stopPropagation();"><span class="dashicons dashicons-admin-page"></span> <?php _e('Duplicate', 'leadbox'); ?></button>
                            <button type="button" class="lbx-btn lbx-btn-danger lbx-btn-sm lbx-dash-delete" onclick="event.stopPropagation();"><span class="dashicons dashicons-trash"></span></button>
                        </div>
                    </div>
                <?php endforeach; ?>

                <a href="<?php echo admin_url('admin.php?page=lbx-slider&edit_slider=new'); ?>" class="lbx-dash-new">
                    <span class="dashicons dashicons-plus-alt"></span>
                    <span><?php _e('Create New Slider', 'leadbox'); ?></span>
                </a>
            </div>
            <?php endif; ?>

            <!-- Custom confirm dialog — replaces native confirm() across the whole slider admin -->
            <div id="lbx-confirm-modal" class="lbx-confirm-backdrop" role="dialog" aria-modal="true" aria-hidden="true">
                <div class="lbx-confirm-dialog" role="alertdialog" aria-labelledby="lbx-confirm-title" aria-describedby="lbx-confirm-message">
                    <div class="lbx-confirm-dialog__icon">
                        <span class="dashicons dashicons-warning"></span>
                    </div>
                    <h3 class="lbx-confirm-dialog__title" id="lbx-confirm-title"></h3>
                    <p class="lbx-confirm-dialog__message" id="lbx-confirm-message"></p>
                    <div class="lbx-confirm-dialog__actions">
                        <button type="button" class="lbx-confirm-btn lbx-confirm-btn--cancel" id="lbx-confirm-cancel"></button>
                        <button type="button" class="lbx-confirm-btn lbx-confirm-btn--confirm" id="lbx-confirm-ok" autofocus></button>
                    </div>
                </div>
            </div>

            <!-- OEM Slide Edit Modal -->
            <div id="lbx-oem-modal" class="lbx-oem-modal-backdrop">
                <div class="lbx-oem-modal">
                    <div class="lbx-oem-modal__header">
                        <img id="lbx-oem-modal-thumb" class="lbx-oem-modal__thumb" src="" alt="">
                        <div class="lbx-oem-modal__header-info">
                            <h3 id="lbx-oem-modal-title"></h3>
                            <p id="lbx-oem-modal-sub"></p>
                        </div>
                        <button type="button" class="lbx-oem-modal__close" id="lbx-oem-modal-close"><?php _e('Done', 'leadbox'); ?></button>
                    </div>
                    <div class="lbx-oem-modal__body">
                        <!-- Content tabs (EN / FR) — replaces the previous stacked EN-then-FR
                             sections. All field IDs are unchanged so saved data and the save
                             handler (which sweeps `.lbx-oem-modal-field`) keep working. -->
                        <div class="lbx-oem-modal__section">
                            <div class="lbx-oem-modal__lang-tabs" role="tablist" aria-label="<?php esc_attr_e('Content language', 'leadbox'); ?>">
                                <button type="button" class="lbx-oem-modal__lang-tab is-active" role="tab" aria-selected="true" data-lang="en">
                                    <span class="lbx-oem-modal__lang-tab-badge">EN</span>
                                    <span><?php _e('English Content', 'leadbox'); ?></span>
                                </button>
                                <button type="button" class="lbx-oem-modal__lang-tab" role="tab" aria-selected="false" data-lang="fr">
                                    <span class="lbx-oem-modal__lang-tab-badge">FR</span>
                                    <span><?php _e('French Content', 'leadbox'); ?></span>
                                </button>
                            </div>

                            <!-- ── EN panel ─────────────────────────────────────────── -->
                            <div class="lbx-oem-modal__lang-panel is-active" data-lang="en" role="tabpanel">
                                <!-- Group: Content (title + description) -->
                                <div class="lbx-oem-modal__group">
                                    <h6 class="lbx-oem-modal__group-title"><?php _e('Content', 'leadbox'); ?></h6>
                                    <div class="lbx-edit-row">
                                        <div>
                                            <label><?php _e('Title', 'leadbox'); ?></label>
                                            <input type="text" id="lbx-oem-modal-field-title" class="lbx-oem-modal-field" data-field="title">
                                        </div>
                                        <div>
                                            <label><?php _e('Description', 'leadbox'); ?></label>
                                            <textarea id="lbx-oem-modal-field-description" class="lbx-oem-modal-field" data-field="description" rows="2"></textarea>
                                        </div>
                                    </div>
                                </div>

                                <!-- Group: Learn More link (EN) — Shop Now / Learn More destination URL. -->
                                <div class="lbx-oem-modal__group">
                                    <h6 class="lbx-oem-modal__group-title"><?php _e('Learn More Link', 'leadbox'); ?></h6>
                                    <p style="margin:0 0 8px;font-size:11px;color:var(--lbx-gray-500);line-height:1.4;"><?php _e('Pre-filled with the offer\'s Learn More URL from the feed. Edit to customize for this slide, or clear to revert to the default.', 'leadbox'); ?></p>
                                    <div class="lbx-edit-row" style="grid-template-columns: 1fr;">
                                        <div>
                                            <input type="url" id="lbx-oem-modal-field-shopNowUrl" class="lbx-oem-modal-field" data-field="shopNowUrl" placeholder="https://...">
                                        </div>
                                    </div>
                                </div>

                                <!-- Group: Rates (Finance + Lease as paired sub-cards) -->
                                <div class="lbx-oem-modal__group">
                                    <h6 class="lbx-oem-modal__group-title"><?php _e('Rates', 'leadbox'); ?></h6>
                                    <div class="lbx-edit-row">
                                        <div class="lbx-oem-modal__sub-card">
                                            <div class="lbx-oem-modal__sub-card-title"><span class="dashicons dashicons-money-alt"></span> <?php _e('Finance', 'leadbox'); ?></div>
                                            <div class="lbx-edit-row">
                                                <div>
                                                    <label><?php _e('Label', 'leadbox'); ?></label>
                                                    <input type="text" id="lbx-oem-modal-field-financeLabel" class="lbx-oem-modal-field" data-field="financeLabel">
                                                </div>
                                                <div>
                                                    <label><?php _e('APR %', 'leadbox'); ?></label>
                                                    <input type="text" id="lbx-oem-modal-field-financeApr" class="lbx-oem-modal-field" data-field="financeApr">
                                                </div>
                                            </div>
                                        </div>
                                        <div class="lbx-oem-modal__sub-card">
                                            <div class="lbx-oem-modal__sub-card-title"><span class="dashicons dashicons-admin-network"></span> <?php _e('Lease', 'leadbox'); ?></div>
                                            <div class="lbx-edit-row">
                                                <div>
                                                    <label><?php _e('Label', 'leadbox'); ?></label>
                                                    <input type="text" id="lbx-oem-modal-field-leaseLabel" class="lbx-oem-modal-field" data-field="leaseLabel">
                                                </div>
                                                <div>
                                                    <label><?php _e('APR %', 'leadbox'); ?></label>
                                                    <input type="text" id="lbx-oem-modal-field-leaseApr" class="lbx-oem-modal-field" data-field="leaseApr">
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </div>

                                <!-- Group: Payment + Down Payment (paired sub-cards) -->
                                <div class="lbx-oem-modal__group">
                                    <h6 class="lbx-oem-modal__group-title"><?php _e('Payments', 'leadbox'); ?></h6>
                                    <div class="lbx-edit-row">
                                        <div class="lbx-oem-modal__sub-card">
                                            <div class="lbx-oem-modal__sub-card-title"><span class="dashicons dashicons-tag"></span> <?php _e('Payment', 'leadbox'); ?></div>
                                            <div class="lbx-edit-row">
                                                <div>
                                                    <label><?php _e('Amount', 'leadbox'); ?></label>
                                                    <input type="text" id="lbx-oem-modal-field-paymentAmount" class="lbx-oem-modal-field" data-field="paymentAmount">
                                                </div>
                                                <div>
                                                    <label><?php _e('Label', 'leadbox'); ?></label>
                                                    <input type="text" id="lbx-oem-modal-field-paymentLabel" class="lbx-oem-modal-field" data-field="paymentLabel">
                                                </div>
                                            </div>
                                        </div>
                                        <div class="lbx-oem-modal__sub-card">
                                            <div class="lbx-oem-modal__sub-card-title"><span class="dashicons dashicons-bank"></span> <?php _e('Down Payment', 'leadbox'); ?></div>
                                            <div class="lbx-edit-row">
                                                <div>
                                                    <label><?php _e('Amount', 'leadbox'); ?></label>
                                                    <input type="text" id="lbx-oem-modal-field-downPayment" class="lbx-oem-modal-field" data-field="downPayment">
                                                </div>
                                                <div>
                                                    <label><?php _e('Label', 'leadbox'); ?></label>
                                                    <input type="text" id="lbx-oem-modal-field-downPaymentLabel" class="lbx-oem-modal-field" data-field="downPaymentLabel">
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </div>

                                <!-- Group: Offer Lines -->
                                <div class="lbx-oem-modal__group">
                                    <h6 class="lbx-oem-modal__group-title"><?php _e('Offer Lines', 'leadbox'); ?></h6>
                                    <div class="lbx-edit-row">
                                        <div>
                                            <label><?php _e('Line 1', 'leadbox'); ?></label>
                                            <input type="text" id="lbx-oem-modal-field-normal1" class="lbx-oem-modal-field" data-field="normal1">
                                        </div>
                                        <div>
                                            <label><?php _e('Line 2', 'leadbox'); ?></label>
                                            <input type="text" id="lbx-oem-modal-field-normal2" class="lbx-oem-modal-field" data-field="normal2">
                                        </div>
                                    </div>
                                </div>

                                <!-- Group: Disclaimer (EN) — language-specific so it lives in the EN tab. -->
                                <div class="lbx-oem-modal__group">
                                    <h6 class="lbx-oem-modal__group-title"><span class="dashicons dashicons-info-outline" style="font-size:11px;width:11px;height:11px;vertical-align:text-bottom;margin-right:3px;"></span> <?php _e('Disclaimer', 'leadbox'); ?></h6>
                                    <p style="margin:0 0 8px;font-size:11px;color:var(--lbx-gray-500);line-height:1.4;"><?php _e('Pre-filled with the offer\'s default legal text. Edit to customize for this slide, or clear to revert to the default.', 'leadbox'); ?></p>
                                    <div class="lbx-edit-row" style="grid-template-columns: 1fr;">
                                        <div>
                                            <textarea id="lbx-oem-modal-field-disclaimer_en" class="lbx-oem-modal-field" data-field="disclaimer_en" rows="3" placeholder="<?php echo esc_attr__('Plain text or HTML.', 'leadbox'); ?>"></textarea>
                                        </div>
                                    </div>
                                </div>
                            </div><!-- /EN panel -->

                            <!-- ── FR panel (only translatable fields — amounts/APRs are universal) ── -->
                            <div class="lbx-oem-modal__lang-panel" data-lang="fr" role="tabpanel" hidden>
                                <p style="margin:0 0 14px;padding:8px 12px;background:#eff6ff;border:1px solid #bfdbfe;border-radius:8px;font-size:11px;color:#1e40af;line-height:1.5;">
                                    <span class="dashicons dashicons-info" style="font-size:13px;width:13px;height:13px;vertical-align:text-bottom;margin-right:4px;"></span>
                                    <?php _e('Only translatable text fields appear here. Numeric values (APRs, amounts) are universal and live in the English tab.', 'leadbox'); ?>
                                </p>

                                <!-- Group: Content (FR) -->
                                <div class="lbx-oem-modal__group">
                                    <h6 class="lbx-oem-modal__group-title"><?php _e('Content', 'leadbox'); ?> (FR)</h6>
                                    <div class="lbx-edit-row">
                                        <div>
                                            <label><?php _e('Title', 'leadbox'); ?> (FR)</label>
                                            <input type="text" id="lbx-oem-modal-field-title_fr" class="lbx-oem-modal-field" data-field="title_fr">
                                        </div>
                                        <div>
                                            <label><?php _e('Description', 'leadbox'); ?> (FR)</label>
                                            <textarea id="lbx-oem-modal-field-description_fr" class="lbx-oem-modal-field" data-field="description_fr" rows="2"></textarea>
                                        </div>
                                    </div>
                                </div>

                                <!-- Group: Learn More link (FR) -->
                                <div class="lbx-oem-modal__group">
                                    <h6 class="lbx-oem-modal__group-title"><?php _e('Learn More Link', 'leadbox'); ?> (FR)</h6>
                                    <div class="lbx-edit-row" style="grid-template-columns: 1fr;">
                                        <div>
                                            <input type="url" id="lbx-oem-modal-field-shopNowUrlFr" class="lbx-oem-modal-field" data-field="shopNowUrlFr" placeholder="https://...">
                                        </div>
                                    </div>
                                </div>

                                <!-- Group: Labels (FR) — all 4 in one row for parity with EN sub-cards -->
                                <div class="lbx-oem-modal__group">
                                    <h6 class="lbx-oem-modal__group-title"><?php _e('Labels', 'leadbox'); ?> (FR)</h6>
                                    <div class="lbx-edit-row lbx-edit-row--3col">
                                        <div>
                                            <label><?php _e('Finance Label', 'leadbox'); ?> (FR)</label>
                                            <input type="text" id="lbx-oem-modal-field-financeLabelFr" class="lbx-oem-modal-field" data-field="financeLabelFr">
                                        </div>
                                        <div>
                                            <label><?php _e('Lease Label', 'leadbox'); ?> (FR)</label>
                                            <input type="text" id="lbx-oem-modal-field-leaseLabelFr" class="lbx-oem-modal-field" data-field="leaseLabelFr">
                                        </div>
                                        <div>
                                            <label><?php _e('Payment Label', 'leadbox'); ?> (FR)</label>
                                            <input type="text" id="lbx-oem-modal-field-paymentLabelFr" class="lbx-oem-modal-field" data-field="paymentLabelFr">
                                        </div>
                                    </div>
                                    <div class="lbx-edit-row">
                                        <div>
                                            <label><?php _e('Down Payment Label', 'leadbox'); ?> (FR)</label>
                                            <input type="text" id="lbx-oem-modal-field-downPaymentLabelFr" class="lbx-oem-modal-field" data-field="downPaymentLabelFr">
                                        </div>
                                        <div><!-- Spacer to keep the 2-col rhythm. --></div>
                                    </div>
                                </div>

                                <!-- Group: Offer Lines (FR) -->
                                <div class="lbx-oem-modal__group">
                                    <h6 class="lbx-oem-modal__group-title"><?php _e('Offer Lines', 'leadbox'); ?> (FR)</h6>
                                    <div class="lbx-edit-row">
                                        <div>
                                            <label><?php _e('Line 1', 'leadbox'); ?> (FR)</label>
                                            <input type="text" id="lbx-oem-modal-field-normal1Fr" class="lbx-oem-modal-field" data-field="normal1Fr">
                                        </div>
                                        <div>
                                            <label><?php _e('Line 2', 'leadbox'); ?> (FR)</label>
                                            <input type="text" id="lbx-oem-modal-field-normal2Fr" class="lbx-oem-modal-field" data-field="normal2Fr">
                                        </div>
                                    </div>
                                </div>

                                <!-- Group: Disclaimer (FR) — language-specific so it lives in the FR tab. -->
                                <div class="lbx-oem-modal__group">
                                    <h6 class="lbx-oem-modal__group-title"><span class="dashicons dashicons-info-outline" style="font-size:11px;width:11px;height:11px;vertical-align:text-bottom;margin-right:3px;"></span> <?php _e('Disclaimer', 'leadbox'); ?> (FR)</h6>
                                    <p style="margin:0 0 8px;font-size:11px;color:var(--lbx-gray-500);line-height:1.4;"><?php _e('Pré-rempli avec le texte légal par défaut de l\'offre. Modifie pour personnaliser ce slide, ou vide le champ pour revenir au texte par défaut.', 'leadbox'); ?></p>
                                    <div class="lbx-edit-row" style="grid-template-columns: 1fr;">
                                        <div>
                                            <textarea id="lbx-oem-modal-field-disclaimer_fr" class="lbx-oem-modal-field" data-field="disclaimer_fr" rows="3" placeholder="<?php echo esc_attr__('Texte brut ou HTML.', 'leadbox'); ?>"></textarea>
                                        </div>
                                    </div>
                                </div>
                            </div><!-- /FR panel -->
                        </div>

                        <!-- CTA Section -->
                        <div class="lbx-oem-modal__section">
                            <div class="lbx-oem-modal__lang-label"><span>CTA</span> <?php _e('Call to Action', 'leadbox'); ?></div>
                            <p style="margin:0 0 10px;font-size:12px;color:var(--lbx-gray-500);"><?php _e('For Media Columns, this controls the per-slide button text and link. Positions only apply to overlay layouts.', 'leadbox'); ?></p>
                            <div id="lbx-oem-ctas-builder"></div>
                        </div>

                        <!-- Image Fit -->
                        <div class="lbx-oem-modal__section">
                            <div class="lbx-oem-modal__lang-label"><span>🖼️</span> <?php _e('Image Display', 'leadbox'); ?></div>
                            <div class="lbx-edit-row">
                                <div>
                                    <label><?php _e('Image Fit', 'leadbox'); ?></label>
                                    <select id="lbx-oem-modal-field-image_fit" class="lbx-oem-modal-field" data-field="image_fit">
                                        <option value="cover"><?php _e('Cover — Fill (may crop)', 'leadbox'); ?></option>
                                        <option value="contain"><?php _e('Contain — Fit entire image', 'leadbox'); ?></option>
                                        <option value="fill"><?php _e('Fill — Stretch to fill', 'leadbox'); ?></option>
                                        <option value="scale-down"><?php _e('Scale Down — Fit without enlarging', 'leadbox'); ?></option>
                                    </select>
                                </div>
                            </div>
                        </div>

                        <!-- Image Link Section -->
                        <div class="lbx-oem-modal__section">
                            <div class="lbx-oem-modal__lang-label"><span>🔗</span> <?php _e('Image Link', 'leadbox'); ?></div>
                            <div class="lbx-edit-row">
                                <div>
                                    <label><?php _e('Link URL', 'leadbox'); ?></label>
                                    <input type="text" id="lbx-oem-modal-field-image_link_url" class="lbx-oem-modal-field" data-field="image_link_url" placeholder="https://">
                                </div>
                                <div>
                                    <label><?php _e('Open In', 'leadbox'); ?></label>
                                    <select id="lbx-oem-modal-field-image_link_target" class="lbx-oem-modal-field" data-field="image_link_target">
                                        <option value="_self"><?php _e('Same tab', 'leadbox'); ?></option>
                                        <option value="_blank"><?php _e('New tab', 'leadbox'); ?></option>
                                    </select>
                                </div>
                            </div>
                        </div>

                        <!-- Media Override -->
                        <div class="lbx-oem-modal__section">
                            <div class="lbx-oem-modal__lang-label"><span>🎬</span> <?php _e('Media Override', 'leadbox'); ?></div>
                            <p style="margin:0 0 10px;font-size:12px;color:var(--lbx-gray-500);"><?php _e('Replace the default OEM image for this offer. Useful for showcasing a specific trim, video walkaround, or 360° tour.', 'leadbox'); ?></p>
                            <div class="lbx-edit-row">
                                <div>
                                    <label><?php _e('Media Type', 'leadbox'); ?></label>
                                    <select id="lbx-oem-modal-field-media_type_override" class="lbx-oem-modal-field" data-field="media_type_override">
                                        <option value=""><?php _e('Use OEM Image (default)', 'leadbox'); ?></option>
                                        <option value="image"><?php _e('Custom Image', 'leadbox'); ?></option>
                                        <option value="video"><?php _e('Custom Video (MP4/WebM)', 'leadbox'); ?></option>
                                        <option value="embed"><?php _e('HTML Embed (iframe)', 'leadbox'); ?></option>
                                    </select>
                                </div>
                            </div>
                            <!-- Image override -->
                            <div id="lbx-oem-image-override-wrap" class="lbx-edit-row" style="display:none;margin-top:10px;">
                                <div style="flex:1;">
                                    <label><?php _e('Custom Image', 'leadbox'); ?></label>
                                    <div style="display:flex;gap:8px;align-items:center;">
                                        <img id="lbx-oem-modal-image-preview" src="" alt="" style="max-width:80px;max-height:60px;object-fit:cover;border:1px solid var(--lbx-gray-200);border-radius:var(--lbx-radius-sm);background:var(--lbx-gray-50);display:none;">
                                        <input type="text" id="lbx-oem-modal-field-image_url_override" class="lbx-oem-modal-field" data-field="image_url_override" placeholder="https://..." style="flex:1;">
                                        <button type="button" id="lbx-oem-modal-image-upload" class="button button-small"><?php _e('Choose Image', 'leadbox'); ?></button>
                                    </div>
                                    <!-- Mobile image override — optional, shown only when media type is 'image'. -->
                                    <div style="margin-top:10px;padding-top:10px;border-top:1px dashed var(--lbx-gray-200);">
                                        <label style="font-size:10px;font-weight:600;color:var(--lbx-gray-500);text-transform:uppercase;letter-spacing:0.03em;"><span class="dashicons dashicons-smartphone" style="font-size:12px;width:12px;height:12px;vertical-align:text-bottom;"></span> <?php _e('Mobile version', 'leadbox'); ?> <span style="font-weight:400;color:var(--lbx-gray-400);text-transform:none;letter-spacing:0;"><?php _e('(optional)', 'leadbox'); ?></span></label>
                                        <div style="display:flex;gap:8px;align-items:center;margin-top:4px;">
                                            <img id="lbx-oem-modal-image-mobile-preview" src="" alt="" style="max-width:60px;max-height:45px;object-fit:cover;border:1px solid var(--lbx-gray-200);border-radius:var(--lbx-radius-sm);background:var(--lbx-gray-50);display:none;">
                                            <input type="text" id="lbx-oem-modal-field-image_url_mobile_override" class="lbx-oem-modal-field" data-field="image_url_mobile_override" placeholder="https://... (<1024px)" style="flex:1;">
                                            <button type="button" id="lbx-oem-modal-image-mobile-upload" class="button button-small"><?php _e('Choose', 'leadbox'); ?></button>
                                        </div>
                                        <p style="margin:4px 0 0;font-size:10px;color:var(--lbx-gray-400);"><?php _e('Used on mobile (≤640px). Falls back to the custom image above if empty.', 'leadbox'); ?></p>
                                    </div>
                                </div>
                            </div>
                            <!-- Video override -->
                            <div id="lbx-oem-video-override-wrap" class="lbx-edit-row" style="display:none;margin-top:10px;">
                                <div style="flex:1;">
                                    <label><?php _e('Custom Video URL', 'leadbox'); ?></label>
                                    <div style="display:flex;gap:8px;align-items:center;">
                                        <input type="url" id="lbx-oem-modal-field-video_url_override" class="lbx-oem-modal-field" data-field="video_url_override" placeholder="https://.../clip.mp4" style="flex:1;">
                                        <button type="button" id="lbx-oem-modal-video-upload" class="button button-small"><?php _e('Choose Video', 'leadbox'); ?></button>
                                    </div>
                                    <p style="margin:4px 0 0;font-size:11px;color:var(--lbx-gray-400);"><?php _e('Respects global Video Playback settings (autoplay, hover, loop).', 'leadbox'); ?></p>
                                </div>
                            </div>
                            <!-- Embed override -->
                            <div id="lbx-oem-embed-override-wrap" class="lbx-edit-row" style="display:none;margin-top:10px;">
                                <div style="flex:1;">
                                    <label><?php _e('Embed HTML', 'leadbox'); ?></label>
                                    <textarea id="lbx-oem-modal-field-embed_code_override" class="lbx-oem-modal-field" data-field="embed_code_override" rows="4" spellcheck="false" placeholder="<?php echo esc_attr__('Paste your iframe / embed HTML here…', 'leadbox'); ?>" style="width:100%;font-family:SFMono-Regular,Consolas,monospace;font-size:11px;line-height:1.5;background:#f9fafb;"></textarea>
                                </div>
                            </div>
                        </div>

                        <div class="lbx-oem-modal__section" style="margin-top:16px;display:flex;flex-direction:column;gap:10px;">
                            <label class="lbx-oem-checkbox-label">
                                <input type="checkbox" id="lbx-oem-modal-field-hide_watermark" class="lbx-oem-modal-field" data-field="hide_watermark">
                                <span><?php _e('Hide watermark on this slide', 'leadbox'); ?></span>
                            </label>
                            <label class="lbx-oem-checkbox-label">
                                <input type="checkbox" id="lbx-oem-modal-field-hide_offer_data" class="lbx-oem-modal-field" data-field="hide_offer_data">
                                <span><?php _e('Hide offer details on this slide', 'leadbox'); ?> <span style="font-size:11px;color:var(--lbx-gray-500);font-weight:400;">(<?php _e('title, APRs, payment & offer lines — image stays', 'leadbox'); ?>)</span></span>
                            </label>
                            <label class="lbx-oem-checkbox-label">
                                <input type="checkbox" id="lbx-oem-modal-field-boxed_offer_text" class="lbx-oem-modal-field" data-field="boxed_offer_text">
                                <span><?php _e('Boxed offer text on this slide', 'leadbox'); ?> <span style="font-size:11px;color:var(--lbx-gray-500);font-weight:400;">(<?php _e('pill style for APRs, payment & text-only offers', 'leadbox'); ?>)</span></span>
                            </label>
                            <label class="lbx-oem-checkbox-label">
                                <input type="checkbox" id="lbx-oem-modal-field-hide_disclaimer" class="lbx-oem-modal-field" data-field="hide_disclaimer">
                                <span><?php _e('Hide disclaimer icon on this slide', 'leadbox'); ?> <span style="font-size:11px;color:var(--lbx-gray-500);font-weight:400;">(<?php _e('overrides the slider-level position — icon won\'t show here even if enabled', 'leadbox'); ?>)</span></span>
                            </label>
                            <button type="button" class="lbx-btn lbx-btn-secondary lbx-btn-sm" id="lbx-oem-fl-configure" style="align-self:flex-start;">
                                <span class="dashicons dashicons-image-crop"></span> <?php _e('Animated frame line…', 'leadbox'); ?>
                            </button>
                        </div>
                    </div>
                    <div class="lbx-oem-modal__footer">
                        <button type="button" class="lbx-btn lbx-btn-primary lbx-btn-sm" id="lbx-oem-modal-done"><?php _e('Save & Close', 'leadbox'); ?></button>
                    </div>
                </div>
            </div>

            <!-- Custom Slide Edit Modal — hosts #lbx-custom-editor (moved out of the form) -->
            <div id="lbx-cs-modal" class="lbx-edit-modal-backdrop" aria-hidden="true" role="dialog" aria-modal="true" aria-labelledby="lbx-cs-modal-title">
                <div class="lbx-edit-modal">
                    <div class="lbx-edit-modal__header">
                        <div class="lbx-edit-modal__header-info">
                            <h3 id="lbx-cs-modal-title"><?php _e('Edit Slide', 'leadbox'); ?></h3>
                            <p><?php _e('Changes are kept as you edit. Close when you\'re done.', 'leadbox'); ?></p>
                        </div>
                        <button type="button" class="lbx-edit-modal__close" id="lbx-cs-modal-close"><?php _e('Done', 'leadbox'); ?></button>
                    </div>
                    <div class="lbx-edit-modal__body">
                        <div id="lbx-custom-editor" class="lbx-cs-editor-wrap"></div>
                    </div>
                </div>
            </div>

            <!-- Additional Slide Edit Modal — hosts #lbx-extra-editor (moved out of the form) -->
            <div id="lbx-ex-modal" class="lbx-edit-modal-backdrop" aria-hidden="true" role="dialog" aria-modal="true" aria-labelledby="lbx-ex-modal-title">
                <div class="lbx-edit-modal">
                    <div class="lbx-edit-modal__header">
                        <div class="lbx-edit-modal__header-info">
                            <h3 id="lbx-ex-modal-title"><?php _e('Edit Additional Slide', 'leadbox'); ?></h3>
                            <p><?php _e('Changes are kept as you edit. Close when you\'re done.', 'leadbox'); ?></p>
                        </div>
                        <button type="button" class="lbx-edit-modal__close" id="lbx-ex-modal-close"><?php _e('Done', 'leadbox'); ?></button>
                    </div>
                    <div class="lbx-edit-modal__body">
                        <div id="lbx-extra-editor"></div>
                    </div>
                </div>
            </div>

            <!-- Static Offer — minimal link-override modal -->
            <div id="lbx-static-modal" class="lbx-edit-modal-backdrop" aria-hidden="true" role="dialog" aria-modal="true" aria-labelledby="lbx-static-modal-title">
                <div class="lbx-edit-modal" style="max-width:860px;">
                    <div class="lbx-edit-modal__header">
                        <div class="lbx-edit-modal__header-info">
                            <h3 id="lbx-static-modal-title"><?php _e('OEM Static Offer', 'leadbox'); ?></h3>
                            <p><?php _e('Optionally override the link this slide points to.', 'leadbox'); ?></p>
                        </div>
                        <button type="button" class="lbx-edit-modal__close" id="lbx-static-modal-close"><?php _e('Done', 'leadbox'); ?></button>
                    </div>
                    <div class="lbx-edit-modal__body">
                        <div style="display:flex; gap:14px; align-items:flex-start;">
                            <img id="lbx-static-modal-thumb" src="" alt="" style="width:140px; height:auto; border-radius:8px; border:1px solid var(--lbx-gray-200); background:var(--lbx-gray-50); flex-shrink:0;">
                            <div style="flex:1; min-width:0;">
                                <div class="lbx-slide-field" style="margin-bottom:12px;">
                                    <label><?php _e('Link URL', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(optional — overrides the offer\'s own link)', 'leadbox'); ?></span></label>
                                    <input type="url" id="lbx-static-modal-link" placeholder="https://">
                                </div>
                                <div class="lbx-slide-field">
                                    <label><?php _e('Open link in', 'leadbox'); ?></label>
                                    <select id="lbx-static-modal-target" class="lbx-input" style="max-width:200px;">
                                        <option value="_self"><?php _e('Same tab', 'leadbox'); ?></option>
                                        <option value="_blank"><?php _e('New tab', 'leadbox'); ?></option>
                                    </select>
                                </div>
                                <div class="lbx-slide-field" style="margin-top:12px;">
                                    <button type="button" class="lbx-btn lbx-btn-secondary lbx-btn-sm" id="lbx-static-fl-configure">
                                        <span class="dashicons dashicons-image-crop"></span> <?php _e('Animated frame line…', 'leadbox'); ?>
                                    </button>
                                </div>
                                <div class="lbx-slide-field" id="lbx-static-modal-disclaimer-wrap" style="margin-top:14px; display:none;">
                                    <label><?php _e('Disclaimer', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(shown in the slide\'s info popup)', 'leadbox'); ?></span></label>
                                    <div id="lbx-static-modal-disclaimer" style="font-size:12px; line-height:1.45; color:var(--lbx-gray-600); background:var(--lbx-gray-50); border:1px solid var(--lbx-gray-200); border-radius:6px; padding:8px 10px; max-height:140px; overflow:auto;"></div>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="lbx-edit-modal__footer" style="padding:14px 24px; border-top:1px solid var(--lbx-gray-200); display:flex; justify-content:flex-end;">
                        <button type="button" class="lbx-btn lbx-btn-primary lbx-btn-sm" id="lbx-static-modal-done"><?php _e('Save & Close', 'leadbox'); ?></button>
                    </div>
                </div>
            </div>

            <!-- ═══ Animated Frame Line — PER-SLIDE settings modal. Opened from the slide
                 editors (OEM / Additional / Static); stacks ABOVE them (higher z-index).
                 JS loads/saves the active slide's own frame_line object. ═══ -->
            <div id="lbx-fl-modal" class="lbx-edit-modal-backdrop" aria-hidden="true" role="dialog" aria-modal="true" aria-labelledby="lbx-fl-modal-title">
                <div class="lbx-edit-modal" style="max-width:620px;">
                    <div class="lbx-edit-modal__header">
                        <div class="lbx-edit-modal__header-info">
                            <h3 id="lbx-fl-modal-title"><?php _e('Animated Frame Line', 'leadbox'); ?></h3>
                            <p><?php _e('Per-slide effect: two mirrored lines travel the slider frame while this slide is showing.', 'leadbox'); ?></p>
                        </div>
                        <button type="button" class="lbx-edit-modal__close" id="lbx-fl-modal-close"><?php _e('Done', 'leadbox'); ?></button>
                    </div>
                    <div class="lbx-edit-modal__body">
                        <label class="lbx-fl-check" style="margin:0 0 14px;">
                            <input type="checkbox" id="lbx-fl-enabled">
                            <?php _e('Enable on this slide', 'leadbox'); ?>
                        </label>
                        <!-- Live mini-preview: same two-half-paths technique as the frontend -->
                        <div class="lbx-fl-preview" id="lbx-fl-preview" aria-hidden="true">
                            <svg><path pathLength="100" d="M0 0"></path><path pathLength="100" d="M0 0"></path></svg>
                        </div>
                        <div class="lbx-panel-grid" style="margin-top:18px;">
                            <div class="lbx-slide-field">
                                <label for="lbx-fl-style"><?php _e('Animation style', 'leadbox'); ?></label>
                                <select id="lbx-fl-style" class="lbx-input" style="max-width:260px;">
                                    <option value="travel"><?php _e('Traveling dash (loops forever)', 'leadbox'); ?></option>
                                    <option value="draw"><?php _e('Draw once & stay (full frame)', 'leadbox'); ?></option>
                                </select>
                            </div>
                            <div class="lbx-slide-field">
                                <label for="lbx-fl-speed"><?php _e('Speed', 'leadbox'); ?> <output id="lbx-fl-speed-out" style="float:right;font-weight:600;">9s</output></label>
                                <input type="range" id="lbx-fl-speed" min="4" max="18" step="0.5" value="9" style="width:100%;">
                            </div>
                            <div class="lbx-slide-field" id="lbx-fl-length-field">
                                <label for="lbx-fl-length"><?php _e('Line length', 'leadbox'); ?> <output id="lbx-fl-length-out" style="float:right;font-weight:600;">18%</output></label>
                                <input type="range" id="lbx-fl-length" min="6" max="40" step="1" value="18" style="width:100%;">
                            </div>
                            <div class="lbx-slide-field">
                                <label for="lbx-fl-width"><?php _e('Thickness', 'leadbox'); ?> <output id="lbx-fl-width-out" style="float:right;font-weight:600;">4px</output></label>
                                <input type="range" id="lbx-fl-width" min="2" max="8" step="0.5" value="4" style="width:100%;">
                            </div>
                            <div class="lbx-slide-field">
                                <label><?php _e('Line color', 'leadbox'); ?></label>
                                <div style="display:flex;align-items:center;gap:8px;">
                                    <button type="button" class="lbx-fl-swatch" data-c="#ffffff" style="background:#fff;" aria-label="<?php esc_attr_e('White', 'leadbox'); ?>"></button>
                                    <button type="button" class="lbx-fl-swatch" data-c="#1c1c1c" style="background:#1c1c1c;" aria-label="<?php esc_attr_e('Black', 'leadbox'); ?>"></button>
                                    <input type="color" id="lbx-fl-color" value="#ffffff" aria-label="<?php esc_attr_e('Custom color', 'leadbox'); ?>" style="width:38px;height:28px;padding:1px;border:1px solid var(--lbx-gray-200);border-radius:6px;background:#fff;cursor:pointer;">
                                </div>
                            </div>
                            <div class="lbx-slide-field">
                                <label class="lbx-fl-check">
                                    <input type="checkbox" id="lbx-fl-glow">
                                    <?php _e('Glow', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(soft light around the line)', 'leadbox'); ?></span>
                                </label>
                            </div>
                            <div class="lbx-slide-field">
                                <label class="lbx-fl-check">
                                    <input type="checkbox" id="lbx-fl-outline">
                                    <?php _e('Contrast outline', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(dark halo — keeps a light line visible on light backgrounds)', 'leadbox'); ?></span>
                                </label>
                            </div>
                        </div>
                    </div>
                    <div class="lbx-edit-modal__footer" style="padding:14px 24px; border-top:1px solid var(--lbx-gray-200); display:flex; justify-content:flex-end;">
                        <button type="button" class="lbx-btn lbx-btn-primary lbx-btn-sm" id="lbx-fl-modal-done"><?php _e('Save & Close', 'leadbox'); ?></button>
                    </div>
                </div>
            </div>

            <!-- ═══ Live Preview Modal ═══ -->
            <!-- Renders the slider in an iframe pointed at a chrome-free frontend preview
                 URL, so it uses the theme's own assets and looks identical to production.
                 The toolbar resizes the iframe to test real responsive breakpoints. -->
            <div id="lbx-preview-modal" class="lbx-preview-backdrop" aria-hidden="true" role="dialog" aria-modal="true" aria-label="<?php esc_attr_e('Slider live preview', 'leadbox'); ?>">
                <div class="lbx-preview-shell">
                    <div class="lbx-preview-toolbar">
                        <div class="lbx-preview-toolbar__group lbx-preview-toolbar__left">
                            <span class="lbx-preview-title"><span class="dashicons dashicons-visibility"></span> <?php _e('Live Preview', 'leadbox'); ?></span>
                            <span class="lbx-preview-dims" id="lbx-preview-dims">—</span>
                        </div>
                        <div class="lbx-preview-toolbar__group lbx-preview-toolbar__center">
                            <div class="lbx-preview-devices" role="tablist">
                                <button type="button" class="lbx-preview-device is-active" data-w="0" title="<?php esc_attr_e('Desktop (full width)', 'leadbox'); ?>"><span class="dashicons dashicons-desktop"></span></button>
                                <button type="button" class="lbx-preview-device" data-w="1280" title="<?php esc_attr_e('Laptop — 1280px', 'leadbox'); ?>"><span class="dashicons dashicons-laptop"></span></button>
                                <button type="button" class="lbx-preview-device" data-w="768" title="<?php esc_attr_e('Tablet — 768px', 'leadbox'); ?>"><span class="dashicons dashicons-tablet"></span></button>
                                <button type="button" class="lbx-preview-device" data-w="375" title="<?php esc_attr_e('Mobile — 375px', 'leadbox'); ?>"><span class="dashicons dashicons-smartphone"></span></button>
                            </div>
                            <div class="lbx-preview-width">
                                <input type="range" id="lbx-preview-width-range" min="320" max="1920" step="5" value="1280" aria-label="<?php esc_attr_e('Preview width', 'leadbox'); ?>">
                                <div class="lbx-preview-width-field">
                                    <input type="number" id="lbx-preview-width-num" min="320" max="3840" step="1" aria-label="<?php esc_attr_e('Preview width in pixels', 'leadbox'); ?>">
                                    <span>px</span>
                                </div>
                            </div>
                        </div>
                        <div class="lbx-preview-toolbar__group lbx-preview-toolbar__right">
                            <div class="lbx-preview-lang" role="group" aria-label="<?php esc_attr_e('Preview language', 'leadbox'); ?>">
                                <button type="button" class="lbx-preview-lang-btn is-active" data-lang="en">EN</button>
                                <button type="button" class="lbx-preview-lang-btn" data-lang="fr">FR</button>
                            </div>
                            <button type="button" class="lbx-preview-iconbtn" id="lbx-preview-refresh" title="<?php esc_attr_e('Reload preview', 'leadbox'); ?>"><span class="dashicons dashicons-update"></span></button>
                            <button type="button" class="lbx-preview-iconbtn" id="lbx-preview-newtab" title="<?php esc_attr_e('Open in new tab', 'leadbox'); ?>"><span class="dashicons dashicons-external"></span></button>
                            <button type="button" class="lbx-preview-iconbtn lbx-preview-iconbtn--close" id="lbx-preview-close" title="<?php esc_attr_e('Close (Esc)', 'leadbox'); ?>"><span class="dashicons dashicons-no-alt"></span></button>
                        </div>
                    </div>
                    <div class="lbx-preview-stage">
                        <div class="lbx-preview-frame-wrap" id="lbx-preview-frame-wrap">
                            <div class="lbx-preview-handle lbx-preview-handle--left" data-side="left" title="<?php esc_attr_e('Drag to resize', 'leadbox'); ?>"></div>
                            <div class="lbx-preview-viewport" id="lbx-preview-viewport">
                                <!-- Canvas holds the iframe at its LOGICAL width and is scaled
                                     down with a transform when it exceeds the stage, so the
                                     slider still renders at the chosen breakpoint but never
                                     forces horizontal scroll. The outer viewport is sized to
                                     the SCALED dimensions (set by JS). -->
                                <div class="lbx-preview-canvas" id="lbx-preview-canvas">
                                    <div class="lbx-preview-loading" id="lbx-preview-loading">
                                        <span class="spinner is-active"></span> <?php _e('Loading preview…', 'leadbox'); ?>
                                    </div>
                                    <div class="lbx-preview-error" id="lbx-preview-error" style="display:none;"></div>
                                    <iframe id="lbx-preview-iframe" title="<?php esc_attr_e('Slider preview', 'leadbox'); ?>" referrerpolicy="same-origin"></iframe>
                                </div>
                            </div>
                            <div class="lbx-preview-handle lbx-preview-handle--right" data-side="right" title="<?php esc_attr_e('Drag to resize', 'leadbox'); ?>"></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <script>
        (function($) {
            var nonce = '<?php echo $nonce; ?>';
            var ajaxurl = '<?php echo admin_url('admin-ajax.php'); ?>';

            // On NEW slider creation, scroll straight down to the tabs so the user
            // sees the General settings without having to scroll past the slides
            // section. Existing sliders keep the default position (top) because the
            // user often just wants to reorder/tweak slides, which are already at top.
            $(function() {
                if ($('#edit-slider-id').val() !== 'new') return;
                var $tabs = $('.lbx-tabs-nav').first();
                if (!$tabs.length) return;
                // Small delay so layout (sticky header, admin bar) has settled.
                setTimeout(function() {
                    $tabs[0].scrollIntoView({ behavior: 'smooth', block: 'start' });
                }, 150);
            });

            // ─── Edit form tabs ─────────────────────────────────────
            // Progressive enhancement: if tabs are missing for any reason, we fall
            // back to the legacy stacked layout (all panels visible) so the form
            // stays usable and no field becomes unreachable.
            $(document).on('click', '.lbx-tabs-nav .lbx-tab', function(e) {
                e.preventDefault();
                var $btn = $(this);
                var tab = $btn.data('tab');
                if (!tab) return;
                var $nav = $btn.closest('.lbx-tabs-nav');
                var $allTabs = $nav.find('.lbx-tab');
                $allTabs.removeClass('is-active').attr('aria-selected', 'false');
                $btn.addClass('is-active').attr('aria-selected', 'true');
                // Drive the magic pill: the ::before transform is bound to --lbx-tab-index.
                var idx = $allTabs.index($btn);
                if (idx >= 0) {
                    $nav[0].style.setProperty('--lbx-tab-index', idx);
                    $nav[0].style.setProperty('--lbx-tab-count', $allTabs.length);
                }
                var $container = $nav.next('.lbx-tabs-content');
                if (!$container.length) $container = $('.lbx-tabs-content').first();
                $container.find('> .lbx-tab-panel').removeClass('is-active');
                $container.find('> .lbx-tab-panel[data-tab="' + tab + '"]').addClass('is-active');
                // CodeMirror can't measure itself while its panel is display:none (it
                // initializes with the Developer tab hidden → blank/misaligned until
                // clicked). Repaint it as soon as its panel becomes visible.
                if (tab === 'developer' && typeof lbxCssCM !== 'undefined' && lbxCssCM) {
                    setTimeout(function() { lbxCssCM.refresh(); }, 30);
                }
            });

            // Initial state: position the pill under the tab that came in is-active.
            // Also keeps --lbx-tab-count accurate if the number of tabs ever changes.
            $(function() {
                $('.lbx-tabs-nav').each(function() {
                    var $nav = $(this);
                    var $allTabs = $nav.find('.lbx-tab');
                    var idx = $allTabs.index($allTabs.filter('.is-active'));
                    if (idx < 0) idx = 0;
                    this.style.setProperty('--lbx-tab-index', idx);
                    this.style.setProperty('--lbx-tab-count', $allTabs.length);
                });
            });

            // Sticky-elevation sensor: adds .is-stuck to the nav once it has scrolled
            // up to its sticky threshold so we can intensify the shadow + saturation.
            (function() {
                var $nav = null;
                var threshold = 32;
                var rafId = null;
                function check() {
                    if (!$nav) {
                        $nav = $('.lbx-tabs-nav').first();
                        if (!$nav.length) return;
                        threshold = parseInt(getComputedStyle($nav[0]).top, 10);
                        if (isNaN(threshold)) threshold = 32;
                    }
                    var rect = $nav[0].getBoundingClientRect();
                    $nav.toggleClass('is-stuck', rect.top <= threshold + 1);
                    rafId = null;
                }
                $(window).on('scroll.lbxStickyTabs', function() {
                    if (rafId) return;
                    rafId = requestAnimationFrame(check);
                });
                $(function() { check(); });
            })();

            // Activate the tab that contains the given element (or selector).
            // No-op when tabs aren't present (legacy layout) or when already active.
            function activateTabContaining(target) {
                var $el = target && target.jquery ? target : $(target);
                if (!$el.length) return;
                var $panel = $el.closest('.lbx-tab-panel');
                if (!$panel.length || $panel.hasClass('is-active')) return;
                var tab = $panel.attr('data-tab');
                if (!tab) return;
                $('.lbx-tabs-nav .lbx-tab[data-tab="' + tab + '"]').trigger('click');
            }
            window.lbxActivateTabContaining = activateTabContaining;

            function showNotice(msg, type) {
                var cls = type === 'error' ? 'lbx-notice-error' : (type === 'info' ? 'lbx-notice-info' : 'lbx-notice-success');
                var icon = type === 'error' ? 'warning' : (type === 'info' ? 'info' : 'yes-alt');
                $('#lbx-slider-notices').html('<div class="lbx-notice ' + cls + '"><span class="dashicons dashicons-' + icon + '"></span>' + msg + '</div>');
                setTimeout(function() { $('#lbx-slider-notices .lbx-notice').fadeOut(400, function() { $(this).remove(); }); }, 6000);
            }

            /**
             * Custom confirm dialog — drop-in replacement for window.confirm().
             * Returns a Promise that resolves to true (OK) or false (Cancel/ESC/backdrop).
             *
             * Usage:
             *   lbxConfirm({
             *       title: 'Remove this slide?',
             *       message: 'This cannot be undone.',
             *       confirmText: 'Remove',
             *       cancelText: 'Cancel',
             *       danger: true   // false => primary blue (for non-destructive confirms)
             *   }).then(function(ok) { if (ok) { ...do it... } });
             */
            var lbxConfirmState = { resolver: null, cancelHandler: null, keyHandler: null };
            function lbxConfirm(opts) {
                opts = opts || {};
                var $modal = $('#lbx-confirm-modal');
                var $dialog = $modal.find('.lbx-confirm-dialog');
                var $okBtn = $('#lbx-confirm-ok');
                var $cancelBtn = $('#lbx-confirm-cancel');

                $('#lbx-confirm-title').text(opts.title || '<?php echo esc_js(__('Are you sure?', 'leadbox')); ?>');
                $('#lbx-confirm-message').text(opts.message || '');
                $okBtn.text(opts.confirmText || '<?php echo esc_js(__('Confirm', 'leadbox')); ?>');
                $cancelBtn.text(opts.cancelText || '<?php echo esc_js(__('Cancel', 'leadbox')); ?>');
                $dialog.toggleClass('lbx-confirm-dialog--info', opts.danger === false);
                $dialog.toggleClass('lbx-confirm-dialog--danger', opts.danger !== false);

                $modal.addClass('is-open').attr('aria-hidden', 'false');
                // Focus the primary button after the enter animation so keyboard users can confirm with Enter.
                setTimeout(function() { $okBtn.trigger('focus'); }, 50);

                return new Promise(function(resolve) {
                    // Detach any stale handlers from a previous invocation.
                    if (lbxConfirmState.cancelHandler) $modal.add($okBtn).add($cancelBtn).off('click.lbxConfirm');
                    if (lbxConfirmState.keyHandler) $(document).off('keydown.lbxConfirm');

                    function close(result) {
                        $modal.removeClass('is-open').attr('aria-hidden', 'true');
                        $modal.add($okBtn).add($cancelBtn).off('click.lbxConfirm');
                        $(document).off('keydown.lbxConfirm');
                        resolve(result);
                    }

                    $okBtn.one('click.lbxConfirm', function() { close(true); });
                    $cancelBtn.one('click.lbxConfirm', function() { close(false); });
                    // Click on backdrop (not on the dialog itself) cancels.
                    $modal.on('click.lbxConfirm', function(e) {
                        if (e.target === this) close(false);
                    });
                    // Keyboard: ESC = cancel, Enter = confirm.
                    $(document).on('keydown.lbxConfirm', function(e) {
                        if (e.key === 'Escape') { e.preventDefault(); close(false); }
                        else if (e.key === 'Enter') { e.preventDefault(); close(true); }
                    });
                });
            }
            // Expose globally so it can be used from any script on the slider admin page.
            window.lbxConfirm = lbxConfirm;

            // ─── Radio-card toggle (generic) ──────────────────────────
            $(document).on('click', '.lbx-radio-card', function() {
                $(this).closest('.lbx-radio-cards').find('.lbx-radio-card').removeClass('active');
                $(this).addClass('active');
            });

            // ─── Stepper widget (+/- buttons on number inputs) ────────
            // Updates the sibling number input respecting min/max bounds and
            // mirrors current state to the ± button disabled attribute so the
            // user gets clear visual feedback at the limits.
            function updateStepperState($input) {
                var min = parseInt($input.attr('min'), 10);
                var max = parseInt($input.attr('max'), 10);
                var cur = parseInt($input.val(), 10);
                if (!isNaN(min)) $input.siblings('.lbx-stepper__btn[data-step="-1"]').prop('disabled', !isNaN(cur) && cur <= min);
                if (!isNaN(max)) $input.siblings('.lbx-stepper__btn[data-step="1"]').prop('disabled', !isNaN(cur) && cur >= max);
            }
            $(document).on('click', '.lbx-stepper__btn', function(e) {
                e.preventDefault();
                var $btn = $(this);
                if ($btn.prop('disabled')) return;
                var step = parseInt($btn.data('step'), 10) || 0;
                var $input = $btn.siblings('.lbx-stepper__input');
                var min = parseInt($input.attr('min'), 10);
                var max = parseInt($input.attr('max'), 10);
                var cur = parseInt($input.val(), 10);
                if (isNaN(cur)) cur = isNaN(min) ? 1 : min;
                var next = cur + step;
                if (!isNaN(min)) next = Math.max(min, next);
                if (!isNaN(max)) next = Math.min(max, next);
                $input.val(next).trigger('change');
            });
            $(document).on('input change', '.lbx-stepper__input', function() {
                updateStepperState($(this));
            });
            // Initialize disabled state on page load for every stepper input.
            $(function() { $('.lbx-stepper__input').each(function() { updateStepperState($(this)); }); });

            // ─── Layout: hide Split/Hero only for countdown ──────
            // The "Refresh data" header button only makes sense when the slider actually pulls
            // offers from Minerva — i.e. it's a Minerva slider AND the OEM and/or Static switch
            // is ON. A custom-only / countdown slider has no feed data to refresh, so it stays
            // hidden even though the slider source is "minerva".
            function updateRefreshDataBtn() {
                var s = $('input[name="slider_source"]:checked').val() || 'minerva';
                var pullsMinerva = s === 'minerva' && ($('#lbx-auto-oem').is(':checked') || $('#lbx-auto-static').is(':checked'));
                $('#lbx-refresh-minerva').toggle(pullsMinerva);
            }

            function updateLayoutVisibility() {
                var src = $('input[name="slider_source"]:checked').val() || 'minerva';
                var $split = $('#lbx-layout-split');
                var $hero = $('#lbx-layout-hero');
                var $mcBp = $('#lbx-media-columns-breakpoints');
                var $ccBp = $('#lbx-carousel-columns-breakpoints');
                var selectedLayout = $('input[name="slider_layout"]:checked').val() || 'carousel';
                var isCountdown = src === 'countdown';
                if (isCountdown) {
                    if ($split.find('input').is(':checked') || $hero.find('input').is(':checked')) {
                        $('#lbx-layout-cards .lbx-radio-card:first-child').trigger('click').find('input').prop('checked', true);
                        selectedLayout = 'carousel';
                    }
                }
                $split.toggle(!isCountdown);
                $hero.toggle(!isCountdown);
                // Hide fields not used by countdown
                $('#lbx-layout-cards').closest('.lbx-field').toggle(!isCountdown);
                $('#lbx-transition').closest('.lbx-field').toggle(!isCountdown);
                $('#lbx-align-cards').closest('.lbx-field').toggle(!isCountdown);
                // "View Inventory" button and "Hide offer data" are OEM-only features
                // (Vue gates them by isMinerva). Hide both for Custom slides and Countdown.
                var isMinervaSrc = src === 'minerva';
                $('#lbx-oem-options-panel').toggle(isMinervaSrc);
                $('#lbx-show-inventory-wrap').toggle(isMinervaSrc);
                $('#lbx-hide-offer-data-wrap').toggle(isMinervaSrc);
                $('#lbx-boxed-offer-text-wrap').toggle(isMinervaSrc);
                // Disclaimer icon — only for dynamic (Minerva) sliders per LBT-1460. Custom/Countdown sliders don't get one.
                $('#lbx-disclaimer-panel').toggle(isMinervaSrc);
                // "Refresh data" only when the slider actually pulls feed data (OEM/static on).
                updateRefreshDataBtn();
                $('#lbx-video-playback-section').toggle(!isCountdown);
                $mcBp.toggle(!isCountdown && selectedLayout === 'media-columns');
                $ccBp.toggle(!isCountdown && selectedLayout === 'carousel');
                // Wrap panel: only show when one of the two breakpoint sub-sections is visible.
                $('#lbx-responsive-columns-panel').toggle(!isCountdown && (selectedLayout === 'media-columns' || selectedLayout === 'carousel'));
                // Intro Animation only applies to the OEM Offer Hero layout — hide it otherwise.
                $('#lbx-hero-intro-animation').closest('.lbx-field').toggle(selectedLayout === 'oem-offer-hero');
            }

            // ─── Per-slide settings (OEM) ─────────────────────────
            // slide_settings MUST be a JS object (not array). If PHP stored it as an empty array
            // (e.g. from older code that did `$data['slide_settings'] = []`), json_encode would
            // output `[]` which becomes a JS Array — and JSON.stringify of an Array with expando
            // properties returns "[]", silently dropping every per-slide override on submit.
            var slideSettings = <?php echo json_encode((object) ($editing_slider['slide_settings'] ?? [])); ?>;
            // Defensive: if for any reason it still arrives as an Array, coerce to a plain object.
            if (Array.isArray(slideSettings)) slideSettings = {};
            var minervaOrder = <?php echo json_encode(array_values((array)($editing_slider['minerva_order'] ?? []))); ?>;
            // Unified order — authoritative source for slide sequence in Minerva mode.
            // Each entry: { type: 'minerva', key: '<year_make_model>' } or { type: 'extra', id: 'ex_...' }.
            // When empty/absent, loadOemPreview() backfills it from legacy minervaOrder + extra positions.
            var unifiedSlides = <?php echo json_encode(array_values((array)($editing_slider['unified_slides'] ?? []))); ?>;
            var oemOfferData = {};
            var currentEditKey = '';

            // ─── Static Offers (Minerva image-only promos) ────────────────
            // staticOfferData: id → { id, title, image, image_mobile, link_url, ... } from the API.
            // staticSettings: id → { visible, link_url, link_target } per-slide overrides.
            var staticOfferData = {};
            var staticSettings = <?php echo json_encode((object) ($editing_slider['static_settings'] ?? [])); ?>;
            if (Array.isArray(staticSettings)) staticSettings = {};
            var staticOffersLoaded = false;  // AJAX initiated
            var staticOffersReady = false;   // AJAX resolved
            // LBT-1472 two independent auto-populate switches.
            var autoPopulateOem = <?php echo $ed_auto_oem ? 'true' : 'false'; ?>;
            var autoPopulateStatic = <?php echo $ed_auto_static ? 'true' : 'false'; ?>;
            // Make filter (multi-make dealers): '' = all makes. Lowercase for matching.
            var makeFilter = '<?php echo esc_js($ed_make_filter); ?>';

            // Generate a stable ID for an extra slide that doesn't have one yet.
            function genExtraId() {
                return 'ex_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
            }

            // Convenience: derive the Minerva key for a given offer object.
            function offerKey(o) {
                return [(o.year || ''), (o.make || ''), (o.model || '')]
                    .filter(Boolean).join('_').toLowerCase().replace(/\s+/g, '-');
            }

            // Build a unified order from legacy fields when unifiedSlides is empty.
            // Mirrors the historic frontend behavior: apply minervaOrder to OEM offers,
            // then splice extras at their `position` (1-based; lower position = earlier).
            function buildUnifiedFromLegacy(offers, order, extras) {
                var unified = [];
                var ordered = applyMinervaOrder(offers, order);
                ordered.forEach(function(o) {
                    unified.push({ type: 'minerva', key: offerKey(o) });
                });
                // Sort extras by position desc so earlier inserts don't shift later ones.
                var sortedExtras = (extras || []).slice().sort(function(a, b) {
                    return (b.position || 1) - (a.position || 1);
                });
                sortedExtras.forEach(function(ex) {
                    if (!ex.id) ex.id = genExtraId();
                    var pos = Math.min(Math.max((ex.position || 1) - 1, 0), unified.length);
                    unified.splice(pos, 0, { type: 'extra', id: ex.id });
                });
                return unified;
            }

            // Initialize drag-and-drop sortable on the unified preview grid.
            // Reads BOTH card types (OEM + Additional) and rebuilds unifiedSlides
            // from the resulting DOM order. minervaOrder is kept in sync for legacy
            // back-compat (the new save path also derives it from unifiedSlides).
            function initOemSortable() {
                var $grid = $('#lbx-oem-preview');
                if (!$grid.length) return;
                if (typeof $grid.sortable !== 'function') {
                    setTimeout(initOemSortable, 100); // Retry once sortable is loaded
                    return;
                }
                if ($grid.hasClass('ui-sortable')) {
                    try { $grid.sortable('destroy'); } catch (e) {}
                }
                $grid.sortable({
                    items: '> .lbx-oem-preview__card',
                    cursor: 'grabbing',
                    placeholder: 'lbx-sortable-placeholder',
                    tolerance: 'pointer',
                    forcePlaceholderSize: true,
                    opacity: 0.75,
                    distance: 5,
                    scroll: false,
                    update: function() {
                        var newUnified = [];
                        $grid.find('> .lbx-oem-preview__card').each(function() {
                            var $c = $(this);
                            var type = $c.data('type');
                            if (type === 'extra') {
                                var id = $c.data('extra-id');
                                if (id) newUnified.push({ type: 'extra', id: String(id) });
                            } else if (type === 'static') {
                                var sk = $c.data('static-key');
                                if (sk) newUnified.push({ type: 'static', key: String(sk) });
                            } else {
                                // Default to minerva for legacy cards rendered before data-type existed.
                                var key = $c.data('slide-key');
                                if (key) newUnified.push({ type: 'minerva', key: String(key) });
                            }
                        });
                        // Make filter active: cards it hides aren't in the DOM, so a naive
                        // rebuild would silently DROP those entries (losing their slot for
                        // good). Re-insert every non-rendered entry at ~its previous index
                        // so switching back to All Makes restores the full order.
                        if (makeFilter) {
                            var entryKey = function(u) { return u.type + ':' + (u.type === 'extra' ? u.id : u.key); };
                            var placed = {};
                            newUnified.forEach(function(u) { placed[entryKey(u)] = true; });
                            unifiedSlides.forEach(function(u, idx) {
                                if (!u || !u.type || placed[entryKey(u)]) return;
                                var at = Math.min(idx, newUnified.length);
                                newUnified.splice(at, 0, u);
                                placed[entryKey(u)] = true;
                            });
                        }
                        unifiedSlides = newUnified;
                        // Legacy minerva order follows the merged list, so hidden OEM
                        // entries keep their slot too.
                        minervaOrder = newUnified
                            .filter(function(u) { return u.type === 'minerva'; })
                            .map(function(u) { return String(u.key); });
                    }
                });
            }

            // Sort an offers array by stored order keys. Unknown keys go to the end in original order.
            function applyMinervaOrder(offers, order) {
                if (!Array.isArray(order) || !order.length) return offers;
                var keyOf = function(o) { return [o.year, o.make, o.model].filter(Boolean).join('_').toLowerCase().replace(/\s+/g, '-'); };
                var indexMap = {};
                order.forEach(function(k, i) { indexMap[k] = i; });
                var sorted = offers.slice().sort(function(a, b) {
                    var ka = keyOf(a), kb = keyOf(b);
                    var ia = (ka in indexMap) ? indexMap[ka] : 9999;
                    var ib = (kb in indexMap) ? indexMap[kb] : 9999;
                    return ia - ib;
                });
                return sorted;
            }

            // ─── Source toggle (radio cards) ──────────────────────────
            // Adaptive scroll-and-pulse: when the user picks a Type, the matching
            // slides section lives ABOVE the tabs. We scroll up to it (only if it's
            // not already visible enough) and pulse a primary halo so the change
            // doesn't feel silent.
            function pulseAndRevealSection($section) {
                if (!$section || !$section.length) return;
                var el = $section[0];
                var rect = el.getBoundingClientRect();
                var $tabsNav = $('.lbx-tabs-nav').first();
                var stickyTop = 32;
                if ($tabsNav.length) {
                    stickyTop = parseInt(getComputedStyle($tabsNav[0]).top, 10);
                    if (isNaN(stickyTop)) stickyTop = 32;
                    stickyTop += $tabsNav.outerHeight() + 16;
                }
                // "Visible enough" = at least 140px of the section is inside the
                // viewport, below the sticky tab bar. Otherwise scroll to it.
                var visiblePx = Math.min(rect.bottom, window.innerHeight) - Math.max(rect.top, stickyTop);
                if (visiblePx < 140) {
                    var targetY = window.pageYOffset + rect.top - stickyTop;
                    window.scrollTo({ top: Math.max(0, targetY), behavior: 'smooth' });
                }
                // Restart the pulse if it's already running (toggle class with reflow).
                $section.removeClass('lbx-section-pulse');
                void el.offsetWidth;
                $section.addClass('lbx-section-pulse');
                setTimeout(function() { $section.removeClass('lbx-section-pulse'); }, 2000);
            }

            $('.lbx-source-card').on('click', function() {
                $('.lbx-source-card').removeClass('active');
                $(this).addClass('active');
                var src = $(this).find('input').val();
                $('#lbx-custom-section').toggle(src === 'custom');
                $('#lbx-countdown-section').toggle(src === 'countdown');
                $('#lbx-oem-preview-section').toggle(src === 'minerva');
                if (src === 'minerva') { loadOemPreview(); }
                updateLayoutVisibility();
                // Wait a frame so the toggle()s above settle before measuring.
                var $target = src === 'minerva' ? $('#lbx-oem-preview-section')
                    : src === 'custom' ? $('#lbx-custom-section')
                    : src === 'countdown' ? $('#lbx-countdown-section')
                    : null;
                if ($target) requestAnimationFrame(function() { pulseAndRevealSection($target); });
            });
            $(document).on('change', 'input[name="slider_layout"]', updateLayoutVisibility);
            updateLayoutVisibility();

            // ─── Type picker (new sliders only) ───────────────────────
            // Type is asked once on creation via a modal, then fixed — the inline Type
            // section stays hidden. A choice just drives the existing source-card handler,
            // so all the section-toggling logic lives in one place.
            var lbxIsNewSlider = <?php echo ($editing_id === 'new') ? 'true' : 'false'; ?>;
            function openTypeModal() { $('#lbx-type-modal').addClass('is-open').attr('aria-hidden', 'false'); }
            function closeTypeModal() { $('#lbx-type-modal').removeClass('is-open').attr('aria-hidden', 'true'); }
            $(document).on('click', '.lbx-type-choice', function() {
                var t = $(this).data('type');
                $('input[name="slider_source"][value="' + t + '"]').prop('checked', true);
                // Reuse the source-card handler to toggle sections, load previews, etc.
                $('.lbx-source-card[data-source="' + t + '"]').trigger('click');
                closeTypeModal();
            });
            if (lbxIsNewSlider) { openTypeModal(); }

            // ─── Custom: Slide Builder (strip + single editor) ────────
            var customSlides = <?php echo json_encode($editing_custom_slides); ?>;
            var activeCustomSlide = -1; // -1 = no slide selected; user must click a card

            function esc(str) {
                return (str || '').replace(/&/g,'&amp;').replace(/"/g,'&quot;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
            }

            var CTA_POSITIONS = [
                { value: 'inline',         label: '<?php echo esc_js(__('Inline (with content)', 'leadbox')); ?>' },
                { value: 'top-left',       label: '<?php echo esc_js(__('Top Left', 'leadbox')); ?>' },
                { value: 'top-center',     label: '<?php echo esc_js(__('Top Center', 'leadbox')); ?>' },
                { value: 'top-right',      label: '<?php echo esc_js(__('Top Right', 'leadbox')); ?>' },
                { value: 'middle-left',    label: '<?php echo esc_js(__('Middle Left', 'leadbox')); ?>' },
                { value: 'middle-center',  label: '<?php echo esc_js(__('Middle Center', 'leadbox')); ?>' },
                { value: 'middle-right',   label: '<?php echo esc_js(__('Middle Right', 'leadbox')); ?>' },
                { value: 'bottom-left',    label: '<?php echo esc_js(__('Bottom Left', 'leadbox')); ?>' },
                { value: 'bottom-center',  label: '<?php echo esc_js(__('Bottom Center', 'leadbox')); ?>' },
                { value: 'bottom-right',   label: '<?php echo esc_js(__('Bottom Right', 'leadbox')); ?>' },
            ];

            function buildCtaPositionOptions(current) {
                return CTA_POSITIONS.map(function(p) {
                    return '<option value="' + p.value + '"' + (p.value === (current || 'inline') ? ' selected' : '') + '>' + p.label + '</option>';
                }).join('');
            }

            function buildCtasUI(ctas, prefix) {
                ctas = (Array.isArray(ctas) && ctas.length) ? ctas : [{ label: '', url: '', target: '_self', color: '', text_color: '', position: 'inline' }];
                var html = '<div class="lbx-ctas-builder" data-prefix="' + prefix + '">' +
                    '<div class="lbx-ctas-header">' +
                        '<h5><span class="dashicons dashicons-admin-links"></span> <?php _e('CTAs', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(optional)', 'leadbox'); ?></span></h5>' +
                        '<button type="button" class="button button-small lbx-cta-add-btn"><?php _e('+ Add CTA', 'leadbox'); ?></button>' +
                    '</div>' +
                    '<div class="lbx-ctas-list">';
                ctas.forEach(function(cta, i) {
                    html += buildCtaRow(cta, i, prefix);
                });
                html += '</div></div>';
                return html;
            }

            function buildCtaRow(cta, i, prefix) {
                var num = i + 1;
                var defColor = (i === 0) ? '#2563eb' : '#6b7280';
                return '<div class="lbx-cta-row" data-index="' + i + '">' +
                    '<div class="lbx-cta-row__num">' + num + '</div>' +
                    '<div class="lbx-cta-row__fields">' +
                        '<div class="lbx-cta-row__main">' +
                            '<input type="text" class="' + prefix + '-cta-label" value="' + esc((cta && cta.label) || '') + '" placeholder="<?php echo esc_attr__('Button label', 'leadbox'); ?>">' +
                            '<input type="url" class="' + prefix + '-cta-url" value="' + esc((cta && cta.url) || '') + '" placeholder="<?php echo esc_attr__('https://...', 'leadbox'); ?>">' +
                            '<select class="' + prefix + '-cta-target">' +
                                '<option value="_self"' + ((cta && cta.target) === '_blank' ? '' : ' selected') + '><?php echo esc_js(__('Same tab', 'leadbox')); ?></option>' +
                                '<option value="_blank"' + ((cta && cta.target) === '_blank' ? ' selected' : '') + '><?php echo esc_js(__('New tab', 'leadbox')); ?></option>' +
                            '</select>' +
                        '</div>' +
                        '<div class="lbx-cta-row__meta">' +
                            '<div class="lbx-cta-row__colors">' +
                                '<div class="lbx-cta-color" title="<?php echo esc_attr__('Button background color', 'leadbox'); ?>">' +
                                    '<span class="lbx-cta-color__tag lbx-cta-color__tag--bg"><?php echo esc_js(__('BG', 'leadbox')); ?></span>' +
                                    '<label class="lbx-cta-color__picker">' +
                                        '<input type="color" class="lbx-cta-color__swatch ' + prefix + '-cta-color-picker" value="' + ((cta && cta.color) || defColor) + '">' +
                                        '<span class="lbx-cta-color__fill" style="background:' + ((cta && cta.color) || defColor) + '"></span>' +
                                    '</label>' +
                                    '<input type="text" class="lbx-cta-color__hex ' + prefix + '-cta-color" value="' + ((cta && cta.color) || '') + '" placeholder="' + defColor + '">' +
                                '</div>' +
                                '<div class="lbx-cta-color" title="<?php echo esc_attr__('Button text color', 'leadbox'); ?>">' +
                                    '<span class="lbx-cta-color__tag lbx-cta-color__tag--text"><?php echo esc_js(__('TEXT', 'leadbox')); ?></span>' +
                                    '<label class="lbx-cta-color__picker">' +
                                        '<input type="color" class="lbx-cta-color__swatch ' + prefix + '-cta-text-color-picker" value="' + ((cta && cta.text_color) || '#000000') + '">' +
                                        '<span class="lbx-cta-color__fill" style="background:' + ((cta && cta.text_color) || '#000000') + '"></span>' +
                                    '</label>' +
                                    '<input type="text" class="lbx-cta-color__hex ' + prefix + '-cta-text-color" value="' + ((cta && cta.text_color) || '') + '" placeholder="#000000">' +
                                '</div>' +
                            '</div>' +
                            '<div class="lbx-cta-row__posrow">' +
                                '<div class="lbx-cta-row__pos">' +
                                    '<label><?php echo esc_js(__('Position', 'leadbox')); ?></label>' +
                                    '<select class="' + prefix + '-cta-position">' +
                                        buildCtaPositionOptions((cta && cta.position) || 'inline') +
                                    '</select>' +
                                '</div>' +
                                '<button type="button" class="lbx-cta-remove-btn" title="<?php echo esc_attr__('Remove CTA', 'leadbox'); ?>">&times;</button>' +
                            '</div>' +
                        '</div>' +
                    '</div>' +
                '</div>';
            }

            function buildScheduleUI(startDate, endDate, prefix) {
                return '<div class="lbx-schedule-section" style="margin-top:12px;">' +
                    '<h5><span class="dashicons dashicons-calendar-alt"></span> <?php echo esc_js(__('Scheduling', 'leadbox')); ?> <span class="lbx-label-hint"><?php echo esc_js(__('(optional)', 'leadbox')); ?></span></h5>' +
                    '<div class="lbx-schedule-row">' +
                        '<div class="lbx-schedule-field">' +
                            '<label><?php echo esc_js(__('Show From', 'leadbox')); ?></label>' +
                            '<input type="datetime-local" class="' + prefix + '-start-date" value="' + esc(startDate || '') + '">' +
                        '</div>' +
                        '<div class="lbx-schedule-field">' +
                            '<label><?php echo esc_js(__('Hide After', 'leadbox')); ?></label>' +
                            '<input type="datetime-local" class="' + prefix + '-end-date" value="' + esc(endDate || '') + '">' +
                        '</div>' +
                    '</div>' +
                    '<div class="lbx-schedule-hint">' +
                        '<span class="dashicons dashicons-info"></span> ' +
                        '<?php echo esc_js(__('Leave empty for always visible. Filtered server-side.', 'leadbox')); ?>' +
                    '</div>' +
                '</div>';
            }

            function readCtasFromDom($container, prefix) {
                var result = [];
                $container.find('.lbx-cta-row').each(function() {
                    var $row = $(this);
                    var label = $row.find('.' + prefix + '-cta-label').val() || '';
                    var url   = $row.find('.' + prefix + '-cta-url').val() || '';
                    if (!label && !url) return; // skip empty rows
                    result.push({
                        label:      label,
                        url:        url,
                        target:     $row.find('.' + prefix + '-cta-target').val() || '_self',
                        color:      $row.find('.' + prefix + '-cta-color').val() || '',
                        text_color: $row.find('.' + prefix + '-cta-text-color').val() || '',
                        position:   $row.find('.' + prefix + '-cta-position').val() || 'inline',
                    });
                });
                return result;
            }

            function normalizeCta(cta, position) {
                return {
                    label: (cta && cta.label) || '',
                    url: (cta && cta.url) || '',
                    target: (cta && cta.target) || '_self',
                    color: (cta && cta.color) || '',
                    text_color: (cta && cta.text_color) || '',
                    position: (cta && cta.position) || position || 'inline'
                };
            }

            function normalizeCtasArray(slide) {
                // Already has ctas array — normalize each entry
                if (Array.isArray(slide.ctas) && slide.ctas.length > 0) {
                    slide.ctas = slide.ctas.map(function(c) { return normalizeCta(c, 'inline'); });
                    return slide;
                }
                // Migrate from legacy main_cta / secondary_cta
                slide.ctas = [];
                if (slide.main_cta && slide.main_cta.label) slide.ctas.push(normalizeCta(slide.main_cta, 'inline'));
                if (slide.secondary_cta && slide.secondary_cta.label) slide.ctas.push(normalizeCta(slide.secondary_cta, 'inline'));
                return slide;
            }

            function normalizeCustomSlide(slide) {
                slide = slide || {};
                slide = normalizeCtasArray(slide);
                slide.media_type = (slide.media_type === 'video' || slide.media_type === 'embed') ? slide.media_type : 'image';
                slide.video_url = slide.video_url || '';
                slide.align = slide.align || '';
                slide.image_size = slide.image_size || 'full';
                slide.image_fit = slide.image_fit || 'cover';
                slide.image_link_url = slide.image_link_url || '';
                slide.image_link_target = slide.image_link_target || '_self';
                slide.hide_watermark = !!slide.hide_watermark;
                slide.title = slide.title || '';
                slide.title_color = slide.title_color || '';
                slide.desc_color = slide.desc_color || '';
                slide.description = slide.description || '';
                slide.video_id = slide.video_id || 0;
                slide.start_date = slide.start_date || '';
                slide.end_date = slide.end_date || '';
                slide.embed_code = slide.embed_code || '';
                slide.image_id_mobile = slide.image_id_mobile || 0;
                slide.image_url_mobile = slide.image_url_mobile || '';
                return slide;
            }

            function renderCustomSlides() {
                var $stripWrap = $('#lbx-custom-slides').empty();
                var $editorWrap = $('#lbx-custom-editor').empty();
                if (customSlides.length === 0) activeCustomSlide = -1;
                // Don't auto-select; user must click a card to edit.
                if (activeCustomSlide >= customSlides.length) activeCustomSlide = -1;

                // Zero-slides: show a rich empty state instead of the tiny "+ Add Slide" card.
                if (customSlides.length === 0) {
                    $('#lbx-custom-sortable-hint').hide(); // No point suggesting drag-reorder when there's nothing to drag.
                    $stripWrap.html(
                        '<button type="button" class="lbx-cs-empty lbx-cs-card-add">' +
                            '<div class="lbx-cs-empty__icon"><span class="dashicons dashicons-format-gallery"></span></div>' +
                            '<h4 class="lbx-cs-empty__title"><?php echo esc_js(__('No slides yet', 'leadbox')); ?></h4>' +
                            '<p class="lbx-cs-empty__desc"><?php echo esc_js(__('Create your first slide to get started. Add an image or video, a title, description and call-to-action buttons.', 'leadbox')); ?></p>' +
                            '<span class="lbx-cs-empty__btn"><span class="dashicons dashicons-plus-alt2"></span> <?php echo esc_js(__('Add your first slide', 'leadbox')); ?></span>' +
                        '</button>'
                    );
                    $editorWrap.empty();
                    closeEditModal($('#lbx-cs-modal'));
                    $('#lbx-custom-count').text('0');
                    initCustomSortable();
                    return;
                }
                $('#lbx-custom-sortable-hint').show();

                var stripHtml = '<div class="lbx-cs-strip">';

                customSlides.forEach(function(rawSlide, i) {
                    var slide = normalizeCustomSlide(rawSlide);
                    customSlides[i] = slide;
                    var cardTitle = slide.title || '<?php echo esc_js(__('Untitled Slide', 'leadbox')); ?>';
                    var isActive = i === activeCustomSlide ? ' lbx-cs-card--active' : '';
                    // Priority: media_type decides what renders. Fall back to icons if media missing.
                    var thumb;
                    if (slide.media_type === 'embed') {
                        thumb = '<div class="lbx-cs-card-embed-ph"><span class="dashicons dashicons-editor-code"></span></div>';
                    } else if (slide.media_type === 'video') {
                        thumb = slide.video_url
                            ? '<video src="' + esc(slide.video_url) + '#t=0.1" preload="metadata" muted playsinline></video>'
                            : '<span class="dashicons dashicons-format-video"></span>';
                    } else {
                        thumb = slide.image_url
                            ? '<img src="' + esc(slide.image_url) + '" alt="">'
                            : '<span class="dashicons dashicons-format-image"></span>';
                    }
                    var mediaBadgeIcon = slide.media_type === 'embed' ? 'editor-code' : (slide.media_type === 'video' ? 'format-video' : 'format-image');
                    var mediaBadge = '<span class="lbx-cs-card-badge"><span class="dashicons dashicons-' + mediaBadgeIcon + '"></span></span>';
                    stripHtml += '' +
                        '<div class="lbx-cs-card' + isActive + '" data-index="' + i + '">' +
                            '<span class="lbx-cs-card-type-badge"><?php echo esc_js(__('Custom', 'leadbox')); ?></span>' +
                            '<div class="lbx-cs-card-thumb">' + thumb + mediaBadge + '</div>' +
                            '<div class="lbx-cs-card-meta">' +
                                '<span class="lbx-cs-card-num">' + (i + 1) + '</span>' +
                                '<span class="lbx-cs-card-title">' + esc(cardTitle) + '</span>' +
                            '</div>' +
                        '</div>';
                });

                stripHtml += '' +
                    '<button type="button" class="lbx-cs-card-add">' +
                        '<span class="dashicons dashicons-plus-alt2"></span>' +
                        '<span><?php echo esc_js(__('Add Slide', 'leadbox')); ?></span>' +
                    '</button>' +
                '</div>';
                $stripWrap.html(stripHtml);

                // No slide selected — close the editor modal (safe no-op if already closed).
                if (activeCustomSlide < 0) {
                    $editorWrap.empty();
                    closeEditModal($('#lbx-cs-modal'));
                    $('#lbx-custom-count').text(customSlides.length);
                    initCustomSortable();
                    return;
                }

                var s = customSlides[activeCustomSlide];
                var mediaType = (s.media_type === 'video' || s.media_type === 'embed') ? s.media_type : 'image';
                var videoWrapStyle = mediaType === 'video' ? '' : 'display:none;';
                var embedWrapStyle = mediaType === 'embed' ? '' : 'display:none;';
                // The image block (preview / mobile / size / fit) makes no sense for an
                // HTML embed — hide the whole block in that case so the user doesn't see
                // a clickable image placeholder when they're configuring iframe code.
                var imageBlockStyle = mediaType === 'embed' ? 'display:none;' : '';
                var imgPreview;
                if (mediaType === 'embed') {
                    imgPreview = '<div class="lbx-slide-image-ph"><span class="dashicons dashicons-editor-code"></span><span><?php echo esc_js(__('HTML Embed', 'leadbox')); ?></span></div>';
                } else if (mediaType === 'video') {
                    imgPreview = s.video_url
                        ? '<div class="lbx-video-preview"><video src="' + esc(s.video_url) + '#t=0.1" preload="metadata" muted playsinline></video><div class="lbx-video-preview-loader"><span class="spinner is-active"></span></div></div>'
                        : '<div class="lbx-slide-image-ph"><span class="dashicons dashicons-format-video"></span><span><?php echo esc_js(__('Video slide', 'leadbox')); ?></span></div>';
                } else {
                    imgPreview = s.image_url
                        ? '<img src="' + esc(s.image_url) + '" alt="">'
                        : '<div class="lbx-slide-image-ph"><span class="dashicons dashicons-cloud-upload"></span><span><?php echo esc_js(__('Click to upload', 'leadbox')); ?></span></div>';
                }
                var boldActive = s.title_bold ? ' active' : '';
                var italicActive = s.title_italic ? ' active' : '';

                $editorWrap.html(
                    '<div class="lbx-cs-editor-head">' +
                        '<h4 class="lbx-cs-editor-title"><?php echo esc_js(__('Editing Slide', 'leadbox')); ?> #' + (activeCustomSlide + 1) + '</h4>' +
                        '<div class="lbx-cs-editor-actions">' +
                            (activeCustomSlide > 0 ? '<button type="button" class="lbx-cs-move" data-dir="up" title="<?php echo esc_attr__('Move up', 'leadbox'); ?>"><span class="dashicons dashicons-arrow-up-alt2"></span></button>' : '') +
                            (activeCustomSlide < customSlides.length - 1 ? '<button type="button" class="lbx-cs-move" data-dir="down" title="<?php echo esc_attr__('Move down', 'leadbox'); ?>"><span class="dashicons dashicons-arrow-down-alt2"></span></button>' : '') +
                            '<button type="button" class="lbx-cs-remove" title="<?php echo esc_attr__('Remove', 'leadbox'); ?>"><span class="dashicons dashicons-trash"></span></button>' +
                        '</div>' +
                    '</div>' +
                    '<div class="lbx-cs-editor-body">' +
                        '<div class="lbx-slide-top-row">' +
                            '<div>' +
                                // Media-type selector — top of the column so the user picks the
                                // kind of background first; the type-specific input (video URL,
                                // embed code) and the image block adapt to whatever this is set to.
                                '<div style="margin-bottom:10px;">' +
                                    '<label style="display:block;font-size:11px;font-weight:600;color:var(--lbx-gray-500);margin-bottom:4px;text-transform:uppercase;"><?php echo esc_js(__('Background Media', 'leadbox')); ?> <span style="font-weight:400;color:var(--lbx-gray-400);text-transform:none;"><?php echo esc_js(__('(recommended — pick image or video)', 'leadbox')); ?></span></label>' +
                                    '<select class="lbx-cs-media-type" style="width:100%;padding:6px 8px;border:1px solid var(--lbx-gray-300);border-radius:var(--lbx-radius-sm);font-size:12px;">' +
                                        '<option value="image"' + (mediaType === 'image' ? ' selected' : '') + '><?php echo esc_js(__('Image', 'leadbox')); ?></option>' +
                                        '<option value="video"' + (mediaType === 'video' ? ' selected' : '') + '><?php echo esc_js(__('Video (MP4/WebM)', 'leadbox')); ?></option>' +
                                        '<option value="embed"' + (mediaType === 'embed' ? ' selected' : '') + '><?php echo esc_js(__('HTML Embed (iframe, etc.)', 'leadbox')); ?></option>' +
                                    '</select>' +
                                '</div>' +
                                // Type-specific input directly under the selector. Each wrap is
                                // hidden unless its corresponding media type is active.
                                '<div class="lbx-cs-video-wrap" style="margin-bottom:10px;' + videoWrapStyle + '">' +
                                    '<input type="url" class="lbx-cs-video-url" value="' + esc(s.video_url) + '" placeholder="<?php echo esc_attr__('https://.../clip.mp4', 'leadbox'); ?>" style="width:100%;padding:6px 8px;border:1px solid var(--lbx-gray-300);border-radius:var(--lbx-radius-sm);font-size:12px;">' +
                                    '<div style="display:flex;gap:6px;margin-top:6px;">' +
                                        '<button type="button" class="button button-small lbx-pick-video" data-index="' + activeCustomSlide + '"><?php echo esc_js(__('Choose Video', 'leadbox')); ?></button>' +
                                        '<button type="button" class="button button-small lbx-clear-video"><?php echo esc_js(__('Clear', 'leadbox')); ?></button>' +
                                    '</div>' +
                                '</div>' +
                                '<div class="lbx-cs-embed-wrap" style="margin-bottom:10px;' + embedWrapStyle + '">' +
                                    '<textarea class="lbx-cs-embed-code" placeholder="<?php echo esc_attr__('Paste your iframe / embed HTML here…', 'leadbox'); ?>" rows="5" spellcheck="false" style="width:100%;padding:8px 10px;border:1px solid var(--lbx-gray-300);border-radius:var(--lbx-radius-sm);font-family:SFMono-Regular,Consolas,monospace;font-size:11px;line-height:1.5;background:#f9fafb;">' + esc(s.embed_code || '') + '</textarea>' +
                                    '<p style="margin:4px 0 0;font-size:10px;color:var(--lbx-gray-500);"><span class="dashicons dashicons-info" style="font-size:11px;width:11px;height:11px;vertical-align:text-bottom;"></span> <?php echo esc_js(__('Paste full HTML (iframe, video tag, etc.). Will replace background image/video.', 'leadbox')); ?></p>' +
                                '</div>' +
                                // Image block — preview, mobile picker, size, fit. The same picker
                                // doubles as the poster source when type=video. Hidden whole-block
                                // when type=embed (no image needed for an iframe).
                                '<div class="lbx-cs-image-block" style="' + imageBlockStyle + '">' +
                                    // Only add the picker modifier when the user is actually
                                    // configuring an image — for type=video the box just shows
                                    // the video poster preview and shouldn't open the media library.
                                    '<div class="lbx-slide-image' + (mediaType === 'image' ? ' lbx-pick-image' : '') + '" data-index="' + activeCustomSlide + '">' + imgPreview + '</div>' +
                                    // Mobile image override — optional. Shown as a compact secondary picker.
                                    '<div class="lbx-slide-mobile-image-row" style="margin-top:6px;">' +
                                        '<label style="display:block;font-size:10px;font-weight:600;color:var(--lbx-gray-500);text-transform:uppercase;letter-spacing:0.03em;margin-bottom:4px;"><?php echo esc_js(__('Mobile image', 'leadbox')); ?> <span style="font-weight:400;color:var(--lbx-gray-400);text-transform:none;letter-spacing:0;"><?php echo esc_js(__('(optional)', 'leadbox')); ?></span></label>' +
                                        '<div style="display:flex;gap:6px;align-items:center;">' +
                                            (s.image_url_mobile
                                                ? '<img class="lbx-cs-mobile-thumb" src="' + esc(s.image_url_mobile) + '" alt="" style="width:42px;height:42px;object-fit:cover;border:1px solid var(--lbx-gray-300);border-radius:var(--lbx-radius-sm);flex-shrink:0;">'
                                                : '<div class="lbx-cs-mobile-thumb lbx-cs-mobile-thumb--empty" style="width:42px;height:42px;background:var(--lbx-gray-100);border:1px dashed var(--lbx-gray-300);border-radius:var(--lbx-radius-sm);display:flex;align-items:center;justify-content:center;color:var(--lbx-gray-400);flex-shrink:0;"><span class="dashicons dashicons-smartphone" style="font-size:16px;width:16px;height:16px;"></span></div>'
                                            ) +
                                            '<button type="button" class="button button-small lbx-cs-pick-mobile-image" data-index="' + activeCustomSlide + '" style="flex:1;"><?php echo esc_js(__('Choose…', 'leadbox')); ?></button>' +
                                            (s.image_url_mobile ? '<button type="button" class="button button-small lbx-cs-clear-mobile-image" title="<?php echo esc_attr__('Remove mobile image', 'leadbox'); ?>">&times;</button>' : '') +
                                        '</div>' +
                                        '<p style="margin:4px 0 0;font-size:10px;color:var(--lbx-gray-400);line-height:1.3;"><?php echo esc_js(__('Used on mobile (≤640px). Falls back to the main image if empty.', 'leadbox')); ?></p>' +
                                    '</div>' +
                                    '<select class="lbx-cs-image-size" style="width:100%;padding:5px 8px;margin-top:6px;border:1px solid var(--lbx-gray-300);border-radius:var(--lbx-radius-sm);font-size:11px;color:var(--lbx-gray-600);">' +
                                        '<option value="full"' + (s.image_size === 'full' ? ' selected' : '') + '><?php echo esc_js(__('Full Size', 'leadbox')); ?></option>' +
                                        '<option value="large"' + (s.image_size === 'large' ? ' selected' : '') + '><?php echo esc_js(__('Large (1024px)', 'leadbox')); ?></option>' +
                                        '<option value="medium_large"' + (s.image_size === 'medium_large' ? ' selected' : '') + '><?php echo esc_js(__('Medium (768px)', 'leadbox')); ?></option>' +
                                    '</select>' +
                                    '<select class="lbx-cs-image-fit" style="width:100%;padding:5px 8px;margin-top:6px;border:1px solid var(--lbx-gray-300);border-radius:var(--lbx-radius-sm);font-size:11px;color:var(--lbx-gray-600);">' +
                                        '<option value="cover"' + (s.image_fit === 'cover' ? ' selected' : '') + '><?php echo esc_js(__('Cover — Fill (may crop)', 'leadbox')); ?></option>' +
                                        '<option value="contain"' + (s.image_fit === 'contain' ? ' selected' : '') + '><?php echo esc_js(__('Contain — Fit entire image', 'leadbox')); ?></option>' +
                                        '<option value="fill"' + (s.image_fit === 'fill' ? ' selected' : '') + '><?php echo esc_js(__('Fill — Stretch to fill', 'leadbox')); ?></option>' +
                                        '<option value="scale-down"' + (s.image_fit === 'scale-down' ? ' selected' : '') + '><?php echo esc_js(__('Scale Down — Fit without enlarging', 'leadbox')); ?></option>' +
                                    '</select>' +
                                '</div>' +
                            '</div>' +
                            '<div class="lbx-slide-fields">' +
                                '<div class="lbx-slide-field">' +
                                    '<label><?php _e('Content Alignment', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(optional)', 'leadbox'); ?></span></label>' +
                                    '<select class="lbx-cs-align">' +
                                        '<option value=""' + (s.align === '' ? ' selected' : '') + '><?php echo esc_js(__('Default', 'leadbox')); ?></option>' +
                                        '<option value="left"' + (s.align === 'left' ? ' selected' : '') + '><?php echo esc_js(__('Left', 'leadbox')); ?></option>' +
                                        '<option value="center"' + (s.align === 'center' ? ' selected' : '') + '><?php echo esc_js(__('Center', 'leadbox')); ?></option>' +
                                        '<option value="right"' + (s.align === 'right' ? ' selected' : '') + '><?php echo esc_js(__('Right', 'leadbox')); ?></option>' +
                                    '</select>' +
                                '</div>' +
                                '<div class="lbx-slide-field">' +
                                    '<label><?php _e('Title', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(optional, recommended)', 'leadbox'); ?></span></label>' +
                                    '<input type="text" class="lbx-cs-title" value="' + esc(s.title) + '" placeholder="<?php echo esc_attr__('e.g. 2025 Bronco Sport', 'leadbox'); ?>">' +
                                    '<div class="lbx-field-toolbar">' +
                                        '<div class="lbx-format-toolbar">' +
                                            '<label class="lbx-format-btn' + boldActive + '" title="<?php echo esc_attr__('Bold', 'leadbox'); ?>"><input type="checkbox" class="lbx-cs-title-bold"' + (s.title_bold ? ' checked' : '') + '><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round"><path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/><path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/></svg></label>' +
                                            '<label class="lbx-format-btn' + italicActive + '" title="<?php echo esc_attr__('Italic', 'leadbox'); ?>"><input type="checkbox" class="lbx-cs-title-italic"' + (s.title_italic ? ' checked' : '') + '><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round"><line x1="19" y1="4" x2="10" y2="4"/><line x1="14" y1="20" x2="5" y2="20"/><line x1="15" y1="4" x2="9" y2="20"/></svg></label>' +
                                        '</div>' +
                                        '<label class="lbx-color-chip">' +
                                            '<span class="lbx-color-chip__preview"><input type="color" class="lbx-cc-swatch lbx-cs-title-color" value="' + (s.title_color || '#ffffff') + '"><span class="lbx-cc-fill" style="background:' + (s.title_color || '#ffffff') + '"></span></span>' +
                                            '<span class="lbx-color-chip__body"><input type="text" class="lbx-color-chip__hex lbx-cs-title-color-hex" value="' + (s.title_color || '') + '" placeholder="#ffffff"><span class="lbx-color-chip__label"><?php echo esc_js(__('Title', 'leadbox')); ?></span></span>' +
                                        '</label>' +
                                    '</div>' +
                                '</div>' +
                                '<div class="lbx-slide-field">' +
                                    '<label><?php _e('Description', 'leadbox'); ?> <span class="lbx-label-hint"><?php _e('(optional)', 'leadbox'); ?> · <?php _e('HTML allowed', 'leadbox'); ?></span></label>' +
                                    '<textarea class="lbx-cs-desc" rows="3" placeholder="<?php echo esc_attr__('e.g. GET <strong>$5,500</strong> in rebates', 'leadbox'); ?>">' + esc(s.description) + '</textarea>' +
                                    '<div class="lbx-field-toolbar" style="margin-top:6px;">' +
                                        '<label class="lbx-color-chip">' +
                                            '<span class="lbx-color-chip__preview"><input type="color" class="lbx-cc-swatch lbx-cs-desc-color" value="' + (s.desc_color || '#ffffff') + '"><span class="lbx-cc-fill" style="background:' + (s.desc_color || '#ffffff') + '"></span></span>' +
                                            '<span class="lbx-color-chip__body"><input type="text" class="lbx-color-chip__hex lbx-cs-desc-color-hex" value="' + (s.desc_color || '') + '" placeholder="#ffffff"><span class="lbx-color-chip__label"><?php echo esc_js(__('Text Color', 'leadbox')); ?></span></span>' +
                                        '</label>' +
                                    '</div>' +
                                '</div>' +
                            '</div>' +
                        '</div>' +
                        buildCtasUI(s.ctas, 'lbx-cs') +
                        '<div class="lbx-cta-group">' +
                            '<h5><span class="dashicons dashicons-admin-links"></span> <?php _e('Image Link', 'leadbox'); ?> <span class="lbx-label-hint" style="font-size:10px;"><?php _e('(optional)', 'leadbox'); ?></span></h5>' +
                            '<div class="lbx-cta-fields">' +
                                '<input type="url" class="lbx-cs-image-link-url" value="' + esc(s.image_link_url || '') + '" placeholder="<?php echo esc_attr__('https://...', 'leadbox'); ?>">' +
                                '<select class="lbx-cs-image-link-target" style="padding:6px 10px;border:1px solid var(--lbx-gray-300);border-radius:var(--lbx-radius-sm);font-size:12px;">' +
                                    '<option value="_self"' + ((s.image_link_target || '_self') === '_self' ? ' selected' : '') + '><?php echo esc_js(__('Same tab', 'leadbox')); ?></option>' +
                                    '<option value="_blank"' + ((s.image_link_target || '_self') === '_blank' ? ' selected' : '') + '><?php echo esc_js(__('New tab', 'leadbox')); ?></option>' +
                                '</select>' +
                            '</div>' +
                        '</div>' +
                        '<label style="display:flex;align-items:center;gap:8px;margin-top:10px;cursor:pointer;">' +
                            '<input type="checkbox" class="lbx-cs-hide-watermark"' + (s.hide_watermark ? ' checked' : '') + '>' +
                            '<span style="font-size:12px;color:var(--lbx-gray-600);"><?php echo esc_js(__('Hide watermark on this slide', 'leadbox')); ?></span>' +
                        '</label>' +
                        buildScheduleUI(s.start_date, s.end_date, 'lbx-cs') +
                    '</div>'
                );
                $('#lbx-custom-count').text(customSlides.length);
                // Bind load/error on any video previews (events don't bubble)
                bindVideoPreviewEvents($editorWrap);
                initCustomSortable();
            }

            // Initialize drag-and-drop sortable on the custom slides strip.
            function initCustomSortable() {
                var $strip = $('#lbx-custom-slides .lbx-cs-strip');
                if (!$strip.length) return;
                if (typeof $strip.sortable !== 'function') {
                    setTimeout(initCustomSortable, 100); // Retry once sortable is loaded
                    return;
                }
                if ($strip.hasClass('ui-sortable')) {
                    try { $strip.sortable('destroy'); } catch (e) {}
                }
                $strip.sortable({
                    items: '> .lbx-cs-card',
                    cursor: 'grabbing',
                    placeholder: 'lbx-sortable-placeholder',
                    tolerance: 'pointer',
                    forcePlaceholderSize: true,
                    opacity: 0.75,
                    distance: 5,
                    scroll: false,
                    start: function(e, ui) {
                        syncSlideData(activeCustomSlide);
                        ui.item.data('startIndex', ui.item.index());
                    },
                    update: function(e, ui) {
                        var from = ui.item.data('startIndex');
                        var to = ui.item.index();
                        if (from === to || from === undefined) return;

                        // Reorder the data array
                        var moved = customSlides.splice(from, 1)[0];
                        customSlides.splice(to, 0, moved);

                        // Track the active slide index
                        if (activeCustomSlide === from) {
                            activeCustomSlide = to;
                        } else if (from < activeCustomSlide && to >= activeCustomSlide) {
                            activeCustomSlide -= 1;
                        } else if (from > activeCustomSlide && to <= activeCustomSlide) {
                            activeCustomSlide += 1;
                        }

                        // Full re-render (updates strip numbers + editor panel title/move buttons).
                        // Deferred to next frame so the drop animation feels snappy.
                        requestAnimationFrame(function() { renderCustomSlides(); });
                    }
                });
            }

            // Generic helpers shared by the Custom + Additional slide edit modals.
            function openEditModal($m) {
                if (!$m || !$m.length) return;
                $m.addClass('is-open').attr('aria-hidden', 'false');
                $('body').addClass('lbx-edit-modal-open');
                // Always reset scroll so reopening another slide doesn't inherit the
                // previous one's scroll position. Wait one frame for layout to settle.
                var $scroller = $m.find('.lbx-edit-modal');
                requestAnimationFrame(function() { $scroller.scrollTop(0); });
            }
            function closeEditModal($m) {
                if (!$m || !$m.length) return;
                $m.removeClass('is-open').attr('aria-hidden', 'true');
                // Only release the body lock if no edit modal remains open.
                if (!$('.lbx-edit-modal-backdrop.is-open').length) {
                    $('body').removeClass('lbx-edit-modal-open');
                }
            }
            // Retained name for backwards-compat with existing call sites — now just
            // opens the modal that hosts #lbx-custom-editor.
            function scrollToCustomEditor() {
                openEditModal($('#lbx-cs-modal'));
            }
            // Close flow: persist whatever the user typed, drop the active index so the
            // card strip loses its --active highlight, hide the modal, refresh the strip.
            function closeCustomSlideModal() {
                if (activeCustomSlide >= 0) syncSlideData(activeCustomSlide);
                activeCustomSlide = -1;
                closeEditModal($('#lbx-cs-modal'));
                renderCustomSlides();
            }
            $(document).on('click', '#lbx-cs-modal-close', closeCustomSlideModal);
            $(document).on('click', '#lbx-cs-modal', function(e) { if (e.target === this) closeCustomSlideModal(); });

            function syncSlideData(index) {
                if (!customSlides.length) return;
                var idx = (typeof index === 'number') ? index : activeCustomSlide;
                if (idx < 0 || idx >= customSlides.length) return;
                var $s = $('#lbx-custom-editor');
                if (!$s.length) return;
                customSlides[idx] = normalizeCustomSlide(customSlides[idx]);
                customSlides[idx].title = $s.find('.lbx-cs-title').val() || '';
                customSlides[idx].title_color = $s.find('.lbx-cs-title-color-hex').val() || '';
                customSlides[idx].title_bold = $s.find('.lbx-cs-title-bold').is(':checked');
                customSlides[idx].title_italic = $s.find('.lbx-cs-title-italic').is(':checked');
                customSlides[idx].desc_color = $s.find('.lbx-cs-desc-color-hex').val() || '';
                customSlides[idx].description = $s.find('.lbx-cs-desc').val() || '';
                customSlides[idx].media_type = $s.find('.lbx-cs-media-type').val() || 'image';
                customSlides[idx].align = $s.find('.lbx-cs-align').val() || '';
                customSlides[idx].video_url = $s.find('.lbx-cs-video-url').val() || '';
                customSlides[idx].video_id = customSlides[idx].video_url ? (customSlides[idx].video_id || 0) : 0;
                customSlides[idx].ctas = readCtasFromDom($s, 'lbx-cs');
                customSlides[idx].image_size = $s.find('.lbx-cs-image-size').val() || 'full';
                customSlides[idx].image_fit = $s.find('.lbx-cs-image-fit').val() || 'cover';
                customSlides[idx].image_link_url = $s.find('.lbx-cs-image-link-url').val() || '';
                customSlides[idx].image_link_target = $s.find('.lbx-cs-image-link-target').val() || '_self';
                customSlides[idx].hide_watermark = $s.find('.lbx-cs-hide-watermark').is(':checked');
                customSlides[idx].start_date = $s.find('.lbx-cs-start-date').val() || '';
                customSlides[idx].end_date = $s.find('.lbx-cs-end-date').val() || '';
                customSlides[idx].embed_code = $s.find('.lbx-cs-embed-code').val() || '';
            }

            // Select slide from strip (scoped to custom slides)
            $(document).on('click', '#lbx-custom-slides .lbx-cs-card', function() {
                var idx = parseInt($(this).data('index'), 10) || 0;
                if (idx !== activeCustomSlide) {
                    syncSlideData(activeCustomSlide);
                    activeCustomSlide = idx;
                    renderCustomSlides();
                }
                scrollToCustomEditor();
            });

            // Add slide card
            $(document).on('click', '.lbx-cs-card-add', function() {
                syncSlideData(activeCustomSlide);
                customSlides.push({
                    image_id: 0, image_url: '', title: '', title_color: '', title_bold: false, title_italic: false, desc_color: '', description: '',
                    media_type: 'image', align: '', video_id: 0, video_url: '',
                    ctas: [{ label: '', url: '', target: '_self', color: '', position: 'inline' }],
                    image_size: 'full', image_link_url: '', image_link_target: '_self'
                });
                activeCustomSlide = customSlides.length - 1;
                renderCustomSlides();
                scrollToCustomEditor();
            });

            // Remove active slide
            $(document).on('click', '.lbx-cs-remove', function() {
                lbxConfirm({
                    title: '<?php echo esc_js(__('Remove this slide?', 'leadbox')); ?>',
                    message: '<?php echo esc_js(__('This will delete the slide and everything inside it. You can still restore it by not saving the slider.', 'leadbox')); ?>',
                    confirmText: '<?php echo esc_js(__('Remove slide', 'leadbox')); ?>',
                    cancelText: '<?php echo esc_js(__('Keep', 'leadbox')); ?>',
                    danger: true
                }).then(function(ok) {
                    if (!ok) return;
                    syncSlideData(activeCustomSlide);
                    customSlides.splice(activeCustomSlide, 1);
                    activeCustomSlide = -1; // Collapse editor; user must pick the next slide.
                    renderCustomSlides();
                });
            });

            // Move active slide
            $(document).on('click', '.lbx-cs-move', function() {
                var dir = $(this).data('dir');
                syncSlideData(activeCustomSlide);
                var target = dir === 'up' ? activeCustomSlide - 1 : activeCustomSlide + 1;
                if (target < 0 || target >= customSlides.length) return;
                var temp = customSlides[activeCustomSlide];
                customSlides[activeCustomSlide] = customSlides[target];
                customSlides[target] = temp;
                activeCustomSlide = target;
                renderCustomSlides();
            });

            // Pick image
            $(document).on('click', '.lbx-pick-image', function() {
                var idx = parseInt($(this).data('index'), 10) || 0;
                var frame = wp.media({
                    title: '<?php echo esc_js(__('Select Slide Image', 'leadbox')); ?>',
                    multiple: false,
                    library: { type: 'image' }
                });
                frame.on('select', function() {
                    var attachment = frame.state().get('selection').first().toJSON();
                    syncSlideData(activeCustomSlide);
                    customSlides[idx] = normalizeCustomSlide(customSlides[idx]);
                    customSlides[idx].image_id = attachment.id;
                    customSlides[idx].image_url = attachment.url;
                    activeCustomSlide = idx;
                    renderCustomSlides();
                });
                frame.open();
            });

            // Pick mobile image (custom slide)
            $(document).on('click', '.lbx-cs-pick-mobile-image', function(e) {
                e.preventDefault();
                var idx = parseInt($(this).data('index'), 10) || 0;
                var frame = wp.media({ title: '<?php echo esc_js(__('Select Mobile Image', 'leadbox')); ?>', multiple: false, library: { type: 'image' } });
                frame.on('select', function() {
                    var att = frame.state().get('selection').first().toJSON();
                    syncSlideData(activeCustomSlide);
                    customSlides[idx] = normalizeCustomSlide(customSlides[idx]);
                    customSlides[idx].image_id_mobile = att.id;
                    customSlides[idx].image_url_mobile = att.url;
                    activeCustomSlide = idx;
                    renderCustomSlides();
                });
                frame.open();
            });
            // Clear mobile image (custom slide)
            $(document).on('click', '.lbx-cs-clear-mobile-image', function() {
                syncSlideData(activeCustomSlide);
                customSlides[activeCustomSlide].image_id_mobile = 0;
                customSlides[activeCustomSlide].image_url_mobile = '';
                renderCustomSlides();
            });

            // Toggle media type fields
            $(document).on('change', '#lbx-custom-editor .lbx-cs-media-type', function() {
                syncSlideData(activeCustomSlide);
                renderCustomSlides();
            });

            // Pick video
            $(document).on('click', '#lbx-custom-editor .lbx-pick-video', function() {
                var idx = parseInt($(this).data('index'), 10) || 0;
                var frame = wp.media({
                    title: '<?php echo esc_js(__('Select Slide Video', 'leadbox')); ?>',
                    multiple: false,
                    library: { type: 'video' }
                });
                frame.on('select', function() {
                    var attachment = frame.state().get('selection').first().toJSON();
                    syncSlideData(activeCustomSlide);
                    customSlides[idx] = normalizeCustomSlide(customSlides[idx]);
                    customSlides[idx].video_id = attachment.id || 0;
                    customSlides[idx].video_url = attachment.url || '';
                    customSlides[idx].media_type = 'video';
                    activeCustomSlide = idx;
                    renderCustomSlides();
                });
                frame.open();
            });

            // Clear video
            $(document).on('click', '#lbx-custom-editor .lbx-clear-video', function() {
                $('#lbx-custom-editor .lbx-cs-video-url').val('');
                syncSlideData(activeCustomSlide);
                // Reset preview to placeholder
                var $img = $('#lbx-custom-editor .lbx-slide-image');
                $img.html('<div class="lbx-slide-image-ph"><span class="dashicons dashicons-format-video"></span><span><?php echo esc_js(__('Video slide', 'leadbox')); ?></span></div>');
            });

            // Bind load/error handlers on video previews (these events don't bubble, so we bind directly)
            function bindVideoPreviewEvents($container) {
                $container.find('.lbx-video-preview video').each(function() {
                    var vid = this;
                    vid.addEventListener('loadeddata', function() {
                        $(vid).siblings('.lbx-video-preview-loader').addClass('loaded');
                    });
                    vid.addEventListener('error', function() {
                        $(vid).closest('.lbx-slide-image').html('<div class="lbx-slide-image-ph"><span class="dashicons dashicons-warning"></span><span><?php echo esc_js(__('Cannot load video', 'leadbox')); ?></span></div>');
                    });
                });
            }

            // Live video preview when URL is pasted or typed
            $(document).on('input', '#lbx-custom-editor .lbx-cs-video-url', function() {
                var url = $(this).val().trim();
                var $img = $('#lbx-custom-editor .lbx-slide-image');
                if (!url) {
                    $img.html('<div class="lbx-slide-image-ph"><span class="dashicons dashicons-format-video"></span><span><?php echo esc_js(__('Video slide', 'leadbox')); ?></span></div>');
                    return;
                }
                $img.html('<div class="lbx-video-preview"><video src="' + esc(url) + '#t=0.1" preload="metadata" muted playsinline></video><div class="lbx-video-preview-loader"><span class="spinner is-active"></span></div></div>');
                bindVideoPreviewEvents($img);
            });

            // Update strip title while typing
            $(document).on('input', '#lbx-custom-editor .lbx-cs-title', function() {
                customSlides[activeCustomSlide] = normalizeCustomSlide(customSlides[activeCustomSlide]);
                customSlides[activeCustomSlide].title = $(this).val();
                var t = customSlides[activeCustomSlide].title || '<?php echo esc_js(__('Untitled Slide', 'leadbox')); ?>';
                $('#lbx-custom-slides .lbx-cs-card--active .lbx-cs-card-title').text(t);
            });

            // (accordion removed — extra slides now use card strip pattern)

            // ─── Countdown: Background image picker ─────────────────
            function openCdBgPicker() {
                var frame = wp.media({
                    title: '<?php echo esc_js(__('Select Background Image', 'leadbox')); ?>',
                    multiple: false,
                    library: { type: 'image' }
                });
                frame.on('select', function() {
                    var attachment = frame.state().get('selection').first().toJSON();
                    var url = attachment.url;
                    $('#lbx-cd-bg-image').val(url);
                    $('#lbx-cd-bg-thumb-img').attr('src', url);
                    $('#lbx-cd-bg-thumb').show();
                    $('#lbx-cd-bg-empty').hide();
                });
                frame.open();
            }
            $('#lbx-cd-pick-bg').on('click', openCdBgPicker);
            $('#lbx-cd-bg-thumb-img').on('click', openCdBgPicker);
            $('#lbx-cd-remove-bg').on('click', function() {
                $('#lbx-cd-bg-image').val('');
                $('#lbx-cd-bg-thumb').hide();
                $('#lbx-cd-bg-empty').show();
            });

            // ─── Slide Behavior: toggle autoplay speed input ─────
            $('#lbx-slider-autoplay').on('change', function() {
                $('#lbx-autoplay-speed-wrap').toggle($(this).is(':checked'));
            });

            // ─── Navigation: toggle arrow/pagination customization sections ─────
            $('#lbx-slider-show-arrows').on('change', function() {
                $('#lbx-arrows-options-wrap').toggle($(this).is(':checked'));
            });
            $('#lbx-slider-show-pagination').on('change', function() {
                $('#lbx-pagination-options-wrap').toggle($(this).is(':checked'));
            });
            // Live opacity value display
            $('#lbx-slider-arrow-opacity').on('input', function() {
                $('#lbx-slider-arrow-opacity-val').text($(this).val());
            });
            $('#lbx-cd-overlay-opacity').on('input', function() {
                $('#lbx-cd-overlay-val').text($(this).val());
            });
            // Sync color picker <-> hex text input for arrow color
            $('#lbx-slider-arrow-color-picker').on('input', function() {
                var v = $(this).val();
                $('#lbx-slider-arrow-color').val(v);
                $(this).siblings('.lbx-cc-fill').css('background', v);
            });
            $('#lbx-slider-arrow-color').on('input', function() {
                var v = $(this).val();
                if (/^#[0-9a-fA-F]{6}$/.test(v)) {
                    $('#lbx-slider-arrow-color-picker').val(v).siblings('.lbx-cc-fill').css('background', v);
                }
            });
            // Sync color picker <-> hex text input for pagination color
            $('#lbx-slider-pagination-color-picker').on('input', function() {
                var v = $(this).val();
                $('#lbx-slider-pagination-color').val(v);
                $(this).siblings('.lbx-cc-fill').css('background', v);
            });
            $('#lbx-slider-pagination-color').on('input', function() {
                var v = $(this).val();
                if (/^#[0-9a-fA-F]{6}$/.test(v)) {
                    $('#lbx-slider-pagination-color-picker').val(v).siblings('.lbx-cc-fill').css('background', v);
                }
            });

            // ─── Custom CSS helpers ──────────────────────────────
            // CodeMirror upgrade for the Custom CSS field (WP core editor: CSS
            // highlighting, linting, bracket auto-close, Tab indentation, line
            // numbers). Falls back silently to the plain textarea when the user
            // disabled syntax highlighting in their WP profile.
            var lbxCssCM = null;
            // This whole admin script is an IIFE that runs while the page is still
            // parsing — but wp_enqueue_code_editor() was called during the page
            // render, so WP prints code-editor.js in the FOOTER (after us). Retry
            // until it (and our settings bootstrap) are loaded; give up after ~3s
            // and the plain textarea simply stays.
            function lbxInitCssEditor(attempt) {
                if (lbxCssCM || !$('#lbx-slider-custom-css').length) return;
                if (!(window.wp && wp.codeEditor && window.lbxCssEditorSettings)) {
                    if ((attempt || 0) < 20) setTimeout(function() { lbxInitCssEditor((attempt || 0) + 1); }, 150);
                    return;
                }
                var lbxCssSettings = $.extend(true, {}, window.lbxCssEditorSettings);
                lbxCssSettings.codemirror = $.extend({}, lbxCssSettings.codemirror, {
                    indentUnit: 4,
                    tabSize: 4,
                    autoCloseBrackets: true,
                    matchBrackets: true,
                });
                // Ctrl-Space property autocomplete when core shipped the hint addon.
                if (window.CodeMirror && CodeMirror.showHint) {
                    lbxCssSettings.codemirror.extraKeys = $.extend({}, lbxCssSettings.codemirror.extraKeys, { 'Ctrl-Space': 'autocomplete' });
                }
                var lbxCssEditor = wp.codeEditor.initialize($('#lbx-slider-custom-css')[0], lbxCssSettings);
                lbxCssCM = lbxCssEditor.codemirror;
                // Keep the hidden textarea in sync on every change so everything that
                // reads it via .val() (collectSliderFormData, the scaffold check, the
                // Save/Preview flows) keeps working untouched.
                lbxCssCM.on('change', function() { lbxCssCM.save(); });
                // If the Developer panel is already visible when we finish initializing
                // (init is retried async), repaint once so content shows immediately.
                if ($('.lbx-tab-panel[data-tab="developer"]').hasClass('is-active')) {
                    setTimeout(function() { lbxCssCM.refresh(); }, 30);
                }
            }
            lbxInitCssEditor();
            // Unified read/write that works with or without CodeMirror.
            function lbxCssGetValue() {
                return lbxCssCM ? lbxCssCM.getValue() : $('#lbx-slider-custom-css').val();
            }
            function lbxCssSetValue(v) {
                if (lbxCssCM) { lbxCssCM.setValue(v); lbxCssCM.save(); }
                else { $('#lbx-slider-custom-css').val(v); }
            }

            // Tiny CSS pretty-printer for the "Format CSS" button. Token-walks the
            // source (comment/string/url() aware) and re-lays braces, semicolons and
            // 4-space indentation. Intentionally conservative — never drops content.
            function lbxFormatCss(src) {
                var out = '', indent = 0, i = 0, ch, inStr = '', inComment = false, parens = 0;
                var pad = function() { return Array(indent + 1).join('    '); };
                var nl = function() { out = out.replace(/[ \t]+$/, '') + '\n' + pad(); };
                src = String(src || '').replace(/\r\n?/g, '\n');
                while (i < src.length) {
                    ch = src[i];
                    if (inComment) {
                        out += ch;
                        // Closing a comment starts a fresh line — otherwise whatever
                        // follows gets glued right next to it on the same line.
                        if (ch === '*' && src[i + 1] === '/') { out += '/'; i += 2; inComment = false; nl(); continue; }
                        i++; continue;
                    }
                    if (inStr) {
                        out += ch;
                        if (ch === '\\') { out += src[i + 1] || ''; i += 2; continue; }
                        if (ch === inStr) inStr = '';
                        i++; continue;
                    }
                    if (ch === '/' && src[i + 1] === '*') { inComment = true; out += '/*'; i += 2; continue; }
                    if (ch === '"' || ch === "'") { inStr = ch; out += ch; i++; continue; }
                    if (ch === '(') { parens++; out += ch; i++; continue; }
                    if (ch === ')') { parens = Math.max(0, parens - 1); out += ch; i++; continue; }
                    if (ch === '{') { out = out.replace(/\s+$/, '') + ' {'; indent++; nl(); i++; continue; }
                    if (ch === '}') { indent = Math.max(0, indent - 1); out = out.replace(/\s+$/, ''); nl(); out += '}'; nl(); i++; continue; }
                    if (ch === ';' && !parens) { out = out.replace(/\s+$/, '') + ';'; nl(); i++; continue; }
                    if (ch === '\n' || ch === ' ' || ch === '\t') {
                        if (out !== '' && !/[\s{(]$/.test(out)) out += ' ';
                        i++; continue;
                    }
                    out += ch; i++;
                }
                return out
                    .split('\n').map(function(l) { return l.replace(/[ \t]+$/, ''); }).join('\n')
                    .replace(/\n{3,}/g, '\n\n')
                    .replace(/^\s+/, '')
                    .replace(/\s*$/, '\n');
            }
            $('#lbx-css-format').on('click', function() {
                var v = lbxCssGetValue();
                if (v.trim()) lbxCssSetValue(lbxFormatCss(v));
            });

            // "Insert new rule" button: append a fresh scoped rule and focus inside the braces.
            $('#lbx-css-insert-scope').on('click', function() {
                var $ta = $('#lbx-slider-custom-css');
                var scope = $ta.data('scope-class') || '';
                if (!scope) return;
                var current = lbxCssGetValue();
                var needsNewline = current.length > 0 && !/\n\s*$/.test(current);
                var placeholder = '<?php echo esc_js(__('Your styles', 'leadbox')); ?>';
                var snippet = (needsNewline ? '\n\n' : '') + scope + ' ' + '{\n    /* ' + placeholder + ' */\n}\n';
                if (lbxCssCM) {
                    lbxCssCM.setValue(current + snippet);
                    lbxCssCM.save();
                    lbxCssCM.focus();
                    // Select the placeholder comment body inside the new rule.
                    var line = lbxCssCM.lineCount() - 3;
                    lbxCssCM.setSelection({ line: line, ch: 4 }, { line: line, ch: 4 + 3 + placeholder.length + 3 });
                    lbxCssCM.scrollIntoView({ line: lbxCssCM.lineCount() - 1, ch: 0 });
                    return;
                }
                var insertAt = current.length;
                $ta.val(current + snippet);
                // Move cursor inside the new braces
                var cursorPos = insertAt + snippet.indexOf('/*') ;
                var el = $ta[0];
                el.focus();
                try { el.setSelectionRange(cursorPos, cursorPos + 2 + placeholder.length + 3); } catch (e) {}
                // Scroll the textarea to the bottom so the new rule is visible
                el.scrollTop = el.scrollHeight;
            });
            // Copy scope class to clipboard when clicked
            $('#lbx-css-scope-class').on('click', function() {
                var text = $(this).text();
                if (navigator.clipboard && navigator.clipboard.writeText) {
                    navigator.clipboard.writeText(text).then(function() {
                        showNotice('<?php echo esc_js(__('Scope class copied to clipboard.', 'leadbox')); ?>', 'success');
                    });
                }
            });

            // ─── Watermark image picker ─────────────────────────
            $('#lbx-watermark-upload').on('click', function(e) {
                e.preventDefault();
                var frame = wp.media({ title: '<?php echo esc_js(__('Select Watermark Image', 'leadbox')); ?>', multiple: false, library: { type: 'image' } });
                frame.on('select', function() {
                    var attachment = frame.state().get('selection').first().toJSON();
                    $('#lbx-watermark-image').val(attachment.url);
                    $('#lbx-watermark-preview').attr('src', attachment.url).show();
                    $('#lbx-watermark-remove').show();
                });
                frame.open();
            });
            $('#lbx-watermark-remove').on('click', function() {
                $('#lbx-watermark-image').val('');
                $('#lbx-watermark-preview').hide();
                $(this).hide();
            });

            // Bold/Italic toggle styling
            $(document).on('change', '.lbx-format-btn input', function() {
                $(this).closest('.lbx-format-btn').toggleClass('active', this.checked);
            });

            // Sync color swatch → hex input + fill preview (universal handler)
            $(document).on('input', '.lbx-cc-swatch', function() {
                var $chip = $(this).closest('.lbx-color-chip');
                $chip.find('.lbx-cc-fill').css('background', $(this).val());
                $chip.find('.lbx-color-chip__hex').val($(this).val());
            });
            // Sync hex input → swatch + fill preview
            $(document).on('input', '.lbx-color-chip__hex', function() {
                var v = $(this).val();
                if (/^#[0-9a-fA-F]{6}$/.test(v)) {
                    var $chip = $(this).closest('.lbx-color-chip');
                    $chip.find('.lbx-cc-swatch').val(v);
                    $chip.find('.lbx-cc-fill').css('background', v);
                }
            });

            // Dedicated CTA color field handlers (isolated from the generic .lbx-color-chip)
            $(document).on('input', '.lbx-cta-color__swatch', function() {
                var v = $(this).val();
                var $field = $(this).closest('.lbx-cta-color');
                $field.find('.lbx-cta-color__fill').css('background', v);
                $field.find('.lbx-cta-color__hex').val(v);
            });
            $(document).on('input', '.lbx-cta-color__hex', function() {
                var v = ($(this).val() || '').trim();
                if (/^#[0-9a-fA-F]{6}$/.test(v)) {
                    var $field = $(this).closest('.lbx-cta-color');
                    $field.find('.lbx-cta-color__swatch').val(v);
                    $field.find('.lbx-cta-color__fill').css('background', v);
                }
            });

            // ─── Dynamic CTA Add / Remove ────────────────────────────────────
            // Add CTA button — scoped to custom editor
            $(document).on('click', '#lbx-custom-editor .lbx-cta-add-btn', function() {
                syncSlideData(activeCustomSlide);
                var slide = customSlides[activeCustomSlide];
                if (!Array.isArray(slide.ctas)) slide.ctas = [];
                slide.ctas.push({ label: '', url: '', target: '_self', color: '', position: 'inline' });
                renderCustomSlides();
            });
            // Remove CTA button — scoped to custom editor
            $(document).on('click', '#lbx-custom-editor .lbx-cta-remove-btn', function() {
                var idx = parseInt($(this).closest('.lbx-cta-row').data('index'), 10);
                syncSlideData(activeCustomSlide);
                var slide = customSlides[activeCustomSlide];
                if (Array.isArray(slide.ctas) && slide.ctas.length > 1) {
                    slide.ctas.splice(idx, 1);
                } else {
                    slide.ctas = [{ label: '', url: '', target: '_self', color: '', position: 'inline' }];
                }
                renderCustomSlides();
            });

            // Defer initial render to document ready so jQuery UI Sortable is loaded.
            $(function() { renderCustomSlides(); });

            // ─── Extra Slides (for OEM Offers) — Editor only (cards live in unified preview) ──────
            var extraSlides = <?php echo json_encode(array_values($editing_extra_slides)); ?>;
            var activeExtraSlide = -1; // -1 = no slide selected; user must click a card
            var extraEditLang = 'en'; // LBT-1489: language tab (en|fr) being edited in the additional-slide card

            function normalizeExtraSlide(slide) {
                slide = slide || {};
                // Every extra needs a stable ID — unifiedSlides references extras by ID.
                if (!slide.id) slide.id = genExtraId();
                slide.position = parseInt(slide.position) || 1;
                slide.title = slide.title || '';
                slide.description = slide.description || '';
                slide.media_type = (slide.media_type === 'video' || slide.media_type === 'embed') ? slide.media_type : 'image';
                slide.video_url = slide.video_url || '';
                slide.video_id = slide.video_id || 0;
                slide.image_id = slide.image_id || 0;
                slide.image_url = slide.image_url || '';
                slide.image_size = slide.image_size || 'full';
                slide.image_fit = slide.image_fit || 'cover';
                slide = normalizeCtasArray(slide);
                slide.image_link_url = slide.image_link_url || '';
                slide.image_link_target = slide.image_link_target || '_self';
                slide.hide_watermark = !!slide.hide_watermark;
                slide.start_date = slide.start_date || '';
                slide.end_date = slide.end_date || '';
                slide.embed_code = slide.embed_code || '';
                // French variants (LBT-1489) — per-language content. Empty = inherits the
                // EN value on the live site (fallback handled in the Blade render).
                slide.title_fr = slide.title_fr || '';
                slide.description_fr = slide.description_fr || '';
                slide.media_type_fr = (slide.media_type_fr === 'video' || slide.media_type_fr === 'embed' || slide.media_type_fr === 'image') ? slide.media_type_fr : '';
                slide.video_url_fr = slide.video_url_fr || '';
                slide.image_id_fr = slide.image_id_fr || 0;
                slide.image_url_fr = slide.image_url_fr || '';
                slide.image_id_mobile_fr = slide.image_id_mobile_fr || 0;
                slide.image_url_mobile_fr = slide.image_url_mobile_fr || '';
                slide.image_size_fr = slide.image_size_fr || '';
                slide.image_fit_fr = slide.image_fit_fr || '';
                slide.ctas_fr = Array.isArray(slide.ctas_fr) ? slide.ctas_fr : [];
                slide.image_link_url_fr = slide.image_link_url_fr || '';
                slide.image_link_target_fr = slide.image_link_target_fr || '_self';
                slide.hide_watermark_fr = !!slide.hide_watermark_fr;
                slide.embed_code_fr = slide.embed_code_fr || '';
                // Disclaimer popup text (LBT-1460). Extras don't have a feed
                // fallback, so these are the sole source for the icon's popup.
                slide.disclaimer_en = slide.disclaimer_en || '';
                slide.disclaimer_fr = slide.disclaimer_fr || '';
                // Per-slide kill switch — hides the icon on this slide even when text exists.
                slide.hide_disclaimer = !!slide.hide_disclaimer;
                // Per-slide responsive visibility — buckets where the slide is hidden.
                slide.hidden_on = Array.isArray(slide.hidden_on) ? slide.hidden_on.filter(function(b){ return b === 'mobile' || b === 'tablet' || b === 'desktop'; }) : [];
                return slide;
            }

            // True when an extra has no real content yet (no media, no title/description).
            // A brand-new "Add Slide" draft the user closes without adding anything must
            // NOT persist as a blank "Untitled Slide".
            function extraSlideIsEmpty(s) {
                if (!s) return true;
                // LBT-1489: a slide counts as non-empty if EITHER language has content, so
                // filling only the FR tab won't get the draft discarded on close.
                function hasContent(suf) {
                    var mt = s['media_type' + suf] || (suf ? '' : 'image');
                    var hasMedia = (mt === 'video')
                        ? !!(s['video_url' + suf] || s.video_id)
                        : (mt === 'embed')
                            ? !!(s['embed_code' + suf] && String(s['embed_code' + suf]).trim())
                            : !!(s['image_url' + suf] || s['image_url_mobile' + suf]);
                    if (hasMedia) return true;
                    if ((s['title' + suf] && String(s['title' + suf]).trim()) || (s['description' + suf] && String(s['description' + suf]).trim())) return true;
                    return false;
                }
                return !(hasContent('') || hasContent('_fr'));
            }

            function renderExtraSlides() {
                var $editorWrap = $('#lbx-extra-editor').empty();
                if (extraSlides.length === 0) activeExtraSlide = -1;
                if (activeExtraSlide >= extraSlides.length) activeExtraSlide = -1;

                // Refresh the unified preview grid so the new/edited/removed extra
                // card appears in the right place. Gated on `oemPreviewReady` (AJAX
                // has resolved) — running this before the Minerva fetch returns would
                // drop every minerva entry from unifiedSlides and silently reshuffle
                // the saved order so extras end up first on the next sync.
                if (oemPreviewReady) {
                    unifiedSlides = syncUnifiedSlides(lastOffersList, extraSlides);
                    renderUnifiedPreview();
                }

                // No slide being edited — close the modal (safe no-op if already closed).
                if (activeExtraSlide < 0) {
                    $editorWrap.empty();
                    closeEditModal($('#lbx-ex-modal'));
                    return;
                }

                var sBase = normalizeExtraSlide(extraSlides[activeExtraSlide]);
                // Active editor language (LBT-1489). The card edits ONE language at a time;
                // `s` is a per-language VIEW of the slide so all existing markup (s.title,
                // s.image_url, s.ctas, …) renders the active language with zero template churn.
                // Text/media fields show the raw FR value (empty = the user still needs to fill
                // it; the live site falls back to EN). Selects/toggles inherit the EN value when
                // the FR slot is blank, so they behave sensibly out of the box.
                var EL = (typeof extraEditLang !== 'undefined' && extraEditLang === 'fr') ? 'fr' : 'en';
                var s = (EL === 'fr') ? Object.assign({}, sBase, {
                    title: sBase.title_fr, description: sBase.description_fr,
                    video_url: sBase.video_url_fr, embed_code: sBase.embed_code_fr,
                    image_url: sBase.image_url_fr, image_id: sBase.image_id_fr,
                    image_url_mobile: sBase.image_url_mobile_fr, image_id_mobile: sBase.image_id_mobile_fr,
                    image_link_url: sBase.image_link_url_fr,
                    ctas: sBase.ctas_fr,
                    media_type: sBase.media_type_fr || sBase.media_type,
                    image_size: sBase.image_size_fr || sBase.image_size,
                    image_fit: sBase.image_fit_fr || sBase.image_fit,
                    image_link_target: sBase.image_link_target_fr || sBase.image_link_target,
                    hide_watermark: sBase.hide_watermark_fr,
                    disclaimer_active: sBase.disclaimer_fr
                }) : Object.assign({}, sBase, { disclaimer_active: sBase.disclaimer_en });
                var mediaType = (s.media_type === 'video' || s.media_type === 'embed') ? s.media_type : 'image';
                var videoWrapStyle = mediaType === 'video' ? '' : 'display:none;';
                var embedWrapStyle = mediaType === 'embed' ? '' : 'display:none;';
                // Hide the whole image block when type=embed — no point showing a clickable
                // image picker when the slide is configured as an iframe embed.
                var imageBlockStyle = mediaType === 'embed' ? 'display:none;' : '';
                var imgPreview;
                if (mediaType === 'embed') {
                    imgPreview = '<div class="lbx-slide-image-ph"><span class="dashicons dashicons-editor-code"></span><span><?php echo esc_js(__('HTML Embed', 'leadbox')); ?></span></div>';
                } else if (mediaType === 'video') {
                    imgPreview = s.video_url
                        ? '<div class="lbx-video-preview"><video src="' + esc(s.video_url) + '#t=0.1" preload="metadata" muted playsinline></video><div class="lbx-video-preview-loader"><span class="spinner is-active"></span></div></div>'
                        : '<div class="lbx-slide-image-ph"><span class="dashicons dashicons-format-video"></span><span><?php echo esc_js(__('Video slide', 'leadbox')); ?></span></div>';
                } else {
                    imgPreview = s.image_url
                        ? '<img src="' + esc(s.image_url) + '" alt="">'
                        : '<div class="lbx-slide-image-ph"><span class="dashicons dashicons-cloud-upload"></span><span><?php echo esc_js(__('Click to upload', 'leadbox')); ?></span></div>';
                }

                $editorWrap.html(
                    '<div class="lbx-cs-editor-head">' +
                        '<h4 class="lbx-cs-editor-title"><?php echo esc_js(__('Editing Additional Slide', 'leadbox')); ?> <span class="lbx-ex-editlang' + (EL === 'fr' ? ' is-fr' : '') + '">' + (EL === 'fr' ? '<?php echo esc_js(__('French', 'leadbox')); ?>' : '<?php echo esc_js(__('English', 'leadbox')); ?>') + '</span></h4>' +
                        '<div class="lbx-cs-editor-actions">' +
                            '<button type="button" class="lbx-ex-remove" title="<?php echo esc_attr__('Remove', 'leadbox'); ?>"><span class="dashicons dashicons-trash"></span></button>' +
                        '</div>' +
                    '</div>' +
                    '<div class="lbx-cs-editor-body">' +
                        // Language tabs (LBT-1489) — switch the WHOLE slide (media, images,
                        // text, CTAs, disclaimer) between EN and FR. Scheduling stays shared.
                        '<div class="lbx-ex-langtabs">' +
                            '<button type="button" class="lbx-ex-langtab' + (EL === 'en' ? ' is-active' : '') + '" data-lang="en"><?php echo esc_js(__('English', 'leadbox')); ?></button>' +
                            '<button type="button" class="lbx-ex-langtab' + (EL === 'fr' ? ' is-active' : '') + '" data-lang="fr"><?php echo esc_js(__('French', 'leadbox')); ?></button>' +
                        '</div>' +
                        '<div class="lbx-slide-top-row">' +
                            '<div>' +
                                // Media-type selector — top of the column so the user picks the
                                // kind of background first; everything below adapts to this.
                                '<div style="margin-bottom:10px;">' +
                                    '<label style="display:block;font-size:11px;font-weight:600;color:var(--lbx-gray-500);margin-bottom:4px;text-transform:uppercase;"><?php echo esc_js(__('Background Media', 'leadbox')); ?></label>' +
                                    '<select class="lbx-ex-media-type" style="width:100%;padding:6px 8px;border:1px solid var(--lbx-gray-300);border-radius:var(--lbx-radius-sm);font-size:12px;">' +
                                        '<option value="image"' + (mediaType === 'image' ? ' selected' : '') + '><?php echo esc_js(__('Image', 'leadbox')); ?></option>' +
                                        '<option value="video"' + (mediaType === 'video' ? ' selected' : '') + '><?php echo esc_js(__('Video (MP4/WebM)', 'leadbox')); ?></option>' +
                                        '<option value="embed"' + (mediaType === 'embed' ? ' selected' : '') + '><?php echo esc_js(__('HTML Embed (iframe, etc.)', 'leadbox')); ?></option>' +
                                    '</select>' +
                                '</div>' +
                                // Type-specific input directly under the selector.
                                '<div class="lbx-ex-video-wrap" style="margin-bottom:10px;' + videoWrapStyle + '">' +
                                    '<input type="url" class="lbx-ex-video-url" value="' + esc(s.video_url) + '" placeholder="<?php echo esc_attr__('https://.../clip.mp4', 'leadbox'); ?>" style="width:100%;padding:6px 8px;border:1px solid var(--lbx-gray-300);border-radius:var(--lbx-radius-sm);font-size:12px;">' +
                                    '<div style="display:flex;gap:6px;margin-top:6px;">' +
                                        '<button type="button" class="button button-small lbx-pick-extra-video" data-index="' + activeExtraSlide + '"><?php echo esc_js(__('Choose Video', 'leadbox')); ?></button>' +
                                        '<button type="button" class="button button-small lbx-clear-extra-video"><?php echo esc_js(__('Clear', 'leadbox')); ?></button>' +
                                    '</div>' +
                                '</div>' +
                                '<div class="lbx-ex-embed-wrap" style="margin-bottom:10px;' + embedWrapStyle + '">' +
                                    '<textarea class="lbx-ex-embed-code" placeholder="<?php echo esc_attr__('Paste your iframe / embed HTML here…', 'leadbox'); ?>" rows="5" spellcheck="false" style="width:100%;padding:8px 10px;border:1px solid var(--lbx-gray-300);border-radius:var(--lbx-radius-sm);font-family:SFMono-Regular,Consolas,monospace;font-size:11px;line-height:1.5;background:#f9fafb;">' + esc(s.embed_code || '') + '</textarea>' +
                                    '<p style="margin:4px 0 0;font-size:10px;color:var(--lbx-gray-500);"><span class="dashicons dashicons-info" style="font-size:11px;width:11px;height:11px;vertical-align:text-bottom;"></span> <?php echo esc_js(__('Paste full HTML (iframe, video tag, etc.).', 'leadbox')); ?></p>' +
                                '</div>' +
                                // Image block — preview / mobile / size / fit. Hidden whole when type=embed.
                                '<div class="lbx-ex-image-block" style="' + imageBlockStyle + '">' +
                                    // Only add the picker modifier when type=image. For video
                                    // the preview is just visual feedback (frame at #t=0.1), not a
                                    // clickable picker — the "Choose Video" button above handles that.
                                    '<div class="lbx-slide-image' + (mediaType === 'image' ? ' lbx-pick-extra-image' : '') + '" data-index="' + activeExtraSlide + '">' + imgPreview + '</div>' +
                                    // Mobile image override — optional.
                                    '<div class="lbx-slide-mobile-image-row" style="margin-top:6px;">' +
                                        '<label style="display:block;font-size:10px;font-weight:600;color:var(--lbx-gray-500);text-transform:uppercase;letter-spacing:0.03em;margin-bottom:4px;"><?php echo esc_js(__('Mobile image', 'leadbox')); ?> <span style="font-weight:400;color:var(--lbx-gray-400);text-transform:none;letter-spacing:0;"><?php echo esc_js(__('(optional)', 'leadbox')); ?></span></label>' +
                                        '<div style="display:flex;gap:6px;align-items:center;">' +
                                            (s.image_url_mobile
                                                ? '<img class="lbx-ex-mobile-thumb" src="' + esc(s.image_url_mobile) + '" alt="" style="width:42px;height:42px;object-fit:cover;border:1px solid var(--lbx-gray-300);border-radius:var(--lbx-radius-sm);flex-shrink:0;">'
                                                : '<div class="lbx-ex-mobile-thumb lbx-ex-mobile-thumb--empty" style="width:42px;height:42px;background:var(--lbx-gray-100);border:1px dashed var(--lbx-gray-300);border-radius:var(--lbx-radius-sm);display:flex;align-items:center;justify-content:center;color:var(--lbx-gray-400);flex-shrink:0;"><span class="dashicons dashicons-smartphone" style="font-size:16px;width:16px;height:16px;"></span></div>'
                                            ) +
                                            '<button type="button" class="lbx-btn lbx-btn-secondary lbx-btn-sm lbx-ex-pick-mobile-image" data-index="' + activeExtraSlide + '" style="flex:1;justify-content:center;"><?php echo esc_js(__('Choose…', 'leadbox')); ?></button>' +
                                            (s.image_url_mobile ? '<button type="button" class="lbx-btn lbx-btn-secondary lbx-btn-sm lbx-ex-clear-mobile-image" title="<?php echo esc_attr__('Remove mobile image', 'leadbox'); ?>">&times;</button>' : '') +
                                        '</div>' +
                                        '<p style="margin:4px 0 0;font-size:10px;color:var(--lbx-gray-400);line-height:1.3;"><?php echo esc_js(__('Used on mobile (≤640px). Falls back to the main image if empty.', 'leadbox')); ?></p>' +
                                    '</div>' +
                                    '<select class="lbx-ex-image-size" style="width:100%;padding:5px 8px;margin-top:6px;border:1px solid var(--lbx-gray-300);border-radius:var(--lbx-radius-sm);font-size:11px;color:var(--lbx-gray-600);">' +
                                        '<option value="full"' + (s.image_size === 'full' ? ' selected' : '') + '><?php echo esc_js(__('Full Size', 'leadbox')); ?></option>' +
                                        '<option value="large"' + (s.image_size === 'large' ? ' selected' : '') + '><?php echo esc_js(__('Large (1024px)', 'leadbox')); ?></option>' +
                                        '<option value="medium_large"' + (s.image_size === 'medium_large' ? ' selected' : '') + '><?php echo esc_js(__('Medium (768px)', 'leadbox')); ?></option>' +
                                    '</select>' +
                                    '<select class="lbx-ex-image-fit" style="width:100%;padding:5px 8px;margin-top:6px;border:1px solid var(--lbx-gray-300);border-radius:var(--lbx-radius-sm);font-size:11px;color:var(--lbx-gray-600);">' +
                                        '<option value="cover"' + (s.image_fit === 'cover' ? ' selected' : '') + '><?php echo esc_js(__('Cover — Fill (may crop)', 'leadbox')); ?></option>' +
                                        '<option value="contain"' + (s.image_fit === 'contain' ? ' selected' : '') + '><?php echo esc_js(__('Contain — Fit entire image', 'leadbox')); ?></option>' +
                                        '<option value="fill"' + (s.image_fit === 'fill' ? ' selected' : '') + '><?php echo esc_js(__('Fill — Stretch to fill', 'leadbox')); ?></option>' +
                                        '<option value="scale-down"' + (s.image_fit === 'scale-down' ? ' selected' : '') + '><?php echo esc_js(__('Scale Down — Fit without enlarging', 'leadbox')); ?></option>' +
                                    '</select>' +
                                '</div>' +
                            '</div>' +
                            '<div class="lbx-slide-fields">' +
                                '<div class="lbx-slide-field">' +
                                    '<label><?php _e('Title', 'leadbox'); ?> <span class="lbx-ex-fldlang' + (EL === 'fr' ? ' is-fr' : '') + '">' + (EL === 'fr' ? 'FR' : 'EN') + '</span> <span class="lbx-label-hint"><?php _e('HTML allowed', 'leadbox'); ?></span></label>' +
                                    '<div class="lbx-rte-wrap">' +
                                        lbxRteToolsHtml() +
                                        '<textarea class="lbx-ex-title" rows="2" placeholder="<?php echo esc_attr__('e.g. Spring Event', 'leadbox'); ?>">' + esc(s.title) + '</textarea>' +
                                    '</div>' +
                                '</div>' +
                                '<div class="lbx-slide-field">' +
                                    '<label><?php _e('Description', 'leadbox'); ?> <span class="lbx-ex-fldlang' + (EL === 'fr' ? ' is-fr' : '') + '">' + (EL === 'fr' ? 'FR' : 'EN') + '</span> <span style="font-weight:400;color:var(--lbx-gray-400);"><?php _e('HTML allowed', 'leadbox'); ?></span></label>' +
                                    '<div class="lbx-rte-wrap">' +
                                        lbxRteToolsHtml() +
                                        '<textarea class="lbx-ex-desc" rows="2" placeholder="<?php echo esc_attr__('Optional description', 'leadbox'); ?>">' + esc(s.description) + '</textarea>' +
                                    '</div>' +
                                '</div>' +
                                buildCtasUI(s.ctas, 'lbx-ex') +
                                // Content position — where the title/description/CTAs block sits on the
                                // slide (language-agnostic; applies to image-overlay layouts).
                                '<div class="lbx-cta-group" style="margin-top:14px;">' +
                                    '<h5><span class="dashicons dashicons-move"></span> <?php _e('Content Position', 'leadbox'); ?> <span class="lbx-label-hint" style="font-size:10px;"><?php _e('(title, description & CTAs)', 'leadbox'); ?></span></h5>' +
                                    '<select class="lbx-ex-content-position" style="width:100%;max-width:260px;padding:5px 8px;border:1px solid var(--lbx-gray-300);border-radius:var(--lbx-radius-sm);font-size:12px;color:var(--lbx-gray-600);">' +
                                        '<option value=""' + (!s.content_position ? ' selected' : '') + '><?php echo esc_js(__('Default (middle, follows alignment)', 'leadbox')); ?></option>' +
                                        '<option value="top-left"' + (s.content_position === 'top-left' ? ' selected' : '') + '><?php echo esc_js(__('Top Left', 'leadbox')); ?></option>' +
                                        '<option value="top-center"' + (s.content_position === 'top-center' ? ' selected' : '') + '><?php echo esc_js(__('Top Center', 'leadbox')); ?></option>' +
                                        '<option value="top-right"' + (s.content_position === 'top-right' ? ' selected' : '') + '><?php echo esc_js(__('Top Right', 'leadbox')); ?></option>' +
                                        '<option value="bottom-left"' + (s.content_position === 'bottom-left' ? ' selected' : '') + '><?php echo esc_js(__('Bottom Left', 'leadbox')); ?></option>' +
                                        '<option value="bottom-center"' + (s.content_position === 'bottom-center' ? ' selected' : '') + '><?php echo esc_js(__('Bottom Center', 'leadbox')); ?></option>' +
                                        '<option value="bottom-right"' + (s.content_position === 'bottom-right' ? ' selected' : '') + '><?php echo esc_js(__('Bottom Right', 'leadbox')); ?></option>' +
                                    '</select>' +
                                '</div>' +
                                '<div class="lbx-cta-group">' +
                                    '<h5><span class="dashicons dashicons-admin-links"></span> <?php _e('Image Link', 'leadbox'); ?> <span class="lbx-label-hint" style="font-size:10px;"><?php _e('(optional)', 'leadbox'); ?></span></h5>' +
                                    '<div class="lbx-cta-fields">' +
                                        '<input type="url" class="lbx-ex-image-link-url" value="' + esc(s.image_link_url || '') + '" placeholder="<?php echo esc_attr__('https://...', 'leadbox'); ?>">' +
                                        '<select class="lbx-ex-image-link-target" style="padding:6px 10px;border:1px solid var(--lbx-gray-300);border-radius:var(--lbx-radius-sm);font-size:12px;">' +
                                            '<option value="_self"' + ((s.image_link_target || '_self') === '_self' ? ' selected' : '') + '><?php echo esc_js(__('Same tab', 'leadbox')); ?></option>' +
                                            '<option value="_blank"' + ((s.image_link_target || '_self') === '_blank' ? ' selected' : '') + '><?php echo esc_js(__('New tab', 'leadbox')); ?></option>' +
                                        '</select>' +
                                    '</div>' +
                                '</div>' +
                                '<label class="lbx-toggle-card">' +
                                    '<input type="checkbox" class="lbx-ex-hide-watermark"' + (s.hide_watermark ? ' checked' : '') + '>' +
                                    '<span class="lbx-toggle-card__icon dashicons dashicons-hidden"></span>' +
                                    '<span class="lbx-toggle-card__label"><?php echo esc_js(__('Hide watermark on this slide', 'leadbox')); ?></span>' +
                                '</label>' +
                                // Disclaimer popup text (LBT-1460) — now per language (LBT-1489). Shows
                                // the active language's text; the hide toggle is shared across both.
                                '<div class="lbx-cta-group" style="margin-top:14px;">' +
                                    '<h5><span class="dashicons dashicons-info-outline"></span> <?php _e('Disclaimer', 'leadbox'); ?> <span class="lbx-ex-fldlang' + (EL === 'fr' ? ' is-fr' : '') + '">' + (EL === 'fr' ? 'FR' : 'EN') + '</span></h5>' +
                                    '<p class="lbx-field-hint"><?php echo esc_js(__('Optional — shown in the info popup. The icon is hidden when this is blank.', 'leadbox')); ?></p>' +
                                    '<div class="lbx-cta-fields" style="flex-direction:column;gap:8px;">' +
                                        '<textarea class="lbx-ex-disclaimer" rows="2" placeholder="' + (EL === 'fr' ? '<?php echo esc_attr__('Texte brut ou HTML', 'leadbox'); ?>' : '<?php echo esc_attr__('Plain text or HTML', 'leadbox'); ?>') + '">' + esc(s.disclaimer_active || '') + '</textarea>' +
                                    '</div>' +
                                    '<label style="display:flex;align-items:center;gap:8px;margin-top:10px;cursor:pointer;">' +
                                        '<input type="checkbox" class="lbx-ex-hide-disclaimer"' + (s.hide_disclaimer ? ' checked' : '') + '>' +
                                        '<span style="font-size:12px;color:var(--lbx-gray-600);"><?php echo esc_js(__('Hide disclaimer icon on this slide', 'leadbox')); ?></span>' +
                                    '</label>' +
                                '</div>' +
                                // Per-slide Animated Frame Line (language-agnostic, like scheduling).
                                // The button opens the shared FL modal, which edits ex.frame_line directly.
                                '<div class="lbx-cta-group" style="margin-top:14px;">' +
                                    '<h5><span class="dashicons dashicons-image-crop"></span> <?php _e('Animated Frame Line', 'leadbox'); ?></h5>' +
                                    '<button type="button" class="lbx-btn lbx-btn-secondary lbx-btn-sm lbx-ex-fl-configure" data-extra-id="' + esc(s.id || '') + '">' +
                                        '<span class="dashicons dashicons-admin-customizer"></span> <?php echo esc_js(__('Configure…', 'leadbox')); ?>' +
                                    '</button>' +
                                '</div>' +
                                buildScheduleUI(s.start_date, s.end_date, 'lbx-ex') +
                            '</div>' +
                        '</div>' +
                    '</div>'
                );
                bindVideoPreviewEvents($editorWrap);
            }

            // Same pattern as scrollToCustomEditor — retained for existing call sites.
            function scrollToExtraEditor() {
                openEditModal($('#lbx-ex-modal'));
            }
            function closeExtraSlideModal() {
                if (activeExtraSlide >= 0) {
                    syncExtraData(activeExtraSlide);
                    // Opened "Add Slide" but added nothing → drop the empty draft instead
                    // of leaving a blank "Untitled Slide" behind.
                    var s = extraSlides[activeExtraSlide];
                    if (extraSlideIsEmpty(s)) {
                        var removedId = s && s.id;
                        extraSlides.splice(activeExtraSlide, 1);
                        if (removedId) {
                            for (var i = unifiedSlides.length - 1; i >= 0; i--) {
                                if (unifiedSlides[i] && unifiedSlides[i].type === 'extra' && unifiedSlides[i].id === removedId) {
                                    unifiedSlides.splice(i, 1);
                                }
                            }
                        }
                    }
                }
                activeExtraSlide = -1;
                closeEditModal($('#lbx-ex-modal'));
                renderExtraSlides();
            }
            $(document).on('click', '#lbx-ex-modal-close', closeExtraSlideModal);
            $(document).on('click', '#lbx-ex-modal', function(e) { if (e.target === this) closeExtraSlideModal(); });
            // ESC closes whichever slide edit modal is open (only one can be active at a time).
            $(document).on('keydown.lbxEditModal', function(e) {
                if (e.key !== 'Escape') return;
                var $open = $('.lbx-edit-modal-backdrop.is-open').first();
                if (!$open.length) return;
                if ($open.is('#lbx-cs-modal')) closeCustomSlideModal();
                else if ($open.is('#lbx-ex-modal')) closeExtraSlideModal();
            });

            function syncExtraData(index) {
                if (!extraSlides.length) return;
                var idx = (typeof index === 'number') ? index : activeExtraSlide;
                if (idx < 0 || idx >= extraSlides.length) return;
                var $s = $('#lbx-extra-editor');
                if (!$s.length) return;
                extraSlides[idx] = normalizeExtraSlide(extraSlides[idx]);
                var o = extraSlides[idx];
                // Write into the slot for the language currently being edited (LBT-1489).
                // sfx '' = EN (the base fields), '_fr' = the French variants.
                var sfx = (typeof extraEditLang !== 'undefined' && extraEditLang === 'fr') ? '_fr' : '';
                o['title' + sfx] = $s.find('.lbx-ex-title').val() || '';
                o['description' + sfx] = $s.find('.lbx-ex-desc').val() || '';
                o['media_type' + sfx] = $s.find('.lbx-ex-media-type').val() || 'image';
                o['video_url' + sfx] = $s.find('.lbx-ex-video-url').val() || '';
                o['embed_code' + sfx] = $s.find('.lbx-ex-embed-code').val() || '';
                o['image_size' + sfx] = $s.find('.lbx-ex-image-size').val() || 'full';
                o['image_fit' + sfx] = $s.find('.lbx-ex-image-fit').val() || 'cover';
                o['image_link_url' + sfx] = $s.find('.lbx-ex-image-link-url').val() || '';
                o['image_link_target' + sfx] = $s.find('.lbx-ex-image-link-target').val() || '_self';
                o['hide_watermark' + sfx] = $s.find('.lbx-ex-hide-watermark').is(':checked');
                o[(sfx ? 'ctas_fr' : 'ctas')] = readCtasFromDom($s, 'lbx-ex');
                o[(sfx ? 'disclaimer_fr' : 'disclaimer_en')] = $s.find('.lbx-ex-disclaimer').val() || '';
                o.video_id = (o.video_url || o.video_url_fr) ? (o.video_id || 0) : 0;
                // Shared across both languages (LBT-1489): scheduling + the disclaimer kill switch.
                o.hide_disclaimer = $s.find('.lbx-ex-hide-disclaimer').is(':checked');
                o.content_position = $s.find('.lbx-ex-content-position').val() || '';
                // NOTE: o.frame_line is intentionally NOT synced from the editor DOM —
                // it's an object edited by the shared FL modal (flWriteTargetConfig).
                o.start_date = $s.find('.lbx-ex-start-date').val() || '';
                o.end_date = $s.find('.lbx-ex-end-date').val() || '';
            }

            // Edit click — opens the modal for the clicked extra card in the unified preview.
            $(document).on('click', '#lbx-oem-preview .lbx-ex-card-edit', function(e) {
                e.stopPropagation();
                var id = $(this).closest('.lbx-oem-preview__card').data('extra-id');
                if (!id) return;
                var idx = -1;
                for (var i = 0; i < extraSlides.length; i++) {
                    if (extraSlides[i] && extraSlides[i].id === id) { idx = i; break; }
                }
                if (idx < 0) return;
                if (idx !== activeExtraSlide) {
                    syncExtraData(activeExtraSlide);
                    activeExtraSlide = idx;
                    extraEditLang = 'en'; // LBT-1489: every slide opens on the EN tab
                    renderExtraSlides();
                }
                scrollToExtraEditor();
            });

            // Quick remove from the card itself — confirms then drops the extra.
            $(document).on('click', '#lbx-oem-preview .lbx-ex-card-remove', function(e) {
                e.stopPropagation();
                var id = $(this).closest('.lbx-oem-preview__card').data('extra-id');
                if (!id) return;
                lbxConfirm({
                    title: '<?php echo esc_js(__('Remove this slide?', 'leadbox')); ?>',
                    message: '<?php echo esc_js(__('This additional slide will be removed from the slider. The change applies after saving.', 'leadbox')); ?>',
                    confirmText: '<?php echo esc_js(__('Remove slide', 'leadbox')); ?>',
                    cancelText: '<?php echo esc_js(__('Keep', 'leadbox')); ?>',
                    danger: true
                }).then(function(ok) {
                    if (!ok) return;
                    var idx = -1;
                    for (var i = 0; i < extraSlides.length; i++) {
                        if (extraSlides[i] && extraSlides[i].id === id) { idx = i; break; }
                    }
                    if (idx < 0) return;
                    extraSlides.splice(idx, 1);
                    if (activeExtraSlide === idx) activeExtraSlide = -1;
                    else if (activeExtraSlide > idx) activeExtraSlide -= 1;
                    renderExtraSlides();
                });
            });

            // Add Slide button (next to the section title). Adds a new extra to the
            // end of the unified list and opens its editor immediately.
            $(document).on('click', '#lbx-add-extra-slide', function() {
                syncExtraData(activeExtraSlide);
                var newSlide = normalizeExtraSlide({});
                // New slides go to the FRONT of the list (both the data pool and the unified order).
                extraSlides.unshift(newSlide);
                unifiedSlides.unshift({ type: 'extra', id: newSlide.id });
                activeExtraSlide = 0;
                extraEditLang = 'en'; // LBT-1489: new slide opens on the EN tab
                renderExtraSlides();
                scrollToExtraEditor();
            });

            // ─── Static Offers ────────────────────────────────────────────
            // Fetch the dealer's Minerva static offers into staticOfferData. Shared by the
            // "Add" button and by the initial load (so saved static cards can render their
            // image/title). cb receives the offers array on success.
            function fetchStaticOffers(cb) {
                $.ajax({
                    url: '<?php echo rest_url('lbx-slider/v1/static-offers-preview'); ?>',
                    method: 'GET',
                    beforeSend: function(xhr) { xhr.setRequestHeader('X-WP-Nonce', '<?php echo wp_create_nonce('wp_rest'); ?>'); },
                    success: function(data) {
                        var offers = (data && data.offers) ? data.offers : [];
                        staticOffersReady = true;
                        offers.forEach(function(o) { staticOfferData[String(o.id)] = o; });
                        if (cb) cb(offers, null);
                    },
                    error: function() {
                        staticOffersReady = true;
                        if (cb) cb([], 'error');
                    }
                });
            }

            // Append every static offer (by id) that isn't already in the unified list.
            // Returns how many were added. Used by auto-populate (toggle ON).
            function autoAppendStatic(offers) {
                var inList = {};
                unifiedSlides.forEach(function(u) { if (u.type === 'static') inList[u.key] = true; });
                var added = 0;
                offers.forEach(function(o) {
                    if (!inList[String(o.id)]) { unifiedSlides.push({ type: 'static', key: String(o.id) }); added++; }
                });
                return added;
            }

            // On load: fetch static offers if the slider needs them — either because it
            // already references some, OR because the Static switch is ON (pull all).
            function loadSavedStaticOffers() {
                var hasStatic = (unifiedSlides || []).some(function(u) { return u.type === 'static'; });
                if ((hasStatic || autoPopulateStatic) && !staticOffersLoaded) {
                    staticOffersLoaded = true;
                    showPreviewBusy('<?php echo esc_js(__('Loading static offers…', 'leadbox')); ?>');
                    fetchStaticOffers(function(offers) {
                        hidePreviewBusy();
                        if (autoPopulateStatic && offers && offers.length) autoAppendStatic(offers);
                        renderUnifiedPreview();
                    });
                }
            }

            // ─── Auto-populate switches (LBT-1472) ─────────────────────────
            // The make filter only makes sense while at least one auto-populate
            // source is ON — there's nothing to filter otherwise. The saved value
            // is kept, so re-enabling a switch restores the previous filter.
            function updateMakeFilterVisibility() {
                $('.lbx-make-filter').toggle(autoPopulateOem || autoPopulateStatic);
            }

            // OEM Offers switch — append/remove the dealer's per-model OEM offers.
            $(document).on('change', '#lbx-auto-oem', function() {
                autoPopulateOem = $(this).is(':checked');
                updateMakeFilterVisibility();
                if (autoPopulateOem) {
                    if (!oemPreviewReady) {
                        // OEM AJAX still in flight — show the loader; loadOemPreview's resolve
                        // hides it and renders with the OEM offers included.
                        showPreviewBusy('<?php echo esc_js(__('Loading OEM offers…', 'leadbox')); ?>');
                        loadOemPreview();
                        return;
                    }
                    // Already loaded → syncUnifiedSlides re-appends OEM instantly.
                    unifiedSlides = syncUnifiedSlides(lastOffersList, extraSlides);
                    renderUnifiedPreview();
                } else {
                    unifiedSlides = unifiedSlides.filter(function(u) { return u.type !== 'minerva'; });
                    renderUnifiedPreview();
                }
                updateRefreshDataBtn();
            });

            // Static Offers switch — append/remove the dealer's static promo offers.
            $(document).on('change', '#lbx-auto-static', function() {
                autoPopulateStatic = $(this).is(':checked');
                updateMakeFilterVisibility();
                if (autoPopulateStatic) {
                    if (staticOffersReady) {
                        autoAppendStatic(Object.keys(staticOfferData).map(function(k){ return staticOfferData[k]; }));
                        renderUnifiedPreview();
                    } else {
                        // First fetch — show the loader while we hit Minerva (can take a few secs).
                        showPreviewBusy('<?php echo esc_js(__('Loading static offers…', 'leadbox')); ?>');
                        staticOffersLoaded = true;
                        fetchStaticOffers(function(offers, err) {
                            hidePreviewBusy();
                            if (err) { showNotice('<?php echo esc_js(__('Could not load static offers. Try again.', 'leadbox')); ?>', 'error'); return; }
                            if (offers && offers.length) autoAppendStatic(offers);
                            else showNotice('<?php echo esc_js(__('No static offers found for this dealer.', 'leadbox')); ?>', 'info');
                            renderUnifiedPreview();
                        });
                    }
                } else {
                    unifiedSlides = unifiedSlides.filter(function(u) { return u.type !== 'static'; });
                    renderUnifiedPreview();
                }
                updateRefreshDataBtn();
            });

            // Make filter (multi-make dealers) — presentation-only: entries stay in
            // unifiedSlides untouched, so switching back to All Makes restores every
            // slide with its order and settings intact. Save/Preview persist the value.
            $(document).on('change', '#lbx-make-filter', function() {
                makeFilter = String($(this).val() || '').toLowerCase();
                renderUnifiedPreview();
            });

            // Static card: Show/Hide toggle.
            $(document).on('change', '#lbx-oem-preview .lbx-static-vis-check', function() {
                var $card = $(this).closest('.lbx-oem-preview__card');
                var id = String($card.data('static-key'));
                var vis = $(this).is(':checked');
                $card.toggleClass('lbx-oem-preview__card--hidden', !vis);
                if (!staticSettings[id]) staticSettings[id] = {};
                staticSettings[id].visible = vis;
            });

            // Static card: remove from THIS slider (doesn't touch Minerva).
            $(document).on('click', '#lbx-oem-preview .lbx-static-card-remove', function(e) {
                e.stopPropagation();
                var id = String($(this).closest('.lbx-oem-preview__card').data('static-key'));
                if (!id) return;
                unifiedSlides = unifiedSlides.filter(function(u) { return !(u.type === 'static' && u.key === id); });
                renderUnifiedPreview();
            });

            // Static card: open the link-override mini-modal.
            $(document).on('click', '#lbx-oem-preview .lbx-static-card-edit', function(e) {
                e.stopPropagation();
                var id = String($(this).closest('.lbx-oem-preview__card').data('static-key'));
                openStaticModal(id);
            });

            function openStaticModal(id) {
                var o = staticOfferData[id] || {};
                var ss = staticSettings[id] || {};
                var $m = $('#lbx-static-modal');
                $m.data('static-id', id);
                $('#lbx-static-modal-title').text(o.title || '<?php echo esc_js(__('OEM Static Offer', 'leadbox')); ?>');
                if (o.image) { $('#lbx-static-modal-thumb').attr('src', o.image).show(); } else { $('#lbx-static-modal-thumb').hide(); }
                // Placeholder shows Minerva's own link; the input holds the override (if any).
                // Pre-fill with the saved override if any, otherwise the offer's own Minerva
                // link so the operator sees the real URL it points to and can tweak it.
                $('#lbx-static-modal-link').val(ss.link_url || o.link_url || '').attr('placeholder', o.link_url || 'https://');
                $('#lbx-static-modal-target').val(ss.link_target || '_self');
                // Disclaimer (read-only) — comes from Minerva; shown only when present.
                if (o.disclaimer && String(o.disclaimer).trim()) {
                    $('#lbx-static-modal-disclaimer').html(o.disclaimer);
                    $('#lbx-static-modal-disclaimer-wrap').show();
                } else {
                    $('#lbx-static-modal-disclaimer').empty();
                    $('#lbx-static-modal-disclaimer-wrap').hide();
                }
                $m.addClass('is-open').attr('aria-hidden', 'false');
                $('body').addClass('lbx-oem-modal-open');
            }
            function closeStaticModal() {
                var $m = $('#lbx-static-modal');
                var id = String($m.data('static-id') || '');
                if (id) {
                    if (!staticSettings[id]) staticSettings[id] = {};
                    var url = $('#lbx-static-modal-link').val().trim();
                    var target = $('#lbx-static-modal-target').val() || '_self';
                    if (url) { staticSettings[id].link_url = url; staticSettings[id].link_target = target; }
                    else { delete staticSettings[id].link_url; delete staticSettings[id].link_target; }
                    renderUnifiedPreview();
                }
                $m.removeClass('is-open').attr('aria-hidden', 'true');
                $('body').removeClass('lbx-oem-modal-open');
            }
            $(document).on('click', '#lbx-static-modal-close, #lbx-static-modal-done', closeStaticModal);

            // ─── Animated Frame Line (PER-SLIDE modal) ──────────────────
            // The modal edits ONE slide's frame_line object. flModalTarget says whose:
            //   {type:'minerva', key}  → slideSettings[key].frame_line
            //   {type:'extra',   id}   → extraSlides[i].frame_line
            //   {type:'static',  id}   → staticSettings[id].frame_line
            // Mini-preview uses the exact frontend technique: two mirrored half-frame
            // paths (pathLength=100) sharing one CSS animation → synced by construction.
            var flModalTarget = null;

            function flReadModal() {
                return {
                    enabled: $('#lbx-fl-enabled').is(':checked'),
                    style:   $('#lbx-fl-style').val() === 'draw' ? 'draw' : 'travel',
                    speed:   parseFloat($('#lbx-fl-speed').val()) || 9,
                    length:  parseInt($('#lbx-fl-length').val(), 10) || 18,
                    width:   parseFloat($('#lbx-fl-width').val()) || 4,
                    color:   $('#lbx-fl-color').val() || '#ffffff',
                    glow:    $('#lbx-fl-glow').is(':checked'),
                    outline: $('#lbx-fl-outline').is(':checked'),
                };
            }
            function flPopulateModal(fl) {
                fl = (fl && typeof fl === 'object') ? fl : {};
                $('#lbx-fl-enabled').prop('checked', !!fl.enabled);
                $('#lbx-fl-style').val(fl.style === 'draw' ? 'draw' : 'travel');
                $('#lbx-fl-speed').val(fl.speed || 9);
                $('#lbx-fl-length').val(fl.length || 18);
                $('#lbx-fl-width').val(fl.width || 4);
                $('#lbx-fl-color').val(fl.color || '#ffffff');
                $('#lbx-fl-glow').prop('checked', !!fl.glow);
                $('#lbx-fl-outline').prop('checked', !!fl.outline);
            }
            function flGetTargetConfig(target) {
                if (!target) return null;
                if (target.type === 'minerva') return (slideSettings[target.key] || {}).frame_line || null;
                if (target.type === 'static')  return (staticSettings[target.id] || {}).frame_line || null;
                if (target.type === 'extra') {
                    var ex = findExtraSlideById(target.id);
                    return (ex && ex.frame_line) || null;
                }
                return null;
            }
            function flWriteTargetConfig(target, fl) {
                if (!target) return;
                var value = fl.enabled ? fl : null; // disabled slides store nothing
                if (target.type === 'minerva') {
                    if (!slideSettings[target.key]) slideSettings[target.key] = {};
                    if (value) slideSettings[target.key].frame_line = value;
                    else delete slideSettings[target.key].frame_line;
                } else if (target.type === 'static') {
                    if (!staticSettings[target.id]) staticSettings[target.id] = {};
                    if (value) staticSettings[target.id].frame_line = value;
                    else delete staticSettings[target.id].frame_line;
                } else if (target.type === 'extra') {
                    var ex = findExtraSlideById(target.id);
                    if (ex) {
                        if (value) ex.frame_line = value;
                        else delete ex.frame_line;
                    }
                }
            }
            function openFlModal(target) {
                flModalTarget = target;
                flPopulateModal(flGetTargetConfig(target));
                $('#lbx-fl-modal').addClass('is-open').attr('aria-hidden', 'false');
                $('body').addClass('lbx-oem-modal-open');
                flDrawPreview();
                flSyncPreview();
            }

            function flDrawPreview() {
                var box = document.getElementById('lbx-fl-preview');
                if (!box) return;
                var w = box.clientWidth, h = box.clientHeight;
                if (!w || !h) return;
                var ix = Math.max(10, w * 0.014), iy = Math.max(12, h * 0.085);
                var cx = w / 2;
                var ps = box.querySelectorAll('path');
                ps[0].setAttribute('d', 'M' + cx + ' ' + iy + ' H' + (w - ix) + ' V' + (h - iy) + ' H' + cx);
                ps[1].setAttribute('d', 'M' + cx + ' ' + iy + ' H' + ix + ' V' + (h - iy) + ' H' + cx);
            }
            function flSyncPreview() {
                var box = document.getElementById('lbx-fl-preview');
                if (!box) return;
                var style = $('#lbx-fl-style').val() === 'draw' ? 'draw' : 'travel';
                var speed = parseFloat($('#lbx-fl-speed').val()) || 9;
                var len   = parseInt($('#lbx-fl-length').val(), 10) || 18;
                var width = parseFloat($('#lbx-fl-width').val()) || 4;
                var color = $('#lbx-fl-color').val() || '#ffffff';
                box.style.setProperty('--flp-speed', speed + 's');
                box.style.setProperty('--flp-width', width + 'px');
                box.style.setProperty('--flp-color', color);
                $(box).toggleClass('lbx-fl-preview--glow', $('#lbx-fl-glow').is(':checked'));
                $(box).toggleClass('lbx-fl-preview--outline', $('#lbx-fl-outline').is(':checked'));
                $(box).toggleClass('lbx-fl-preview--draw', style === 'draw');
                // Line length only applies to the traveling dash; draw uses the full run.
                $('#lbx-fl-length-field').toggle(style !== 'draw');
                $(box).find('path').attr('stroke-dasharray', style === 'draw' ? '100 100' : (len + ' ' + (100 - len)));
                // Restart the paths' CSS animation so a draw-in replays for evaluation.
                $(box).find('path').each(function() {
                    this.style.animation = 'none';
                    void this.offsetWidth; // reflow — commits the 'none'
                    this.style.animation = '';
                });
                $('#lbx-fl-speed-out').text(speed + 's');
                $('#lbx-fl-length-out').text(len + '%');
                $('#lbx-fl-width-out').text(width + 'px');
            }
            // Open from each slide editor (the FL modal stacks above them).
            $(document).on('click', '#lbx-oem-fl-configure', function() {
                if (!currentEditKey) return;
                openFlModal({ type: 'minerva', key: currentEditKey });
            });
            $(document).on('click', '#lbx-static-fl-configure', function() {
                var id = String($('#lbx-static-modal').data('static-id') || '');
                if (id) openFlModal({ type: 'static', id: id });
            });
            $(document).on('click', '.lbx-ex-fl-configure', function() {
                var id = String($(this).data('extra-id') || '');
                if (id) openFlModal({ type: 'extra', id: id });
            });

            function closeFlModal() {
                if (flModalTarget) {
                    flWriteTargetConfig(flModalTarget, flReadModal());
                    flModalTarget = null;
                    renderUnifiedPreview(); // refresh the card badges
                }
                $('#lbx-fl-modal').removeClass('is-open').attr('aria-hidden', 'true');
                // The FL modal stacks over the slide editors. Keep the body scroll-lock
                // ONLY while a modal that MANAGES the lock (adds AND removes it on its
                // own close) is still open underneath — i.e. the OEM and Static modals.
                // The Additional editor (#lbx-ex-modal) never touches the lock, so
                // counting it here would strand the class on <body> and kill scrolling.
                if (!$('#lbx-oem-modal.is-open, #lbx-static-modal.is-open').length) {
                    $('body').removeClass('lbx-oem-modal-open');
                }
            }
            $(document).on('click', '#lbx-fl-modal-close, #lbx-fl-modal-done', closeFlModal);
            $(document).on('input change', '#lbx-fl-style, #lbx-fl-speed, #lbx-fl-length, #lbx-fl-width, #lbx-fl-color, #lbx-fl-glow, #lbx-fl-outline', flSyncPreview);
            $(document).on('click', '#lbx-fl-modal .lbx-fl-swatch', function() {
                $('#lbx-fl-color').val($(this).data('c'));
                flSyncPreview();
            });
            $(document).on('click', '#lbx-static-modal', function(e) { if (e.target === this) closeStaticModal(); });

            // Remove from the editor's Done bar (legacy button kept in the modal header).
            // Switch the additional-slide editor between EN and FR (LBT-1489). Persist what's
            // in the current language, flip the active language, then re-render the whole card
            // so media/images/text/CTAs/disclaimer all reflect the selected language.
            $(document).on('click', '.lbx-ex-langtab', function() {
                var lang = $(this).attr('data-lang') === 'fr' ? 'fr' : 'en';
                if (lang === extraEditLang) return;
                syncExtraData(activeExtraSlide);
                extraEditLang = lang;
                renderExtraSlides();
            });

            $(document).on('click', '.lbx-ex-remove', function() {
                lbxConfirm({
                    title: '<?php echo esc_js(__('Remove this slide?', 'leadbox')); ?>',
                    message: '<?php echo esc_js(__('This additional slide will be removed from the slider. The change applies after saving.', 'leadbox')); ?>',
                    confirmText: '<?php echo esc_js(__('Remove slide', 'leadbox')); ?>',
                    cancelText: '<?php echo esc_js(__('Keep', 'leadbox')); ?>',
                    danger: true
                }).then(function(ok) {
                    if (!ok) return;
                    extraSlides.splice(activeExtraSlide, 1);
                    activeExtraSlide = -1; // Collapse editor; user must pick the next slide.
                    renderExtraSlides();
                });
            });

            // Pick image
            $(document).on('click', '#lbx-extra-editor .lbx-pick-extra-image', function() {
                var frame = wp.media({ title: '<?php echo esc_js(__('Select Slide Image', 'leadbox')); ?>', multiple: false, library: { type: 'image' } });
                frame.on('select', function() {
                    var att = frame.state().get('selection').first().toJSON();
                    syncExtraData(activeExtraSlide);
                    var sfx = extraEditLang === 'fr' ? '_fr' : '';
                    extraSlides[activeExtraSlide]['image_id' + sfx] = att.id;
                    extraSlides[activeExtraSlide]['image_url' + sfx] = att.url || '';
                    renderExtraSlides();
                });
                frame.open();
            });

            // Pick mobile image (extra slide)
            $(document).on('click', '#lbx-extra-editor .lbx-ex-pick-mobile-image', function(e) {
                e.preventDefault();
                var frame = wp.media({ title: '<?php echo esc_js(__('Select Mobile Image', 'leadbox')); ?>', multiple: false, library: { type: 'image' } });
                frame.on('select', function() {
                    var att = frame.state().get('selection').first().toJSON();
                    syncExtraData(activeExtraSlide);
                    var sfx = extraEditLang === 'fr' ? '_fr' : '';
                    extraSlides[activeExtraSlide]['image_id_mobile' + sfx] = att.id;
                    extraSlides[activeExtraSlide]['image_url_mobile' + sfx] = att.url || '';
                    renderExtraSlides();
                });
                frame.open();
            });
            // Clear mobile image (extra slide)
            $(document).on('click', '#lbx-extra-editor .lbx-ex-clear-mobile-image', function() {
                syncExtraData(activeExtraSlide);
                var sfx = extraEditLang === 'fr' ? '_fr' : '';
                extraSlides[activeExtraSlide]['image_id_mobile' + sfx] = 0;
                extraSlides[activeExtraSlide]['image_url_mobile' + sfx] = '';
                renderExtraSlides();
            });

            // Toggle media type
            $(document).on('change', '#lbx-extra-editor .lbx-ex-media-type', function() {
                syncExtraData(activeExtraSlide);
                renderExtraSlides();
            });

            // Pick video
            $(document).on('click', '#lbx-extra-editor .lbx-pick-extra-video', function() {
                var frame = wp.media({ title: '<?php echo esc_js(__('Select Slide Video', 'leadbox')); ?>', multiple: false, library: { type: 'video' } });
                frame.on('select', function() {
                    var att = frame.state().get('selection').first().toJSON();
                    syncExtraData(activeExtraSlide);
                    var sfx = extraEditLang === 'fr' ? '_fr' : '';
                    extraSlides[activeExtraSlide].video_id = att.id || 0;
                    extraSlides[activeExtraSlide]['video_url' + sfx] = att.url || '';
                    extraSlides[activeExtraSlide]['media_type' + sfx] = 'video';
                    renderExtraSlides();
                });
                frame.open();
            });

            // Clear video
            $(document).on('click', '#lbx-extra-editor .lbx-clear-extra-video', function() {
                $('#lbx-extra-editor .lbx-ex-video-url').val('');
                syncExtraData(activeExtraSlide);
            });

            // ─── Quick-format toolbar (B / I / U) for HTML-enabled text fields ──
            // Floats inside the field (top-right). Wraps the current selection with
            // the tag — or inserts an empty pair and leaves the caret inside it.
            function lbxRteToolsHtml() {
                return '<div class="lbx-rte-tools" role="toolbar" aria-label="<?php echo esc_attr__('Text formatting', 'leadbox'); ?>">' +
                    '<button type="button" class="lbx-rte-btn" data-tag="strong" title="<?php echo esc_attr__('Bold', 'leadbox'); ?>"><strong>B</strong></button>' +
                    '<button type="button" class="lbx-rte-btn" data-tag="em" title="<?php echo esc_attr__('Italic', 'leadbox'); ?>"><em>I</em></button>' +
                    '<button type="button" class="lbx-rte-btn" data-tag="u" title="<?php echo esc_attr__('Underline', 'leadbox'); ?>"><u>U</u></button>' +
                '</div>';
            }
            $(document).on('click', '.lbx-rte-btn', function() {
                var ta = $(this).closest('.lbx-rte-wrap').find('textarea')[0];
                if (!ta) return;
                var tag = String($(this).data('tag'));
                var open = '<' + tag + '>', close = '</' + tag + '>';
                var start = ta.selectionStart, end = ta.selectionEnd;
                var val = ta.value, sel = val.slice(start, end);
                ta.value = val.slice(0, start) + open + sel + close + val.slice(end);
                ta.focus();
                // With a selection: caret after the closing tag. Without: inside the pair.
                var pos = sel ? start + open.length + sel.length + close.length : start + open.length;
                ta.setSelectionRange(pos, pos);
                $(ta).trigger('input'); // run the live-sync handlers
            });

            // Live title update — keeps the unified preview card's title in sync while typing.
            $(document).on('input', '#lbx-extra-editor .lbx-ex-title', function() {
                if (activeExtraSlide < 0 || activeExtraSlide >= extraSlides.length) return;
                extraSlides[activeExtraSlide] = normalizeExtraSlide(extraSlides[activeExtraSlide]);
                // LBT-1489 — write into the active language slot, NOT always EN. Without the
                // suffix, typing in the FR tab overwrote the EN title key letter by letter.
                var sfx = extraEditLang === 'fr' ? '_fr' : '';
                extraSlides[activeExtraSlide]['title' + sfx] = $(this).val();
                // Card label uses the EN/base title as the slide's stable name in the list.
                // Strip HTML — titles may carry spans/<br>; cards show plain text.
                var t = String(extraSlides[activeExtraSlide].title || '').replace(/<[^>]*>/g, '').trim() || '<?php echo esc_js(__('Untitled Slide', 'leadbox')); ?>';
                var id = extraSlides[activeExtraSlide].id;
                $('#lbx-oem-preview .lbx-oem-preview__card[data-extra-id="' + id + '"] .lbx-oem-preview__card-title').text(t);
            });

            // Video URL live preview
            $(document).on('input', '#lbx-extra-editor .lbx-ex-video-url', function() {
                var url = $(this).val().trim();
                var $img = $('#lbx-extra-editor .lbx-slide-image');
                if (!url) {
                    $img.html('<div class="lbx-slide-image-ph"><span class="dashicons dashicons-format-video"></span><span><?php echo esc_js(__('Video slide', 'leadbox')); ?></span></div>');
                    return;
                }
                $img.html('<div class="lbx-video-preview"><video src="' + esc(url) + '#t=0.1" preload="metadata" muted playsinline></video><div class="lbx-video-preview-loader"><span class="spinner is-active"></span></div></div>');
                bindVideoPreviewEvents($img);
            });

            // Add CTA — extra slides
            $(document).on('click', '#lbx-extra-editor .lbx-cta-add-btn', function() {
                syncExtraData(activeExtraSlide);
                var slide = extraSlides[activeExtraSlide];
                if (!Array.isArray(slide.ctas)) slide.ctas = [];
                slide.ctas.push({ label: '', url: '', target: '_self', color: '', position: 'inline' });
                renderExtraSlides();
            });
            // Remove CTA — extra slides
            $(document).on('click', '#lbx-extra-editor .lbx-cta-remove-btn', function() {
                var idx = parseInt($(this).closest('.lbx-cta-row').data('index'), 10);
                syncExtraData(activeExtraSlide);
                var slide = extraSlides[activeExtraSlide];
                if (Array.isArray(slide.ctas) && slide.ctas.length > 1) {
                    slide.ctas.splice(idx, 1);
                } else {
                    slide.ctas = [{ label: '', url: '', target: '_self', color: '', position: 'inline' }];
                }
                renderExtraSlides();
            });

            // Defer initial render to document ready so jQuery UI Sortable is loaded.
            $(function() { renderExtraSlides(); });

            // ─── Unified Slides Preview (OEM + Additional) ─────────────────
            // Fetches Minerva offers, then renders BOTH OEM offers and additional slides
            // (extras) as a single sortable grid. The order comes from `unifiedSlides`,
            // which is backfilled from legacy `minervaOrder + extra positions` when empty.
            var oemPreviewLoaded = false;  // Set true once the AJAX has been initiated.
            var oemPreviewReady = false;   // Set true once the AJAX has resolved (success OR error).
            var lastOffersList = [];       // Cached so re-renders after extras edits don't re-hit the API.

            // Busy overlay shown over the slides grid while OEM/Static offers are being fetched.
            function showPreviewBusy(msg) {
                $('#lbx-oem-preview-busy .lbx-busy-text').text(msg || '<?php echo esc_js(__('Loading offers…', 'leadbox')); ?>');
                $('#lbx-oem-preview-busy').addClass('is-on');
            }
            function hidePreviewBusy() { $('#lbx-oem-preview-busy').removeClass('is-on'); }

            function loadOemPreview() {
                if (oemPreviewLoaded) {
                    if (oemPreviewReady) renderUnifiedPreview();
                    return;
                }
                oemPreviewLoaded = true;
                var $preview = $('#lbx-oem-preview');
                $preview.html('<div class="lbx-oem-preview__loading"><span class="spinner is-active" style="float:none;margin:0 8px 0 0;"></span> <?php echo esc_js(__('Loading slides...', 'leadbox')); ?></div>');
                $.ajax({
                    url: '<?php echo rest_url('lbx-slider/v1/minerva-preview'); ?>',
                    method: 'GET',
                    beforeSend: function(xhr) {
                        xhr.setRequestHeader('X-WP-Nonce', '<?php echo wp_create_nonce('wp_rest'); ?>');
                    },
                    success: function(data) {
                        lastOffersList = (data && data.offers) ? data.offers : [];
                        oemPreviewReady = true;
                        // Cache offer data for the OEM edit modal lookups.
                        oemOfferData = {};
                        lastOffersList.forEach(function(o) {
                            var key = offerKey(o);
                            var title = [o.year, o.make, o.model].filter(Boolean).join(' ');
                            oemOfferData[key] = {
                                title: title, sub: o.trim || '', img: o.image || '',
                                financeApr: o.financeApr || '', financeLabel: o.financeLabel || '', financeLabelFr: o.financeLabelFr || '',
                                leaseApr: o.leaseApr || '', leaseLabel: o.leaseLabel || '', leaseLabelFr: o.leaseLabelFr || '',
                                paymentAmount: o.paymentAmount || '', paymentLabel: o.paymentLabel || '', paymentLabelFr: o.paymentLabelFr || '',
                                downPayment: o.downPayment || '', downPaymentLabel: o.downPaymentLabel || '', downPaymentLabelFr: o.downPaymentLabelFr || '',
                                normal1: o.normal1 || '', normal2: o.normal2 || '',
                                normal1Fr: o.normal1Fr || '', normal2Fr: o.normal2Fr || '',
                                finance: o.finance || '', lease: o.lease || '', payment: o.payment || '',
                                legalEn: o.legalEn || '', legalFr: o.legalFr || '',
                                shopNowUrl: o.shopNowUrl || '', shopNowUrlFr: o.shopNowUrlFr || ''
                            };
                        });
                        // Sync the unified list with what we just got from Minerva + the current extras.
                        unifiedSlides = syncUnifiedSlides(lastOffersList, extraSlides);
                        hidePreviewBusy();
                        renderUnifiedPreview();
                        // If saved static offers exist, fetch them too so their cards render.
                        loadSavedStaticOffers();
                    },
                    error: function() {
                        oemPreviewReady = true; // Failure is still a resolved state — let extras edits render.
                        hidePreviewBusy();
                        $('#lbx-oem-count').text('!');
                        $preview.html('<div class="lbx-oem-preview__empty" style="color:var(--lbx-danger);"><?php echo esc_js(__('Failed to load OEM offers.', 'leadbox')); ?></div>');
                        loadSavedStaticOffers();
                    }
                });
            }

            // Header "Refresh data" button: clears the server-side Minerva (+ static) cache so
            // the next preview AND the live front-end pull FRESH feed data, then re-fetches the
            // editor's offer list in place. Per-slide custom edits live in the saved slider
            // options and are re-applied on top of the feed — so a refresh never overwrites them.
            $(document).on('click', '#lbx-refresh-minerva', function() {
                var $b = $(this).prop('disabled', true);
                $b.find('.dashicons').addClass('lbx-spin');
                $.post(ajaxurl, { action: 'lbx_slider_refresh_minerva', nonce: nonce })
                    .always(function() {
                        oemPreviewLoaded = false;
                        oemPreviewReady = false;
                        if (typeof loadOemPreview === 'function') loadOemPreview();
                        if (typeof loadSavedStaticOffers === 'function') loadSavedStaticOffers();
                        $b.prop('disabled', false).find('.dashicons').removeClass('lbx-spin');
                    });
            });

            // Reconcile unifiedSlides with the current Minerva offers and extras lists.
            // - If unifiedSlides is empty AND there's legacy data → bootstrap from legacy
            // - Drop entries whose target no longer exists (offer removed upstream, extra deleted)
            // - Append entries for any new offers/extras not yet in the order
            //
            // IMPORTANT: when `offers` is empty we treat the Minerva side as "unknown",
            // not as "no offers exist". Otherwise an early call (DOM ready before the
            // Minerva AJAX returns) would drop every minerva entry from unifiedSlides
            // and the next sync would re-append them at the end — silently reshuffling
            // the saved order so extras end up first.
            function syncUnifiedSlides(offers, extras) {
                var offersAvailable = Array.isArray(offers) && offers.length > 0;
                var minervaKeys = {};
                if (offersAvailable) {
                    offers.forEach(function(o) { minervaKeys[offerKey(o)] = true; });
                }
                var extraIds = {};
                extras.forEach(function(e) { if (e && e.id) extraIds[e.id] = true; });

                var existing = (Array.isArray(unifiedSlides) ? unifiedSlides : []).filter(function(u) {
                    if (!u || !u.type) return false;
                    // OEM switch OFF: OEM offers are never shown (they only exist via the switch).
                    if (u.type === 'minerva') {
                        if (!autoPopulateOem) return false;
                        return offersAvailable ? !!minervaKeys[u.key] : true;
                    }
                    if (u.type === 'extra')   return !!extraIds[u.id];
                    // Keep static entries until the static list has loaded (then validate).
                    if (u.type === 'static')  return staticOffersReady ? !!staticOfferData[u.key] : true;
                    return false;
                });

                // First-run bootstrap from legacy ordering when there's nothing usable yet.
                if (!existing.length && (minervaOrder.length > 0 || extras.length > 0 || offersAvailable)) {
                    existing = buildUnifiedFromLegacy(offers || [], minervaOrder, extras);
                }

                var inUnifiedMinerva = {};
                var inUnifiedExtra = {};
                existing.forEach(function(u) {
                    if (u.type === 'minerva') inUnifiedMinerva[u.key] = true;
                    if (u.type === 'extra')   inUnifiedExtra[u.id]  = true;
                });

                // Append newcomer OEM offers ONLY when the OEM switch is ON (and we have data).
                if (autoPopulateOem && offersAvailable) {
                    offers.forEach(function(o) {
                        var k = offerKey(o);
                        if (!inUnifiedMinerva[k]) existing.push({ type: 'minerva', key: k });
                    });
                }
                extras.forEach(function(e) {
                    if (e && e.id && !inUnifiedExtra[e.id]) existing.push({ type: 'extra', id: e.id });
                });
                // Static offers are appended by autoAppendStatic() (auto-populate) or the
                // "Add Static Offers" button (manual) — not here.
                return existing;
            }

            // Render the unified preview grid from the current unifiedSlides + offers/extras.
            // True when a slide's make(s) pass the "Show Only {Make} Slides" filter.
            // Accepts a string (OEM offers) or an array (static offers can target several
            // makes). Slides with no make data are dealer-wide promos → always pass.
            function makeFilterMatches(makeOrMakes) {
                if (!makeFilter) return true;
                var list = Array.isArray(makeOrMakes) ? makeOrMakes : (makeOrMakes ? [makeOrMakes] : []);
                if (!list.length) return true;
                for (var i = 0; i < list.length; i++) {
                    if (String(list[i]).toLowerCase().trim() === makeFilter) return true;
                }
                return false;
            }

            function renderUnifiedPreview() {
                var $preview = $('#lbx-oem-preview');
                var $count = $('#lbx-oem-count');

                var offerMap = {};
                lastOffersList.forEach(function(o) { offerMap[offerKey(o)] = o; });
                var extraMap = {};
                extraSlides.forEach(function(e) { if (e && e.id) extraMap[e.id] = e; });

                var hiddenByMake = 0;
                var renderable = unifiedSlides.filter(function(u) {
                    if (u.type === 'minerva') {
                        if (!(autoPopulateOem && !!offerMap[u.key])) return false;
                        if (!makeFilterMatches(offerMap[u.key].make)) { hiddenByMake++; return false; }
                        return true;
                    }
                    if (u.type === 'extra')   return !!extraMap[u.id];
                    if (u.type === 'static')  {
                        var so = staticOfferData[u.key];
                        if (!so) return false;
                        if (!makeFilterMatches(so.makes || so.make)) { hiddenByMake++; return false; }
                        return true;
                    }
                    return false;
                });
                $count.text(renderable.length);

                // Surface how many slides the make filter is hiding right now.
                var $mfNote = $('#lbx-make-filter-note');
                if (makeFilter && hiddenByMake > 0) {
                    $mfNote.text('<?php echo esc_js(__('%d hidden by make filter', 'leadbox')); ?>'.replace('%d', hiddenByMake)).show();
                } else {
                    $mfNote.hide();
                }

                if (!renderable.length) {
                    var emptyMsg = (makeFilter && hiddenByMake > 0)
                        ? '<?php echo esc_js(__('All slides are hidden by the make filter. Switch it back to "All Makes" to see them.', 'leadbox')); ?>'
                        : '<?php echo esc_js(__('No slides yet. Click "Add Slide" above to create one.', 'leadbox')); ?>';
                    $preview.html(
                        '<div class="lbx-oem-preview__empty">' +
                            '<span class="dashicons dashicons-format-gallery"></span><br>' +
                            emptyMsg +
                        '</div>'
                    );
                    return;
                }

                var html = '';
                renderable.forEach(function(u) {
                    if (u.type === 'minerva') html += renderMinervaCardHtml(offerMap[u.key]);
                    else if (u.type === 'extra') html += renderExtraCardHtml(extraMap[u.id]);
                    else if (u.type === 'static') html += renderStaticCardHtml(staticOfferData[u.key]);
                });
                $preview.html(html);
                initOemSortable();
            }

            // ─── Per-slide responsive visibility (📱 / 📑 / 🖥) ─────────────
            // Each card carries three device toggles. hidden_on is the list of breakpoint
            // buckets where the slide is HIDDEN ([] = visible everywhere). Stored per slide
            // in slideSettings[key] (OEM), staticSettings[id] (static) or the extra object.
            var LBX_RES_BUCKETS = [
                { k: 'mobile',  icon: 'smartphone', label: '<?php echo esc_js(__('Mobile (≤ 640px)', 'leadbox')); ?>' },
                { k: 'tablet',  icon: 'tablet',     label: '<?php echo esc_js(__('Tablet (641–1023px)', 'leadbox')); ?>' },
                { k: 'desktop', icon: 'desktop',    label: '<?php echo esc_js(__('Desktop (≥ 1024px)', 'leadbox')); ?>' }
            ];
            function responsiveTogglesHtml(hiddenOn) {
                hiddenOn = Array.isArray(hiddenOn) ? hiddenOn : [];
                var h = '<div class="lbx-oem-preview__res" role="group" aria-label="<?php echo esc_attr__('Show on devices', 'leadbox'); ?>">';
                LBX_RES_BUCKETS.forEach(function(b) {
                    var on = hiddenOn.indexOf(b.k) === -1;
                    h += '<button type="button" class="lbx-res-toggle' + (on ? ' is-on' : '') + '"'
                       + ' data-bucket="' + b.k + '" aria-pressed="' + (on ? 'true' : 'false') + '"'
                       + ' title="' + b.label + '"><span class="dashicons dashicons-' + b.icon + '"></span></button>';
                });
                h += '</div>';
                return h;
            }
            function findExtraSlideById(id) {
                for (var i = 0; i < extraSlides.length; i++) {
                    if (String(extraSlides[i].id) === String(id)) return extraSlides[i];
                }
                return null;
            }
            function getSlideHiddenOn(type, $card) {
                if (type === 'minerva') { var k = String($card.data('slide-key')); return (slideSettings[k] && slideSettings[k].hidden_on) || []; }
                if (type === 'static')  { var s = String($card.data('static-key')); return (staticSettings[s] && staticSettings[s].hidden_on) || []; }
                if (type === 'extra')   { var ex = findExtraSlideById($card.data('extra-id')); return (ex && ex.hidden_on) || []; }
                return [];
            }
            function setSlideHiddenOn(type, $card, arr) {
                if (type === 'minerva') { var k = String($card.data('slide-key')); slideSettings[k] = slideSettings[k] || {}; slideSettings[k].hidden_on = arr; }
                else if (type === 'static') { var s = String($card.data('static-key')); staticSettings[s] = staticSettings[s] || {}; staticSettings[s].hidden_on = arr; }
                else if (type === 'extra') { var ex = findExtraSlideById($card.data('extra-id')); if (ex) ex.hidden_on = arr; } // serialized whole on save
            }
            // Toggle one breakpoint on/off for a card's slide.
            $(document).on('click', '#lbx-oem-preview .lbx-res-toggle', function(e) {
                e.preventDefault();
                e.stopPropagation(); // don't let the click bubble to the card-body (opens the modal)
                var $btn = $(this);
                var $card = $btn.closest('.lbx-oem-preview__card');
                var type = String($card.data('type'));
                var bucket = String($btn.data('bucket'));
                var hidden = getSlideHiddenOn(type, $card).slice();
                var i = hidden.indexOf(bucket);
                var nowVisible;
                if (i === -1) { hidden.push(bucket); nowVisible = false; } // was visible → hide here
                else { hidden.splice(i, 1); nowVisible = true; }           // was hidden → show here
                setSlideHiddenOn(type, $card, hidden);
                $btn.toggleClass('is-on', nowVisible).attr('aria-pressed', nowVisible ? 'true' : 'false');
                // Visually flag a slide that ends up hidden on every breakpoint.
                $card.toggleClass('lbx-oem-preview__card--res-off', hidden.length >= 3);
            });

            // Live-sync the column/visibility range labels (≤640px, etc.) when the admin
            // edits the breakpoint inputs, so "Slides per view" matches the breakpoints above.
            function updateBpRangeLabels() {
                var m = parseInt($('#lbx-bp-mobile').val(), 10) || 640;
                var t = parseInt($('#lbx-bp-tablet').val(), 10) || 1024;
                if (t <= m) t = m + 1; // keep tablet above mobile (mirrors the save-side clamp)
                $('.lbx-bp-range--mobile').text('≤ ' + m + 'px');
                $('.lbx-bp-range--tablet').text('≤ ' + t + 'px');
                $('.lbx-bp-range--desktop').text('> ' + t + 'px');
            }
            $(document).on('input change', '#lbx-bp-mobile, #lbx-bp-tablet', updateBpRangeLabels);

            // Card for a Static Offer — image-only with a Show toggle, link-override edit,
            // and a remove button (static offers are pulled in, so removing just drops them
            // from this slider's list, not from Minerva).
            // Small corner badge for cards whose slide has its own Animated Frame Line.
            function flBadgeHtml(fl) {
                return (fl && fl.enabled)
                    ? '<span class="lbx-fl-badge" title="<?php echo esc_attr__('Animated frame line on this slide', 'leadbox'); ?>">✦</span>'
                    : '';
            }

            function renderStaticCardHtml(o) {
                if (!o) return '';
                var id = String(o.id);
                var ss = staticSettings[id] || {};
                var visible = ss.visible !== false;
                var hiddenOn = ss.hidden_on || [];
                var hiddenCls = (visible ? '' : ' lbx-oem-preview__card--hidden') + (hiddenOn.length >= 3 ? ' lbx-oem-preview__card--res-off' : '');
                var hasLinkOverride = !!ss.link_url;
                var title = o.title || '<?php echo esc_js(__('OEM Static Offer', 'leadbox')); ?>';

                var html = '<div class="lbx-oem-preview__card' + hiddenCls + '" data-type="static" data-static-key="' + esc(id) + '">';
                html += '<span class="lbx-oem-preview__type-badge lbx-oem-preview__type-badge--static"><?php echo esc_js(__('OEM Static', 'leadbox')); ?></span>';
                html += flBadgeHtml(ss.frame_line);
                html += '<div class="lbx-oem-preview__thumb-col">';
                html += '<div class="lbx-oem-preview__overlay">';
                html += '<label class="lbx-oem-preview__vis"><input type="checkbox" class="lbx-static-vis-check"' + (visible ? ' checked' : '') + '><span class="lbx-oem-preview__vis-label"><?php echo esc_js(__('Show', 'leadbox')); ?></span></label>';
                html += '</div>';
                if (o.image) {
                    html += '<img src="' + esc(o.image) + '" alt="' + esc(o.alt || title) + '" loading="lazy">';
                } else {
                    html += '<div class="lbx-oem-preview__card-noimg"><span class="dashicons dashicons-format-image"></span></div>';
                }
                if (hasLinkOverride || o.link_url) {
                    html += '<span class="lbx-oem-preview__override-badge" title="<?php echo esc_attr__('Has link', 'leadbox'); ?>"><span class="dashicons dashicons-admin-links"></span></span>';
                }
                html += '<button type="button" class="lbx-oem-preview__edit-btn lbx-static-card-edit" title="<?php echo esc_js(__('Edit link', 'leadbox')); ?>"><span class="dashicons dashicons-edit"></span></button>';
                html += '<button type="button" class="lbx-oem-preview__remove-btn lbx-static-card-remove" title="<?php echo esc_js(__('Remove from slider', 'leadbox')); ?>"><span class="dashicons dashicons-trash"></span></button>';
                html += '</div>';
                html += '<div class="lbx-oem-preview__card-body">';
                html += '<p class="lbx-oem-preview__card-title">' + esc(title) + '</p>';
                html += responsiveTogglesHtml(hiddenOn);
                html += '</div>';
                html += '</div>';
                return html;
            }

            // ─── Card HTML builders ────────────────────────────────────
            function renderMinervaCardHtml(o) {
                var title = [o.year, o.make, o.model].filter(Boolean).join(' ');
                var sub = o.trim || '';
                var img = o.image || '';
                var key = offerKey(o);
                var ss = slideSettings[key] || {};
                var visible = ss.visible !== false;
                var align = ss.align || '';
                // Minerva's per-offer alignment — surfaced as the inherited "Default" in the
                // selector (Minerva's own default is Left). Stays value="" so it isn't frozen
                // into an admin override unless the operator explicitly picks Left/Center/Right.
                var mAlign = (o.contentAlignment || '').toString().trim().toLowerCase();
                if (mAlign !== 'left' && mAlign !== 'center' && mAlign !== 'right') mAlign = 'left';
                var mAlignLabel = { left: '<?php echo esc_js(__('Left', 'leadbox')); ?>', center: '<?php echo esc_js(__('Center', 'leadbox')); ?>', right: '<?php echo esc_js(__('Right', 'leadbox')); ?>' }[mAlign];
                var hiddenOn = ss.hidden_on || [];
                var hiddenCls = (visible ? '' : ' lbx-oem-preview__card--hidden') + (hiddenOn.length >= 3 ? ' lbx-oem-preview__card--res-off' : '');

                var html = '<div class="lbx-oem-preview__card' + hiddenCls + '" data-type="minerva" data-slide-key="' + esc(key) + '">';
                html += '<span class="lbx-oem-preview__type-badge lbx-oem-preview__type-badge--minerva"><?php echo esc_js(__('OEM', 'leadbox')); ?></span>';
                html += flBadgeHtml(ss.frame_line);
                html += '<div class="lbx-oem-preview__thumb-col">';
                html += '<div class="lbx-oem-preview__overlay">';
                html += '<label class="lbx-oem-preview__vis"><input type="checkbox" class="lbx-oem-vis-check"' + (visible ? ' checked' : '') + '><span class="lbx-oem-preview__vis-label"><?php echo esc_js(__('Show', 'leadbox')); ?></span></label>';
                html += '<select class="lbx-oem-preview__align lbx-oem-align-select">';
                html += '<option value=""' + (!align ? ' selected' : '') + '><?php echo esc_js(__('Auto', 'leadbox')); ?> (' + esc(mAlignLabel) + ')</option>';
                html += '<option value="left"' + (align === 'left' ? ' selected' : '') + '><?php echo esc_js(__('Left', 'leadbox')); ?></option>';
                html += '<option value="center"' + (align === 'center' ? ' selected' : '') + '><?php echo esc_js(__('Center', 'leadbox')); ?></option>';
                html += '<option value="right"' + (align === 'right' ? ' selected' : '') + '><?php echo esc_js(__('Right', 'leadbox')); ?></option>';
                html += '</select>';
                html += '</div>';
                // Media override preview takes priority over the Minerva image when set.
                if (ss.media_type_override === 'image' && ss.image_url_override) {
                    html += '<img src="' + esc(ss.image_url_override) + '" alt="' + esc(title) + '" loading="lazy">';
                    html += '<span class="lbx-oem-preview__override-badge" title="<?php echo esc_attr__('Custom image override', 'leadbox'); ?>"><span class="dashicons dashicons-format-image"></span></span>';
                } else if (ss.media_type_override === 'video' && ss.video_url_override) {
                    html += '<video src="' + esc(ss.video_url_override) + '#t=0.1" preload="metadata" muted playsinline></video>';
                    html += '<span class="lbx-oem-preview__override-badge" title="<?php echo esc_attr__('Custom video override', 'leadbox'); ?>"><span class="dashicons dashicons-format-video"></span></span>';
                } else if (ss.media_type_override === 'embed' && ss.embed_code_override) {
                    html += '<div class="lbx-oem-preview__card-noimg"><span class="dashicons dashicons-editor-code"></span></div>';
                    html += '<span class="lbx-oem-preview__override-badge" title="<?php echo esc_attr__('Custom HTML embed override', 'leadbox'); ?>"><span class="dashicons dashicons-editor-code"></span></span>';
                } else if (img) {
                    html += '<img src="' + esc(img) + '" alt="' + esc(title) + '" loading="lazy">';
                } else {
                    html += '<div class="lbx-oem-preview__card-noimg"><span class="dashicons dashicons-car"></span></div>';
                }
                html += '<button type="button" class="lbx-oem-preview__edit-btn lbx-oem-edit-open" title="<?php echo esc_js(__('Edit slide', 'leadbox')); ?>"><span class="dashicons dashicons-edit"></span></button>';
                html += '</div>';
                html += '<div class="lbx-oem-preview__card-body">';
                html += '<p class="lbx-oem-preview__card-title">' + esc(title) + '</p>';
                if (sub) { html += '<p class="lbx-oem-preview__card-sub">' + esc(sub) + '</p>'; }
                html += responsiveTogglesHtml(hiddenOn);
                html += '</div>';
                html += '</div>';
                return html;
            }

            function renderExtraCardHtml(ex) {
                ex = normalizeExtraSlide(ex);
                var title = String(ex.title || '').replace(/<[^>]*>/g, '').trim() || '<?php echo esc_js(__('Untitled Slide', 'leadbox')); ?>';
                var hiddenOn = ex.hidden_on || [];
                // Bilingual indicator — true when the slide has ANY French content (LBT-1489).
                var hasFr = !!(ex.title_fr || ex.description_fr || ex.image_url_fr || ex.image_url_mobile_fr || ex.video_url_fr || ex.embed_code_fr || (ex.ctas_fr && ex.ctas_fr.length) || ex.disclaimer_fr || ex.image_link_url_fr);
                // Two distinct images → the thumb itself shows the split, so skip the corner badge.
                var hasSplit = ex.media_type !== 'embed' && ex.media_type !== 'video' && ex.image_url && ex.image_url_fr && ex.image_url !== ex.image_url_fr;

                var thumbHtml;
                if (ex.media_type === 'embed') {
                    thumbHtml = '<div class="lbx-oem-preview__card-noimg"><span class="dashicons dashicons-editor-code"></span></div>';
                } else if (ex.media_type === 'video') {
                    thumbHtml = ex.video_url
                        ? '<video src="' + esc(ex.video_url) + '#t=0.1" preload="metadata" muted playsinline></video>'
                        : '<div class="lbx-oem-preview__card-noimg"><span class="dashicons dashicons-format-video"></span></div>';
                } else if (hasSplit) {
                    // Two distinct images (EN + FR): split thumb — left half EN, right half FR.
                    thumbHtml = '<div class="lbx-oem-preview__thumb-split">'
                        + '<span class="lbx-oem-preview__half" style="background-image:url(\'' + esc(ex.image_url) + '\')"><i>EN</i></span>'
                        + '<span class="lbx-oem-preview__half" style="background-image:url(\'' + esc(ex.image_url_fr) + '\')"><i>FR</i></span>'
                        + '</div>';
                } else {
                    thumbHtml = ex.image_url
                        ? '<img src="' + esc(ex.image_url) + '" alt="" loading="lazy">'
                        : '<div class="lbx-oem-preview__card-noimg"><span class="dashicons dashicons-format-image"></span></div>';
                }

                var html = '<div class="lbx-oem-preview__card lbx-oem-preview__card--extra' + (hiddenOn.length >= 3 ? ' lbx-oem-preview__card--res-off' : '') + '" data-type="extra" data-extra-id="' + esc(ex.id) + '">';
                html += '<span class="lbx-oem-preview__type-badge lbx-oem-preview__type-badge--extra"><?php echo esc_js(__('Additional', 'leadbox')); ?></span>';
                html += flBadgeHtml(ex.frame_line);
                html += '<div class="lbx-oem-preview__thumb-col">';
                html += thumbHtml;
                if (hasFr && !hasSplit) html += '<span class="lbx-oem-preview__lang-badge" title="<?php echo esc_attr__('This slide has English & French versions', 'leadbox'); ?>">EN · FR</span>';
                html += '<button type="button" class="lbx-oem-preview__edit-btn lbx-ex-card-edit" title="<?php echo esc_js(__('Edit slide', 'leadbox')); ?>"><span class="dashicons dashicons-edit"></span></button>';
                html += '<button type="button" class="lbx-oem-preview__remove-btn lbx-ex-card-remove" title="<?php echo esc_js(__('Remove slide', 'leadbox')); ?>"><span class="dashicons dashicons-trash"></span></button>';
                html += '</div>';
                html += '<div class="lbx-oem-preview__card-body">';
                html += '<p class="lbx-oem-preview__card-title">' + esc(title) + '</p>';
                html += responsiveTogglesHtml(hiddenOn);
                html += '</div>';
                html += '</div>';
                return html;
            }
            // Per-slide visibility toggle
            $(document).on('change', '.lbx-oem-vis-check', function() {
                var $card = $(this).closest('.lbx-oem-preview__card');
                var key = $card.data('slide-key');
                var vis = $(this).is(':checked');
                $card.toggleClass('lbx-oem-preview__card--hidden', !vis);
                if (!slideSettings[key]) slideSettings[key] = {};
                slideSettings[key].visible = vis;
            });

            // Per-slide alignment
            $(document).on('change', '.lbx-oem-align-select', function() {
                var $card = $(this).closest('.lbx-oem-preview__card');
                var key = $card.data('slide-key');
                if (!slideSettings[key]) slideSettings[key] = {};
                slideSettings[key].align = $(this).val();
            });

            // ─── OEM Edit Modal ─────────────────────────────────────
            // Helper: get override value or fall back to Minerva value
            function ov(ss, field, minervaVal) {
                return (ss[field] !== undefined && ss[field] !== '') ? ss[field] : (minervaVal || '');
            }

            function openOemModal(key) {
                currentEditKey = key;
                var o = oemOfferData[key] || {};
                var ss = slideSettings[key] || {};
                var $m = $('#lbx-oem-modal');

                // Header
                $('#lbx-oem-modal-title').text(o.title || key);
                $('#lbx-oem-modal-sub').text(o.sub || '');
                if (o.img) {
                    $('#lbx-oem-modal-thumb').attr('src', o.img).show();
                } else {
                    $('#lbx-oem-modal-thumb').hide();
                }
                // Reset the language tabs to EN whenever the modal opens. Keeps the entry
                // point predictable across edits — the operator always lands on English.
                $m.find('.lbx-oem-modal__lang-tab').removeClass('is-active').attr('aria-selected', 'false');
                $m.find('.lbx-oem-modal__lang-tab[data-lang="en"]').addClass('is-active').attr('aria-selected', 'true');
                $m.find('.lbx-oem-modal__lang-panel').removeClass('is-active').attr('hidden', '');
                $m.find('.lbx-oem-modal__lang-panel[data-lang="en"]').addClass('is-active').removeAttr('hidden');

                // EN Content — pre-fill with saved override or feed data
                $('#lbx-oem-modal-field-title').val(ov(ss, 'title', o.title));
                $('#lbx-oem-modal-field-description').val(ov(ss, 'description', ''));
                $('#lbx-oem-modal-field-financeLabel').val(ov(ss, 'financeLabel', o.financeLabel));
                $('#lbx-oem-modal-field-financeApr').val(ov(ss, 'financeApr', o.financeApr));
                $('#lbx-oem-modal-field-leaseLabel').val(ov(ss, 'leaseLabel', o.leaseLabel));
                $('#lbx-oem-modal-field-leaseApr').val(ov(ss, 'leaseApr', o.leaseApr));
                $('#lbx-oem-modal-field-paymentLabel').val(ov(ss, 'paymentLabel', o.paymentLabel));
                $('#lbx-oem-modal-field-paymentAmount').val(ov(ss, 'paymentAmount', o.paymentAmount));
                $('#lbx-oem-modal-field-normal1').val(ov(ss, 'normal1', o.normal1));
                $('#lbx-oem-modal-field-normal2').val(ov(ss, 'normal2', o.normal2));
                $('#lbx-oem-modal-field-downPayment').val(ov(ss, 'downPayment', o.downPayment));
                $('#lbx-oem-modal-field-downPaymentLabel').val(ov(ss, 'downPaymentLabel', o.downPaymentLabel));
                // Learn More URL — pre-fill with the saved override or the feed's URL so the
                // operator can SEE which link is currently used and edit it (or clear to revert).
                $('#lbx-oem-modal-field-shopNowUrl').val(ov(ss, 'shopNowUrl', o.shopNowUrl));

                // FR Content
                $('#lbx-oem-modal-field-title_fr').val(ov(ss, 'title_fr', o.title));
                $('#lbx-oem-modal-field-description_fr').val(ov(ss, 'description_fr', ''));
                $('#lbx-oem-modal-field-financeLabelFr').val(ov(ss, 'financeLabelFr', o.financeLabelFr));
                $('#lbx-oem-modal-field-leaseLabelFr').val(ov(ss, 'leaseLabelFr', o.leaseLabelFr));
                $('#lbx-oem-modal-field-paymentLabelFr').val(ov(ss, 'paymentLabelFr', o.paymentLabelFr));
                $('#lbx-oem-modal-field-normal1Fr').val(ov(ss, 'normal1Fr', o.normal1Fr));
                $('#lbx-oem-modal-field-normal2Fr').val(ov(ss, 'normal2Fr', o.normal2Fr));
                $('#lbx-oem-modal-field-downPaymentLabelFr').val(ov(ss, 'downPaymentLabelFr', o.downPaymentLabelFr));
                $('#lbx-oem-modal-field-shopNowUrlFr').val(ov(ss, 'shopNowUrlFr', o.shopNowUrlFr));

                // Disclaimer override — pre-fill with the saved override if any, falling
                // back to the legal text from the feed (so the operator sees what's currently
                // showing and can edit it directly). Note: any save snapshots this text as an
                // override, even if untouched — that's the accepted trade-off for visibility.
                $('#lbx-oem-modal-field-disclaimer_en').val(ov(ss, 'disclaimer_en', o.legalEn));
                $('#lbx-oem-modal-field-disclaimer_fr').val(ov(ss, 'disclaimer_fr', o.legalFr));

                // CTAs — dynamic builder
                var oemCtas = Array.isArray(ss.ctas) && ss.ctas.length ? ss.ctas : (
                    // Migrate legacy cta_label / cta2_label
                    (ss.cta_label || ss.cta_url) ? [
                        { label: ss.cta_label || '', url: ss.cta_url || '', target: ss.cta_target || '_self', color: '', position: 'inline' },
                        (ss.cta2_label || ss.cta2_url) ? { label: ss.cta2_label || '', url: ss.cta2_url || '', target: '_self', color: '', position: 'inline' } : null
                    ].filter(Boolean) :
                    [{ label: '', url: '', target: '_self', color: '', position: 'inline' }]
                );
                $('#lbx-oem-ctas-builder').html(buildCtasUI(oemCtas, 'lbx-oem'));

                // Image fit
                $('#lbx-oem-modal-field-image_fit').val(ss.image_fit || 'cover');

                // Image link
                $('#lbx-oem-modal-field-image_link_url').val(ss.image_link_url || '');
                $('#lbx-oem-modal-field-image_link_target').val(ss.image_link_target || '_self');

                // Media override
                $('#lbx-oem-modal-field-media_type_override').val(ss.media_type_override || '');
                $('#lbx-oem-modal-field-image_url_override').val(ss.image_url_override || '');
                $('#lbx-oem-modal-field-image_url_mobile_override').val(ss.image_url_mobile_override || '');
                $('#lbx-oem-modal-field-video_url_override').val(ss.video_url_override || '');
                $('#lbx-oem-modal-field-embed_code_override').val(ss.embed_code_override || '');
                if (ss.image_url_override) {
                    $('#lbx-oem-modal-image-preview').attr('src', ss.image_url_override).show();
                } else {
                    $('#lbx-oem-modal-image-preview').hide();
                }
                if (ss.image_url_mobile_override) {
                    $('#lbx-oem-modal-image-mobile-preview').attr('src', ss.image_url_mobile_override).show();
                } else {
                    $('#lbx-oem-modal-image-mobile-preview').hide();
                }
                // Show the right wrap based on type
                var mt = ss.media_type_override || '';
                $('#lbx-oem-image-override-wrap').toggle(mt === 'image');
                $('#lbx-oem-video-override-wrap').toggle(mt === 'video');
                $('#lbx-oem-embed-override-wrap').toggle(mt === 'embed');

                // Watermark
                $('#lbx-oem-modal-field-hide_watermark').prop('checked', !!ss.hide_watermark);
                // Hide offer data (per-slide override of the global setting)
                $('#lbx-oem-modal-field-hide_offer_data').prop('checked', !!ss.hide_offer_data);
                // Hide disclaimer icon (per-slide kill switch — bypasses the slider-level Position setting)
                $('#lbx-oem-modal-field-hide_disclaimer').prop('checked', !!ss.hide_disclaimer);
                // Boxed offer text (per-slide override of the global setting)
                $('#lbx-oem-modal-field-boxed_offer_text').prop('checked', !!ss.boxed_offer_text);

                $m.addClass('is-open');
                // Always start at the top — otherwise the modal retains the scroll
                // position from the previous slide edit, which is disorienting.
                // Wait one frame so the modal is fully laid out before resetting.
                var $scroller = $m.find('.lbx-oem-modal');
                requestAnimationFrame(function() { $scroller.scrollTop(0); });
                $('body').addClass('lbx-oem-modal-open');
            }

            function syncOemCtas() {
                if (!currentEditKey) return;
                if (!slideSettings[currentEditKey]) slideSettings[currentEditKey] = {};
                slideSettings[currentEditKey].ctas = readCtasFromDom($('#lbx-oem-ctas-builder'), 'lbx-oem');
            }

            // Force-sync all modal fields into slideSettings[currentEditKey], in case the live
            // change/input handler missed any (e.g. programmatic .val() calls without .trigger()).
            function syncOemModalFields() {
                if (!currentEditKey) return;
                if (!slideSettings[currentEditKey]) slideSettings[currentEditKey] = {};
                $('#lbx-oem-modal .lbx-oem-modal-field').each(function() {
                    var $el = $(this);
                    var field = $el.data('field');
                    if (!field) return;
                    slideSettings[currentEditKey][field] = this.type === 'checkbox' ? $el.is(':checked') : $el.val();
                });
            }

            function closeOemModal() {
                syncOemModalFields();   // ← force-sync ALL fields, not just CTAs
                syncOemCtas();
                var lastKey = currentEditKey;
                currentEditKey = '';
                $('#lbx-oem-modal').removeClass('is-open');
                $('body').removeClass('lbx-oem-modal-open');
                // Update the affected card's thumbnail in place (reflects media override changes).
                if (lastKey) refreshOemCardThumb(lastKey);
            }

            // ─── OEM modal — EN/FR tab switching ─────────────────────────
            // Delegated so it survives re-renders (modal markup is static, but
            // safer than direct binding and consistent with the rest of the file).
            $(document).on('click', '#lbx-oem-modal .lbx-oem-modal__lang-tab', function() {
                var $btn = $(this);
                var lang = $btn.data('lang');
                if (!lang) return;
                var $tabs = $btn.closest('.lbx-oem-modal__lang-tabs');
                $tabs.find('.lbx-oem-modal__lang-tab').removeClass('is-active').attr('aria-selected', 'false');
                $btn.addClass('is-active').attr('aria-selected', 'true');
                var $section = $btn.closest('.lbx-oem-modal__section');
                $section.find('> .lbx-oem-modal__lang-panel').each(function() {
                    var $p = $(this);
                    var match = $p.data('lang') === lang;
                    $p.toggleClass('is-active', match);
                    // Toggle the HTML `hidden` attribute too so screen readers ignore the inactive panel.
                    if (match) $p.removeAttr('hidden'); else $p.attr('hidden', '');
                });
            });

            // Update a single OEM card's thumbnail to reflect current media override (no AJAX).
            function refreshOemCardThumb(key) {
                var $card = $('#lbx-oem-preview > .lbx-oem-preview__card[data-slide-key="' + key + '"]');
                if (!$card.length) return;
                var ss = slideSettings[key] || {};
                var offerData = oemOfferData[key] || {};
                var img = offerData.img || '';
                var title = offerData.title || '';

                // Remove old thumb (img/video/noimg div) and override badge
                $card.find('> .lbx-oem-preview__thumb-col > img, > .lbx-oem-preview__thumb-col > video, > .lbx-oem-preview__thumb-col > .lbx-oem-preview__card-noimg, > .lbx-oem-preview__thumb-col > .lbx-oem-preview__override-badge').remove();

                var $thumbCol = $card.find('> .lbx-oem-preview__thumb-col');
                var $editBtn = $thumbCol.find('> .lbx-oem-preview__edit-btn');
                var thumbHtml = '';
                var badgeHtml = '';
                if (ss.media_type_override === 'image' && ss.image_url_override) {
                    thumbHtml = '<img src="' + esc(ss.image_url_override) + '" alt="' + esc(title) + '" loading="lazy">';
                    badgeHtml = '<span class="lbx-oem-preview__override-badge"><span class="dashicons dashicons-format-image"></span></span>';
                } else if (ss.media_type_override === 'video' && ss.video_url_override) {
                    thumbHtml = '<video src="' + esc(ss.video_url_override) + '#t=0.1" preload="metadata" muted playsinline></video>';
                    badgeHtml = '<span class="lbx-oem-preview__override-badge"><span class="dashicons dashicons-format-video"></span></span>';
                } else if (ss.media_type_override === 'embed' && ss.embed_code_override) {
                    thumbHtml = '<div class="lbx-oem-preview__card-noimg"><span class="dashicons dashicons-editor-code"></span></div>';
                    badgeHtml = '<span class="lbx-oem-preview__override-badge"><span class="dashicons dashicons-editor-code"></span></span>';
                } else if (img) {
                    thumbHtml = '<img src="' + esc(img) + '" alt="' + esc(title) + '" loading="lazy">';
                } else {
                    thumbHtml = '<div class="lbx-oem-preview__card-noimg"><span class="dashicons dashicons-car"></span></div>';
                }
                // Insert before the edit button
                if ($editBtn.length) $editBtn.before(thumbHtml + badgeHtml);
                else $thumbCol.append(thumbHtml + badgeHtml);
            }

            $(document).on('click', '.lbx-oem-edit-open', function(e) {
                e.stopPropagation();
                var key = $(this).closest('.lbx-oem-preview__card').data('slide-key');
                openOemModal(key);
            });

            // Also open modal on card body click
            $(document).on('click', '.lbx-oem-preview__card-body', function() {
                var key = $(this).closest('.lbx-oem-preview__card').data('slide-key');
                openOemModal(key);
            });

            $('#lbx-oem-modal-close, #lbx-oem-modal-done').on('click', closeOemModal);

            // Close on backdrop click
            $('#lbx-oem-modal').on('click', function(e) {
                if (e.target === this) closeOemModal();
            });

            // Close on Escape
            $(document).on('keydown', function(e) {
                if (e.key === 'Escape' && $('#lbx-oem-modal').hasClass('is-open')) closeOemModal();
            });

            // Add CTA — OEM modal
            $(document).on('click', '#lbx-oem-ctas-builder .lbx-cta-add-btn', function() {
                syncOemCtas();
                var ctas = (slideSettings[currentEditKey] || {}).ctas || [];
                ctas.push({ label: '', url: '', target: '_self', color: '', position: 'inline' });
                slideSettings[currentEditKey] = slideSettings[currentEditKey] || {};
                slideSettings[currentEditKey].ctas = ctas;
                $('#lbx-oem-ctas-builder').html(buildCtasUI(ctas, 'lbx-oem'));
            });
            // Remove CTA — OEM modal
            $(document).on('click', '#lbx-oem-ctas-builder .lbx-cta-remove-btn', function() {
                var idx = parseInt($(this).closest('.lbx-cta-row').data('index'), 10);
                syncOemCtas();
                var ctas = (slideSettings[currentEditKey] || {}).ctas || [];
                if (ctas.length > 1) { ctas.splice(idx, 1); } else { ctas = [{ label: '', url: '', target: '_self', color: '', position: 'inline' }]; }
                slideSettings[currentEditKey].ctas = ctas;
                $('#lbx-oem-ctas-builder').html(buildCtasUI(ctas, 'lbx-oem'));
            });

            // Live save fields to slideSettings
            $(document).on('input change', '.lbx-oem-modal-field', function() {
                if (!currentEditKey) return;
                var field = $(this).data('field');
                if (!slideSettings[currentEditKey]) slideSettings[currentEditKey] = {};
                slideSettings[currentEditKey][field] = this.type === 'checkbox' ? $(this).is(':checked') : $(this).val();
            });

            // Media Override: toggle wraps when type changes
            $(document).on('change', '#lbx-oem-modal-field-media_type_override', function() {
                var v = $(this).val();
                $('#lbx-oem-image-override-wrap').toggle(v === 'image');
                $('#lbx-oem-video-override-wrap').toggle(v === 'video');
                $('#lbx-oem-embed-override-wrap').toggle(v === 'embed');
            });

            // Media Override: image preview live update
            $(document).on('input', '#lbx-oem-modal-field-image_url_override', function() {
                var v = $(this).val();
                if (v) $('#lbx-oem-modal-image-preview').attr('src', v).show();
                else $('#lbx-oem-modal-image-preview').hide();
            });

            // Media Override: image picker
            $(document).on('click', '#lbx-oem-modal-image-upload', function(e) {
                e.preventDefault();
                var frame = wp.media({ title: '<?php echo esc_js(__('Select Image', 'leadbox')); ?>', multiple: false, library: { type: 'image' } });
                frame.on('select', function() {
                    var att = frame.state().get('selection').first().toJSON();
                    var url = att.url;
                    $('#lbx-oem-modal-field-image_url_override').val(url).trigger('input');
                    if (currentEditKey) {
                        if (!slideSettings[currentEditKey]) slideSettings[currentEditKey] = {};
                        slideSettings[currentEditKey].image_url_override = url;
                    }
                });
                frame.open();
            });

            // Media Override: mobile image preview live update + picker
            $(document).on('input', '#lbx-oem-modal-field-image_url_mobile_override', function() {
                var v = $(this).val();
                if (v) $('#lbx-oem-modal-image-mobile-preview').attr('src', v).show();
                else $('#lbx-oem-modal-image-mobile-preview').hide();
            });
            $(document).on('click', '#lbx-oem-modal-image-mobile-upload', function(e) {
                e.preventDefault();
                var frame = wp.media({ title: '<?php echo esc_js(__('Select Mobile Image', 'leadbox')); ?>', multiple: false, library: { type: 'image' } });
                frame.on('select', function() {
                    var att = frame.state().get('selection').first().toJSON();
                    var url = att.url;
                    $('#lbx-oem-modal-field-image_url_mobile_override').val(url).trigger('input');
                    if (currentEditKey) {
                        if (!slideSettings[currentEditKey]) slideSettings[currentEditKey] = {};
                        slideSettings[currentEditKey].image_url_mobile_override = url;
                    }
                });
                frame.open();
            });

            // Media Override: video picker
            $(document).on('click', '#lbx-oem-modal-video-upload', function(e) {
                e.preventDefault();
                var frame = wp.media({ title: '<?php echo esc_js(__('Select Video', 'leadbox')); ?>', multiple: false, library: { type: 'video' } });
                frame.on('select', function() {
                    var att = frame.state().get('selection').first().toJSON();
                    var url = att.url;
                    $('#lbx-oem-modal-field-video_url_override').val(url).trigger('input');
                    if (currentEditKey) {
                        if (!slideSettings[currentEditKey]) slideSettings[currentEditKey] = {};
                        slideSettings[currentEditKey].video_url_override = url;
                    }
                });
                frame.open();
            });

            // Auto-load if source is minerva on page load
            if ($('input[name="slider_source"]:checked').val() === 'minerva') {
                loadOemPreview();
            }

            // ─── Countdown Date Live Validation ──────────────────────
            function validateCountdownDates() {
                var startVal = $('#lbx-cd-start-date').val();
                var endVal   = $('#lbx-cd-end-date').val();
                var $startWrap = $('#lbx-cd-start-wrap');
                var $endWrap   = $('#lbx-cd-end-wrap');
                var $startErr  = $('#lbx-cd-start-error');
                var $endErr    = $('#lbx-cd-end-error');

                // Reset
                $startWrap.removeClass('has-error');
                $endWrap.removeClass('has-error');
                $startErr.hide().text('');
                $endErr.hide().text('');

                var now = Date.now();
                var endTs = endVal ? new Date(endVal).getTime() : null;
                var startTs = startVal ? new Date(startVal).getTime() : null;

                // End date validations
                if (endVal && isNaN(endTs)) {
                    $endWrap.addClass('has-error');
                    $endErr.text('<?php echo esc_js(__('Invalid date format.', 'leadbox')); ?>').show();
                } else if (endTs && endTs <= now) {
                    $endWrap.addClass('has-error');
                    $endErr.text('<?php echo esc_js(__('End date must be in the future.', 'leadbox')); ?>').show();
                }

                // Start date validations
                if (startVal && isNaN(startTs)) {
                    $startWrap.addClass('has-error');
                    $startErr.text('<?php echo esc_js(__('Invalid date format.', 'leadbox')); ?>').show();
                } else if (startTs && endTs && startTs >= endTs) {
                    $startWrap.addClass('has-error');
                    $startErr.text('<?php echo esc_js(__('Start date must be before end date.', 'leadbox')); ?>').show();
                }
            }

            $('#lbx-cd-start-date, #lbx-cd-end-date').on('change input', validateCountdownDates);

            // ─── Collect form data ────────────────────────────────────
            // Single source of truth for the slider config payload. Used by both Save
            // (persists) and Preview (stashes to a transient). Pure data gathering — no
            // validation, no early returns — so the preview can render even incomplete
            // sliders. The caller sets `action` + `nonce`.
            function collectSliderFormData() {
                var name = $('#new-slider-name').val().trim();
                var source = $('input[name="slider_source"]:checked').val() || 'minerva';
                var postData = {
                    nonce: nonce,
                    slider_id: $('#edit-slider-id').val(),
                    slider_name: name || '<?php echo esc_js(__('Untitled Slider', 'leadbox')); ?>',
                    slider_source: source,
                    slider_enabled: $('#new-slider-enabled').is(':checked') ? 1 : 0,
                    // Layout & Design
                    slider_layout: $('input[name="slider_layout"]:checked').val() || 'carousel',
                    slider_transition: $('#lbx-transition').val() || 'slide',
                    slider_content_align: $('input[name="slider_content_align"]:checked').val() || 'left',
                    slider_height: $('#lbx-slider-height').val() || '',
                    slider_image_fit: $('#lbx-slider-image-fit').val() || '',
                    slider_hero_intro_animation: $('#lbx-hero-intro-animation').val() || 'none',
                    show_view_inventory: $('#lbx-show-inventory').is(':checked') ? 1 : 0,
                    hide_offer_data: $('#lbx-hide-offer-data').is(':checked') ? 1 : 0,
                    boxed_offer_text: $('#lbx-boxed-offer-text').is(':checked') ? 1 : 0,
                    gradient_color: $('#lbx-gradient-hex').val() || '',
                    slider_autoplay: $('#lbx-slider-autoplay').is(':checked') ? 1 : 0,
                    slider_autoplay_speed: parseInt($('#lbx-slider-autoplay-speed').val(), 10) || 5000,
                    slider_loop: $('#lbx-slider-loop').is(':checked') ? 1 : 0,
                    slider_show_arrows: $('#lbx-slider-show-arrows').is(':checked') ? 1 : 0,
                    slider_arrow_style: $('#lbx-slider-arrow-style').val() || 'circle',
                    slider_arrow_color: $('#lbx-slider-arrow-color').val() || '',
                    slider_arrow_opacity: parseInt($('#lbx-slider-arrow-opacity').val(), 10) || 90,
                    slider_arrow_visibility: $('#lbx-slider-arrow-visibility').val() || 'always',
                    slider_show_pagination: $('#lbx-slider-show-pagination').is(':checked') ? 1 : 0,
                    slider_pagination_color: $('#lbx-slider-pagination-color').val() || '',
                    slider_show_overlay: $('#lbx-slider-show-overlay').is(':checked') ? 1 : 0,
                    slider_custom_css: (function() {
                        // If the textarea only contains the pre-filled scaffold (empty rule), save as empty
                        // so the frontend doesn't inject a useless <style> tag.
                        var $ta = $('#lbx-slider-custom-css');
                        var v = $ta.val() || '';
                        var scope = $ta.data('scope-class') || '';
                        if (scope) {
                            // Strip whitespace and check if it's just "SCOPE { /* ... */ }"
                            var cleaned = v.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\s+/g, '').toLowerCase();
                            var scaffold = (scope + '{}').toLowerCase().replace(/\s+/g, '');
                            if (cleaned === scaffold) return '';
                        }
                        return v;
                    })(),
                    media_columns_mobile: $('#lbx-mc-mobile').val() || 1,
                    media_columns_tablet: $('#lbx-mc-tablet').val() || 2,
                    media_columns_desktop: $('#lbx-mc-desktop').val() || 3,
                    carousel_columns_mobile:  $('#lbx-cc-mobile').val() || 1,
                    carousel_columns_tablet:  $('#lbx-cc-tablet').val() || 2,
                    carousel_columns_desktop: $('#lbx-cc-desktop').val() || 3,
                    breakpoint_mobile: $('#lbx-bp-mobile').val() || 640,
                    breakpoint_tablet: $('#lbx-bp-tablet').val() || 1024,
                    // Watermark
                    watermark_image: $('#lbx-watermark-image').val() || '',
                    watermark_position: $('#lbx-watermark-position').val() || 'bottom-right',
                    watermark_size: parseInt($('#lbx-watermark-size').val(), 10) || 50,
                    // Disclaimer icon position (hidden | top-left | top-right | bottom-left | bottom-right)
                    disclaimer_position: $('#lbx-disclaimer-position').val() || 'hidden',
                    // Video playback
                    video_autoplay: $('#lbx-video-autoplay').is(':checked') ? 1 : 0,
                    video_hover_play: $('#lbx-video-hover-play').is(':checked') ? 1 : 0,
                    video_loop: $('#lbx-video-loop').is(':checked') ? 1 : 0,
                    // Scheduling
                    slider_start_date: $('#lbx-slider-start-date').val() || '',
                    slider_end_date: $('#lbx-slider-end-date').val() || '',
                };

                if (source === 'minerva') {
                    syncExtraData();
                    postData.extra_slides = JSON.stringify(extraSlides);
                    postData.slide_settings = JSON.stringify(slideSettings);
                    postData.minerva_order = JSON.stringify(minervaOrder || []);
                    postData.unified_slides = JSON.stringify(unifiedSlides || []);
                    postData.static_settings = JSON.stringify(staticSettings || {});
                    postData.auto_populate_oem = autoPopulateOem ? 1 : 0;
                    postData.auto_populate_static = autoPopulateStatic ? 1 : 0;
                    postData.make_filter = makeFilter || '';
                }

                if (source === 'custom') {
                    syncSlideData();
                    postData.custom_slides = JSON.stringify(customSlides);
                }

                if (source === 'countdown') {
                    postData.countdown_end_date = $('#lbx-cd-end-date').val();
                    postData.countdown_start_date = $('#lbx-cd-start-date').val();
                    postData.countdown_title = $('#lbx-cd-title').val();
                    postData.countdown_desc = $('#lbx-cd-desc').val();
                    postData.countdown_expired_msg = $('#lbx-cd-expired-msg').val();
                    postData.countdown_bg_image = $('#lbx-cd-bg-image').val();
                    postData.countdown_cta_label = $('#lbx-cd-cta-label').val();
                    postData.countdown_cta_url = $('#lbx-cd-cta-url').val();
                    postData.countdown_image_link_url = $('#lbx-cd-image-link-url').val();
                    postData.countdown_image_link_target = $('#lbx-cd-image-link-target').val();
                    postData.countdown_hide_watermark = $('#lbx-cd-hide-watermark').is(':checked') ? 1 : 0;
                    postData.countdown_style = $('#lbx-cd-style').val() || 'flip';
                    postData.countdown_overlay_opacity = parseInt($('#lbx-cd-overlay-opacity').val(), 10);
                    if (isNaN(postData.countdown_overlay_opacity)) postData.countdown_overlay_opacity = 60;
                }

                return postData;
            }

            // Validate the form before a Save (NOT before a Preview — preview is forgiving).
            // Returns true when OK to proceed; shows inline errors + returns false otherwise.
            function validateSliderForm() {
                var name = $('#new-slider-name').val().trim();
                if (!name) {
                    showNotice('<?php echo esc_js(__('Please enter a slider name.', 'leadbox')); ?>', 'error');
                    var $nameEl = $('#new-slider-name');
                    activateTabContaining($nameEl);
                    $nameEl.trigger('focus');
                    if ($nameEl[0] && typeof $nameEl[0].select === 'function') $nameEl[0].select();
                    return false;
                }
                var source = $('input[name="slider_source"]:checked').val() || 'minerva';
                if (source === 'custom') {
                    syncSlideData();
                    if (!customSlides.length) {
                        showNotice('<?php echo esc_js(__('Please add at least one slide.', 'leadbox')); ?>', 'error');
                        activateTabContaining($('#lbx-custom-section'));
                        return false;
                    }
                }
                if (source === 'countdown') {
                    validateCountdownDates();
                    if (!$('#lbx-cd-end-date').val()) {
                        $('#lbx-cd-end-wrap').addClass('has-error');
                        $('#lbx-cd-end-error').text('<?php echo esc_js(__('End date is required.', 'leadbox')); ?>').show();
                        activateTabContaining($('#lbx-cd-end-date'));
                        $('#lbx-cd-end-date').focus();
                        return false;
                    }
                    if ($('#lbx-countdown-section .has-error').length) {
                        var $firstErrorInput = $('#lbx-countdown-section .has-error:first input');
                        activateTabContaining($firstErrorInput);
                        $firstErrorInput.focus();
                        return false;
                    }
                }
                return true;
            }

            // ─── Save ─────────────────────────────────────────────────
            $('#lbx-create-slider').on('click', function() {
                if (!validateSliderForm()) return;
                var postData = collectSliderFormData();
                postData.action = 'lbx_save_slider';
                var btn = $(this).prop('disabled', true).css('opacity', 0.6);
                $.post(ajaxurl, postData, function(r) {
                    btn.prop('disabled', false).css('opacity', 1);
                    if (r.success) {
                        showNotice(r.data.message, 'success');
                        // If this was a new slider, update the URL and hidden ID so subsequent saves are edits
                        if (r.data.slider_id && $('#edit-slider-id').val() !== r.data.slider_id) {
                            $('#edit-slider-id').val(r.data.slider_id);
                            window.history.replaceState(null, '', '<?php echo admin_url('admin.php?page=lbx-slider&edit_slider='); ?>' + encodeURIComponent(r.data.slider_id));
                            // It's now a saved slider — the button shifts from "Create" to "Save Changes".
                            $('#lbx-create-slider .lbx-save-label').text('<?php echo esc_js(__('Save Changes', 'leadbox')); ?>');
                        }
                    } else {
                        showNotice(r.data, 'error');
                    }
                });
            });

            // ─── Live Preview ─────────────────────────────────────────
            var lbxPreview = (function() {
                var $modal = $('#lbx-preview-modal');
                var $stage = $('#lbx-preview-modal .lbx-preview-stage');
                var $frameWrap = $('#lbx-preview-frame-wrap');
                var $viewport = $('#lbx-preview-viewport');
                var $canvas = $('#lbx-preview-canvas');
                var $iframe = $('#lbx-preview-iframe');
                var $range = $('#lbx-preview-width-range');
                var $num = $('#lbx-preview-width-num');
                var $dims = $('#lbx-preview-dims');
                var $loading = $('#lbx-preview-loading');
                var $error = $('#lbx-preview-error');
                var lastUrl = '';
                var MIN_W = 320, MAX_W = 3840;
                // Space reserved on each side for the drag handles so the scaled viewport
                // + handles always fit inside the stage (keeps everything centered, no scroll).
                var HANDLE_RESERVE = 40;

                // State drives every render. logicalW = the width the slider renders at
                // (its breakpoint); contentH = its real rendered height; full = fit-to-stage.
                var state = { full: true, logicalW: 1280, contentH: 480 };
                var heightTimers = [];
                function clearHeightTimers() { heightTimers.forEach(clearTimeout); heightTimers = []; }

                // Available width inside the stage. Prefer the live box (getBoundingClientRect)
                // minus padding so it's accurate even right after the modal opens.
                function stageWidth() {
                    var el = $stage[0];
                    if (!el) return 1200;
                    var cs = window.getComputedStyle(el);
                    var pad = (parseFloat(cs.paddingLeft) || 0) + (parseFloat(cs.paddingRight) || 0);
                    return Math.max(200, Math.floor(el.clientWidth - pad));
                }

                // The single render: size the logical canvas, scale it to fit the stage
                // (never upscale — like Chrome's responsive mode), size the outer viewport to
                // the SCALED box so there's never horizontal scroll, and keep ALL controls in
                // sync (range + numeric field + dims) so nothing shows a stale value.
                function render() {
                    var availW = stageWidth();
                    var full = state.full;
                    // Full uses the whole stage; fixed widths leave room for the handles.
                    var usableW = full ? availW : Math.max(120, availW - HANDLE_RESERVE);
                    var logicalW = full ? availW : Math.max(MIN_W, Math.min(MAX_W, state.logicalW));
                    var h = state.contentH || 480;
                    var factor = Math.min(1, usableW / logicalW); // scale down only

                    $canvas.css({ width: logicalW + 'px', height: h + 'px', transform: 'scale(' + factor + ')' });
                    $viewport.css({ width: Math.round(logicalW * factor) + 'px', height: Math.round(h * factor) + 'px' });

                    // Keep the toolbar controls coherent in EVERY mode (fixes the "shows 375
                    // while in Full" desync). Clamp to the slider's range bounds.
                    var shownW = Math.round(logicalW);
                    var rMin = parseInt($range.attr('min'), 10) || MIN_W;
                    var rMax = parseInt($range.attr('max'), 10) || 1920;
                    $range.val(Math.min(rMax, Math.max(rMin, shownW)));
                    $num.val(shownW);

                    var prefix = full ? '<?php echo esc_js(__('Full', 'leadbox')); ?> · ' : '';
                    var zoom = factor < 0.999 ? ' · ' + Math.round(factor * 100) + '%' : '';
                    $dims.text(prefix + shownW + ' × ' + h + ' px' + zoom);
                }

                // Measure the slider's real rendered height inside the iframe (same-origin)
                // and re-render. Cheap-ish but touches the iframe DOM, so we DON'T call it on
                // every drag frame — only on settle (mouseup), preset change, and load.
                function fitHeight() {
                    try {
                        var doc = $iframe[0].contentDocument;
                        if (doc && doc.body) {
                            var el = doc.querySelector('.lbx-slider') || doc.querySelector('.lbx-preview-stage');
                            var h = el ? Math.ceil(el.getBoundingClientRect().height) : 0;
                            if (!h) h = Math.max(doc.body.scrollHeight, (doc.documentElement ? doc.documentElement.scrollHeight : 0));
                            if (h > 40) state.contentH = h;
                        }
                    } catch (e) { /* same-origin always; guard just in case */ }
                    render();
                }

                // Re-measure height across a few ticks (Vue mounts + Swiper resizes async).
                function scheduleHeightSync() {
                    clearHeightTimers();
                    [80, 300, 650].forEach(function(d) { heightTimers.push(setTimeout(fitHeight, d)); });
                }

                // Set the logical width (or full) and render. Light — no iframe DOM reads.
                function applyWidth(width) {
                    var full = !width || width === 0;
                    state.full = full;
                    $frameWrap.toggleClass('is-full', full);
                    if (!full) state.logicalW = Math.max(MIN_W, Math.min(MAX_W, parseInt(width, 10) || 1280));
                    render();
                    scheduleHeightSync();
                }

                function setActiveDevice(width) {
                    $modal.find('.lbx-preview-device').each(function() {
                        $(this).toggleClass('is-active', parseInt($(this).data('w'), 10) === parseInt(width, 10));
                    });
                }

                function showLoading(on) { $loading.toggle(!!on); }
                function showError(msg) {
                    $error.html('<span class="dashicons dashicons-warning"></span><div>' + msg + '</div>').show();
                    $loading.hide();
                }

                // EN/FR preview language. Always appends an explicit &lang=en|fr so the
                // backend forces the locale BOTH ways (it calls switch_to_locale). Omitting
                // it for EN would fall through to the site default — French on French-first
                // sites — so the EN button would wrongly render French.
                var previewLang = 'en';
                function withLang(url) {
                    if (!url) return url;
                    return url + (url.indexOf('?') > -1 ? '&' : '?') + 'lbx_lang=' + previewLang;
                }
                function open() {
                    if ($('#edit-slider-id').length === 0) return;
                    $error.hide();
                    showLoading(true);
                    // Reset to full-desktop each open for a predictable starting point.
                    state = { full: true, logicalW: 1280, contentH: state.contentH || 480 };
                    setActiveDevice(0);
                    // Stash the current form config, then point the iframe at the preview URL.
                    var postData = collectSliderFormData();
                    postData.action = 'lbx_slider_preview_stash';
                    $.post(ajaxurl, postData, function(r) {
                        if (r && r.success && r.data && r.data.url) {
                            lastUrl = r.data.url;
                            $iframe.attr('src', withLang(lastUrl));
                        } else {
                            showError('<?php echo esc_js(__('Could not build the preview. Try again.', 'leadbox')); ?>');
                        }
                    }).fail(function() {
                        showError('<?php echo esc_js(__('Could not build the preview. Try again.', 'leadbox')); ?>');
                    });
                    $modal.addClass('is-open').attr('aria-hidden', 'false');
                    $('body').addClass('lbx-preview-open');
                    // Wait for the modal to lay out before measuring the stage — otherwise
                    // the first render reads a stale/too-small width and the dims are wrong.
                    requestAnimationFrame(function() { requestAnimationFrame(render); });
                }

                function close() {
                    $modal.removeClass('is-open').attr('aria-hidden', 'true');
                    $('body').removeClass('lbx-preview-open');
                    $iframe.attr('src', 'about:blank');
                }

                function reload() {
                    if (!$modal.hasClass('is-open')) return;
                    $error.hide();
                    showLoading(true);
                    open();
                }

                // iframe finished loading → hide spinner + measure real height.
                $iframe.on('load', function() {
                    if (($iframe.attr('src') || '') === 'about:blank') return;
                    showLoading(false);
                    scheduleHeightSync();
                });

                // EN / FR preview language toggle — reloads the iframe in the chosen locale.
                $modal.on('click', '.lbx-preview-lang-btn', function() {
                    var l = $(this).data('lang');
                    if (l === previewLang) return;
                    previewLang = l;
                    $modal.find('.lbx-preview-lang-btn').removeClass('is-active');
                    $(this).addClass('is-active');
                    if (lastUrl) { showLoading(true); $iframe.attr('src', withLang(lastUrl)); }
                });

                // Device presets.
                $modal.on('click', '.lbx-preview-device', function() {
                    var w = parseInt($(this).data('w'), 10) || 0;
                    setActiveDevice(w);
                    applyWidth(w);
                });

                // Range + numeric field (two-way sync).
                $range.on('input', function() {
                    var w = parseInt($(this).val(), 10);
                    setActiveDevice(w);
                    applyWidth(w);
                });
                $num.on('input', function() {
                    var w = parseInt($(this).val(), 10);
                    if (isNaN(w)) return;
                    setActiveDevice(w);
                    applyWidth(w);
                });

                // Drag handles — symmetric resize. Throttled with rAF and light per frame
                // (render() only writes CSS; height is re-measured ONCE on release), which
                // fixes the "sticks then jumps" lag from re-reading the iframe every frame.
                $modal.on('mousedown', '.lbx-preview-handle', function(e) {
                    e.preventDefault();
                    var side = $(this).data('side');
                    var $h = $(this).addClass('is-dragging');
                    var startX = e.clientX;
                    var startW = state.full ? stageWidth() : state.logicalW;
                    // Leave full mode locked to the current width.
                    state.full = false;
                    $frameWrap.removeClass('is-full');
                    setActiveDevice(-1);
                    var raf = null, lastX = startX;
                    function onMove(ev) {
                        lastX = ev.clientX;
                        if (raf) return;
                        raf = requestAnimationFrame(function() {
                            raf = null;
                            var delta = (lastX - startX) * (side === 'right' ? 2 : -2); // grows on both sides
                            state.logicalW = Math.max(MIN_W, Math.min(MAX_W, Math.round(startW + delta)));
                            render();
                        });
                    }
                    function onUp() {
                        if (raf) { cancelAnimationFrame(raf); raf = null; }
                        $h.removeClass('is-dragging');
                        $(document).off('mousemove', onMove).off('mouseup', onUp);
                        $('body').css('user-select', '');
                        fitHeight(); // settle the height once, after the drag ends
                    }
                    $('body').css('user-select', 'none');
                    $(document).on('mousemove', onMove).on('mouseup', onUp);
                });

                // Toolbar buttons.
                $('#lbx-preview-btn').on('click', open);
                $('#lbx-preview-close').on('click', close);
                $('#lbx-preview-refresh').on('click', reload);
                $('#lbx-preview-newtab').on('click', function() {
                    if (lastUrl) window.open(withLang(lastUrl), '_blank');
                });
                $modal.on('click', function(e) { if (e.target === this) close(); });

                // Keyboard: Esc closes; Cmd/Ctrl+Shift+P opens.
                $(document).on('keydown', function(e) {
                    if (e.key === 'Escape' && $modal.hasClass('is-open')) { close(); return; }
                    if ((e.metaKey || e.ctrlKey) && e.shiftKey && (e.key === 'P' || e.key === 'p')) {
                        if ($('#lbx-preview-btn').length) { e.preventDefault(); open(); }
                    }
                });

                // Re-fit on window resize (full mode tracks the stage; scaled mode re-checks fit).
                var resizeRaf = null;
                $(window).on('resize', function() {
                    if (!$modal.hasClass('is-open')) return;
                    if (resizeRaf) return;
                    resizeRaf = requestAnimationFrame(function() { resizeRaf = null; render(); });
                });

                return { open: open, close: close };
            })();

            // ─── Dashboard: Toggle ────────────────────────────────────
            // Use a direct click handler so inline `onclick="event.stopPropagation();"`
            // doesn't prevent the AJAX request (the handler previously depended on bubbling).
            $('.lbx-dash-toggle').on('click', function() {
                var $card = $(this).closest('.lbx-dash-card');
                var id = $card.data('slider-id');
                var btn = $(this);
                $.post(ajaxurl, { action: 'lbx_toggle_slider', nonce: nonce, slider_id: id }, function(r) {
                    if (r.success) {
                        // Reload to reflect schedule-aware status (Active vs Scheduled vs Expired).
                        // The status badge depends on current time + start_date + end_date — easier to refetch from server than recompute client-side.
                        showNotice(r.data.message, 'success');
                        setTimeout(function() { window.location.reload(); }, 600);
                    }
                });
            });

            // ─── Dashboard: Delete ────────────────────────────────────
            $('.lbx-dash-delete').on('click', function() {
                var $btn = $(this);
                var $card = $btn.closest('.lbx-dash-card');
                var id = $card.data('slider-id');
                var sliderName = $card.find('.lbx-dash-card__name').text() || '<?php echo esc_js(__('this slider', 'leadbox')); ?>';
                lbxConfirm({
                    title: '<?php echo esc_js(__('Delete slider?', 'leadbox')); ?>',
                    message: '"' + sliderName + '" <?php echo esc_js(__('will be permanently deleted. This cannot be undone.', 'leadbox')); ?>',
                    confirmText: '<?php echo esc_js(__('Delete', 'leadbox')); ?>',
                    cancelText: '<?php echo esc_js(__('Cancel', 'leadbox')); ?>',
                    danger: true
                }).then(function(ok) {
                    if (!ok) return;
                    $.post(ajaxurl, { action: 'lbx_delete_slider', nonce: nonce, slider_id: id }, function(r) {
                        if (r.success) {
                            $card.css('opacity', 0.5).fadeOut(400, function() { $(this).remove(); });
                            showNotice(r.data.message, 'success');
                        }
                    });
                });
            });

            // ─── Dashboard: Duplicate ─────────────────────────────────
            // Creates a copy under a fresh id and lands the user on the edit page for the
            // new slider so they can tweak the name / contents immediately. Duplicate starts
            // disabled so production traffic doesn't see a cloned slider by accident.
            $('.lbx-dash-duplicate').on('click', function() {
                var $btn = $(this);
                if ($btn.prop('disabled')) return;
                var $card = $btn.closest('.lbx-dash-card');
                var id = $card.data('slider-id');
                var sliderName = $card.find('.lbx-dash-card__name').text() || '<?php echo esc_js(__('this slider', 'leadbox')); ?>';
                lbxConfirm({
                    title: '<?php echo esc_js(__('Duplicate slider?', 'leadbox')); ?>',
                    message: '"' + sliderName + '" <?php echo esc_js(__('will be copied as a new slider (starts disabled).', 'leadbox')); ?>',
                    confirmText: '<?php echo esc_js(__('Duplicate', 'leadbox')); ?>',
                    cancelText: '<?php echo esc_js(__('Cancel', 'leadbox')); ?>',
                    danger: false
                }).then(function(ok) {
                    if (!ok) return;
                    $btn.prop('disabled', true).css('opacity', 0.6);
                    $.post(ajaxurl, { action: 'lbx_duplicate_slider', nonce: nonce, slider_id: id }, function(r) {
                        if (r.success && r.data && r.data.slider_id) {
                            showNotice(r.data.message, 'success');
                            // Land the user on the edit page for the new slider.
                            setTimeout(function() {
                                window.location.href = '<?php echo admin_url('admin.php?page=lbx-slider&edit_slider='); ?>' + encodeURIComponent(r.data.slider_id);
                            }, 400);
                        } else {
                            $btn.prop('disabled', false).css('opacity', 1);
                            showNotice((r.data && r.data.message) || r.data || '<?php echo esc_js(__('Could not duplicate slider.', 'leadbox')); ?>', 'error');
                        }
                    }).fail(function() {
                        $btn.prop('disabled', false).css('opacity', 1);
                        showNotice('<?php echo esc_js(__('Network error while duplicating.', 'leadbox')); ?>', 'error');
                    });
                });
            });

            // ─── Dashboard: Click card to edit ────────────────────────
            $(document).on('click', '.lbx-dash-card', function() {
                var id = $(this).data('slider-id');
                window.location.href = '<?php echo admin_url('admin.php?page=lbx-slider&edit_slider='); ?>' + encodeURIComponent(id);
            });
        })(jQuery);
        </script>
        <?php
    }
}