<?php
namespace DS_Plugin;

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

class Settings {
    const OPTION_KEY = 'ds_settings';
    const LICENSE_STATUS_CACHE_KEY = 'ds_license_status_admin_v1';
    const LICENSE_STATUS_CACHE_TTL = 300; // 5m
    const LICENSE_BOOTSTRAP_META_KEY = 'ds_license_bootstrap_meta_v1';

    public static function init() {
        add_action('admin_menu', [__CLASS__, 'add_settings_submenu']);
        add_action('admin_post_ds_download_asset_pack', [__CLASS__, 'download_asset_pack']);
        add_action('admin_init', [__CLASS__, 'handle_admin_actions']);
        add_action('admin_notices', [__CLASS__, 'render_license_admin_notice']);
    }

    public static function defaults() {
        return [
            'antispam_enabled' => 1,
            'interval_sec' => 5,
            'hour_limit' => 60,
            'allowed_origins' => '',
            'removebg_api_key' => '',
            'texture_library_urls' => '',
            'frame_library_urls' => '',
            'custom_icon_groups_json' => '',
            'custom_emoticon_groups_json' => '',
            'brand_header_mode' => 'logo',
            'brand_header_enabled' => 1,
            'brand_logo_url' => '',
            'brand_title_text' => 'Pressio',
            'brand_subtitle_text' => 'Precision - Power - Scale',
            'brand_subtitle_position' => 'right',
            'ui_primary_color' => '#0a7f73',
            'ui_accent_color' => '#f89f25',
            'ui_secondary_accent_color' => '#2f80ed',
            'ui_bg_color' => '#f3f8ff',
            'ui_text_color' => '#122033',
            'ui_text_font' => 'segoe_ui',
            'ui_panel_color' => '#ffffff',
            'ui_button_color' => '#ffffff',
            'ui_line_color' => '#d3dde8',
            'ui_muted_color' => '#5f7188',
            'ui_modern_icon_color' => '#ffffff',
            'ui_active_preset' => 'system:lorandit_teal',
            'ui_custom_presets_json' => '',
            'ui_variant' => 'classic',
            'ui_classic_palette' => 'arctic_mint',
            'ui_modern_right_panel_position' => 'right',
            'preview_hide_white_zone_bg' => 1,
            'protection_enabled' => 1,
            'protection_trial_total' => 0,
            'protection_license_api_url' => 'https://license.lorandit.com/v1',
            'protection_license_api_token' => '',
            'protection_license_bootstrap_token' => '',
            'protection_license_install_token' => '',
            'protection_license_key' => '',
            'protection_bootstrap_autofill' => 1,
            'protection_license_cache_min' => 15,
        ];
    }

    public static function get() {
        $opts = get_option(self::OPTION_KEY, []);
        $merged = wp_parse_args(is_array($opts) ? $opts : [], self::defaults());
        $merged['ui_variant'] = 'classic';
        $merged['ui_modern_right_panel_position'] = 'right';
        $font_choices = self::ui_text_font_choices();
        $ui_text_font = isset($merged['ui_text_font']) ? sanitize_key((string) $merged['ui_text_font']) : 'segoe_ui';
        if (!isset($font_choices[$ui_text_font])) $ui_text_font = 'segoe_ui';
        $merged['ui_text_font'] = $ui_text_font;

        if (defined('DS_LICENSE_API_URL') && is_string(DS_LICENSE_API_URL) && DS_LICENSE_API_URL !== '') {
            $merged['protection_license_api_url'] = esc_url_raw(DS_LICENSE_API_URL);
        }

        if (
            empty($merged['protection_license_api_token']) &&
            defined('DS_LICENSE_API_TOKEN') &&
            is_string(DS_LICENSE_API_TOKEN) &&
            DS_LICENSE_API_TOKEN !== ''
        ) {
            $merged['protection_license_api_token'] = sanitize_text_field(DS_LICENSE_API_TOKEN);
        }

        if (
            empty($merged['protection_license_bootstrap_token']) &&
            defined('DS_LICENSE_BOOTSTRAP_TOKEN') &&
            is_string(DS_LICENSE_BOOTSTRAP_TOKEN) &&
            DS_LICENSE_BOOTSTRAP_TOKEN !== ''
        ) {
            $merged['protection_license_bootstrap_token'] = sanitize_text_field(DS_LICENSE_BOOTSTRAP_TOKEN);
        }

        if (
            empty($merged['protection_license_install_token']) &&
            defined('DS_LICENSE_INSTALL_TOKEN') &&
            is_string(DS_LICENSE_INSTALL_TOKEN) &&
            DS_LICENSE_INSTALL_TOKEN !== ''
        ) {
            $merged['protection_license_install_token'] = sanitize_text_field(DS_LICENSE_INSTALL_TOKEN);
        }

        if (empty($merged['protection_license_install_token'])) {
            $install_token = Protection::resolve_install_token($merged);
            if (is_string($install_token) && $install_token !== '') {
                $merged['protection_license_install_token'] = sanitize_text_field($install_token);
            }
        }

        return $merged;
    }

    public static function ui_text_font_choices() {
        return [
            'segoe_ui' => [
                'label' => __('Segoe UI (Default)', 'ds'),
                'stack' => '"Segoe UI", Tahoma, Arial, sans-serif',
            ],
            'system_ui' => [
                'label' => __('System UI', 'ds'),
                'stack' => 'system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif',
            ],
            'roboto' => [
                'label' => __('Roboto', 'ds'),
                'stack' => 'Roboto, "Segoe UI", Arial, sans-serif',
            ],
            'open_sans' => [
                'label' => __('Open Sans', 'ds'),
                'stack' => '"Open Sans", "Segoe UI", Arial, sans-serif',
            ],
            'montserrat' => [
                'label' => __('Montserrat', 'ds'),
                'stack' => 'Montserrat, "Segoe UI", Arial, sans-serif',
            ],
            'lato' => [
                'label' => __('Lato', 'ds'),
                'stack' => 'Lato, "Segoe UI", Arial, sans-serif',
            ],
            'poppins' => [
                'label' => __('Poppins', 'ds'),
                'stack' => 'Poppins, "Segoe UI", Arial, sans-serif',
            ],
            'nunito' => [
                'label' => __('Nunito Sans', 'ds'),
                'stack' => '"Nunito Sans", "Segoe UI", Arial, sans-serif',
            ],
            'arial' => [
                'label' => __('Arial', 'ds'),
                'stack' => 'Arial, sans-serif',
            ],
            'verdana' => [
                'label' => __('Verdana', 'ds'),
                'stack' => 'Verdana, Arial, sans-serif',
            ],
        ];
    }

    private static function appearance_color_defaults() {
        return [
            'ui_primary_color' => '#0a7f73',
            'ui_accent_color' => '#f89f25',
            'ui_secondary_accent_color' => '#2f80ed',
            'ui_bg_color' => '#f3f8ff',
            'ui_text_color' => '#122033',
            'ui_panel_color' => '#ffffff',
            'ui_button_color' => '#ffffff',
            'ui_line_color' => '#d3dde8',
            'ui_muted_color' => '#5f7188',
        ];
    }

    private static function normalize_appearance_colors($colors) {
        $defaults = self::appearance_color_defaults();
        $out = [];
        foreach ($defaults as $key => $fallback) {
            $val = isset($colors[$key]) ? sanitize_hex_color((string) $colors[$key]) : '';
            $out[$key] = $val ?: $fallback;
        }
        return $out;
    }

    private static function appearance_system_presets() {
        return [
            'system:lorandit_teal' => [
                'label' => __('Lorandit Teal', 'ds'),
                'colors' => self::normalize_appearance_colors([
                    'ui_primary_color' => '#0a7f73',
                    'ui_accent_color' => '#f89f25',
                    'ui_secondary_accent_color' => '#2f80ed',
                    'ui_bg_color' => '#f3f8ff',
                    'ui_text_color' => '#122033',
                    'ui_panel_color' => '#ffffff',
                    'ui_button_color' => '#ffffff',
                    'ui_line_color' => '#d3dde8',
                    'ui_muted_color' => '#5f7188',
                ]),
            ],
            'system:midnight_blue' => [
                'label' => __('Midnight Blue', 'ds'),
                'colors' => self::normalize_appearance_colors([
                    'ui_primary_color' => '#1d4ed8',
                    'ui_accent_color' => '#22c55e',
                    'ui_secondary_accent_color' => '#0ea5e9',
                    'ui_bg_color' => '#eaf2ff',
                    'ui_text_color' => '#0f172a',
                    'ui_panel_color' => '#ffffff',
                    'ui_button_color' => '#ffffff',
                    'ui_line_color' => '#c5d3ea',
                    'ui_muted_color' => '#4b5d78',
                ]),
            ],
            'system:sunset_warm' => [
                'label' => __('Sunset Warm', 'ds'),
                'colors' => self::normalize_appearance_colors([
                    'ui_primary_color' => '#d97706',
                    'ui_accent_color' => '#ef4444',
                    'ui_secondary_accent_color' => '#7c3aed',
                    'ui_bg_color' => '#fff7ed',
                    'ui_text_color' => '#3b2f2f',
                    'ui_panel_color' => '#ffffff',
                    'ui_button_color' => '#ffffff',
                    'ui_line_color' => '#ecd8c5',
                    'ui_muted_color' => '#7b6054',
                ]),
            ],
            'system:forest_ink' => [
                'label' => __('Forest Ink', 'ds'),
                'colors' => self::normalize_appearance_colors([
                    'ui_primary_color' => '#166534',
                    'ui_accent_color' => '#0284c7',
                    'ui_secondary_accent_color' => '#a855f7',
                    'ui_bg_color' => '#ecfdf5',
                    'ui_text_color' => '#102a23',
                    'ui_panel_color' => '#ffffff',
                    'ui_button_color' => '#ffffff',
                    'ui_line_color' => '#c9e3d7',
                    'ui_muted_color' => '#45665a',
                ]),
            ],
        ];
    }

    private static function decode_custom_appearance_presets($raw) {
        $text = trim((string) $raw);
        if ($text === '') return [];
        $decoded = json_decode($text, true);
        if (!is_array($decoded)) return [];

        $out = [];
        $i = 0;
        foreach ($decoded as $item) {
            if (!is_array($item)) continue;
            $name = sanitize_text_field((string) ($item['name'] ?? ''));
            if ($name === '') continue;
            $id = sanitize_key((string) ($item['id'] ?? ''));
            if ($id === '') $id = 'preset_' . $i;
            $i++;
            $colors = self::normalize_appearance_colors(
                (isset($item['colors']) && is_array($item['colors'])) ? $item['colors'] : []
            );
            $out[] = [
                'id' => 'custom:' . $id,
                'name' => $name,
                'colors' => $colors,
            ];
        }
        return $out;
    }

    private static function encode_custom_appearance_presets($presets) {
        if (!is_array($presets) || empty($presets)) return '';
        $out = [];
        foreach ($presets as $item) {
            if (!is_array($item)) continue;
            $raw_id = (string) ($item['id'] ?? '');
            if (strpos($raw_id, 'custom:') === 0) $raw_id = substr($raw_id, 7);
            $id = sanitize_key($raw_id);
            $name = sanitize_text_field((string) ($item['name'] ?? ''));
            if ($id === '' || $name === '') continue;
            $colors = self::normalize_appearance_colors(
                (isset($item['colors']) && is_array($item['colors'])) ? $item['colors'] : []
            );
            $out[] = [
                'id' => $id,
                'name' => $name,
                'colors' => $colors,
            ];
        }
        if (empty($out)) return '';
        return (string) wp_json_encode($out, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    }

    public static function add_settings_submenu() {
        add_submenu_page(
            'edit.php?post_type=ds_template',
            __('Lorandit DS Pro Settings', 'ds'),
            __('Settings', 'ds'),
            'manage_options',
            'ds-settings',
            [__CLASS__, 'render_settings_page']
        );
    }

    private static function sample_asset_pack_groups($kind) {
        $is_icons = ((string) $kind === 'icons');
        if ($is_icons) {
            return [
                [
                    'id' => 'group_social',
                    'name' => 'Social',
                    'items' => [
                        [ 'id' => 'icon_ig', 'label' => 'Instagram', 'icon' => 'camera', 'glyph' => 'I' ],
                        [ 'id' => 'icon_tt', 'label' => 'TikTok', 'icon' => 'play', 'glyph' => 'T' ],
                        [ 'id' => 'icon_yt', 'label' => 'YouTube', 'icon' => 'play', 'glyph' => 'Y' ],
                        [ 'id' => 'icon_fb', 'label' => 'Facebook', 'icon' => 'user', 'glyph' => 'F' ],
                    ],
                ],
                [
                    'id' => 'group_commerce',
                    'name' => 'Commerce',
                    'items' => [
                        [ 'id' => 'icon_sale', 'label' => 'Sale', 'icon' => 'tag', 'glyph' => '$' ],
                        [ 'id' => 'icon_new', 'label' => 'New', 'icon' => 'sparkles', 'glyph' => 'N' ],
                        [ 'id' => 'icon_gift', 'label' => 'Gift', 'icon' => 'gift', 'glyph' => 'G' ],
                        [ 'id' => 'icon_secure', 'label' => 'Secure', 'icon' => 'shield', 'glyph' => 'S' ],
                    ],
                ],
            ];
        }
        return [
            [
                'id' => 'group_mood',
                'name' => 'Mood',
                'items' => [
                    [ 'id' => 'emoji_smile', 'label' => 'Smile', 'emoji' => ':)' ],
                    [ 'id' => 'emoji_laugh', 'label' => 'Laugh', 'emoji' => ':D' ],
                    [ 'id' => 'emoji_cool', 'label' => 'Cool', 'emoji' => 'B-)' ],
                    [ 'id' => 'emoji_party', 'label' => 'Party', 'emoji' => '<3' ],
                ],
            ],
            [
                'id' => 'group_symbols',
                'name' => 'Symbols',
                'items' => [
                    [ 'id' => 'emoji_heart', 'label' => 'Heart', 'emoji' => '<3' ],
                    [ 'id' => 'emoji_star', 'label' => 'Star', 'emoji' => '*' ],
                    [ 'id' => 'emoji_check', 'label' => 'Check', 'emoji' => 'OK' ],
                    [ 'id' => 'emoji_rocket', 'label' => 'Rocket', 'emoji' => '>>' ],
                ],
            ],
        ];
    }

    private static function sample_asset_pack_json($kind) {
        return (string) wp_json_encode(
            self::sample_asset_pack_groups($kind),
            JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
        );
    }

    private static function asset_pack_filename($kind, $format, $mode = 'sample') {
        $k = ((string) $kind === 'icons') ? 'icons' : 'emoticons';
        $f = ((string) $format === 'zip') ? 'zip' : 'json';
        $m = ((string) $mode === 'current') ? 'current' : 'sample';
        return 'ds-' . $k . '-' . $m . '-pack.' . $f;
    }

    private static function asset_pack_download_url($kind, $format, $mode = 'sample') {
        $kind_safe = ((string) $kind === 'icons') ? 'icons' : 'emoticons';
        $format_safe = ((string) $format === 'zip') ? 'zip' : 'json';
        $mode_safe = ((string) $mode === 'current') ? 'current' : 'sample';
        $url = add_query_arg([
            'action' => 'ds_download_asset_pack',
            'type' => $kind_safe,
            'format' => $format_safe,
            'mode' => $mode_safe,
        ], admin_url('admin-post.php'));
        return wp_nonce_url($url, 'ds_download_asset_pack');
    }

    private static function sample_asset_pack_download_url($kind, $format) {
        return self::asset_pack_download_url($kind, $format, 'sample');
    }

    private static function current_asset_pack_json($kind) {
        $opts = self::get();
        $key = ((string) $kind === 'icons') ? 'custom_icon_groups_json' : 'custom_emoticon_groups_json';
        $raw = (string) ($opts[$key] ?? '');
        $normalized = self::sanitize_custom_asset_groups_json($raw, $kind);
        if ($normalized === null || $normalized === '') {
            return '[]';
        }
        return $normalized;
    }

    public static function download_asset_pack() {
        if (!is_user_logged_in()) auth_redirect();
        check_admin_referer('ds_download_asset_pack');

        $kind = isset($_GET['type']) ? sanitize_key((string) wp_unslash($_GET['type'])) : 'icons';
        $format = isset($_GET['format']) ? sanitize_key((string) wp_unslash($_GET['format'])) : 'json';
        $mode = isset($_GET['mode']) ? sanitize_key((string) wp_unslash($_GET['mode'])) : 'sample';
        $kind = ($kind === 'icons') ? 'icons' : 'emoticons';
        $format = ($format === 'zip') ? 'zip' : 'json';
        $mode = ($mode === 'current') ? 'current' : 'sample';

        $json = ($mode === 'current')
            ? self::current_asset_pack_json($kind)
            : self::sample_asset_pack_json($kind);
        if ($json === '') {
            wp_die(__('Could not build asset pack.', 'ds'));
        }

        if ($format === 'zip') {
            if (!class_exists('\ZipArchive')) {
                wp_die(__('ZIP export requires the PHP Zip extension.', 'ds'));
            }
            $tmp_zip = tempnam(sys_get_temp_dir(), 'ds_pack_');
            if (!is_string($tmp_zip) || $tmp_zip === '') {
                wp_die(__('Could not allocate temporary ZIP file.', 'ds'));
            }
            $zip = new \ZipArchive();
            if ($zip->open($tmp_zip, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) {
                @unlink($tmp_zip);
                wp_die(__('Could not create ZIP file.', 'ds'));
            }
            $entry_name = (($kind === 'icons') ? 'icons' : 'emoticons') . '-' . $mode . '.json';
            $zip->addFromString($entry_name, $json);
            $zip->close();

            $zip_bin = @file_get_contents($tmp_zip);
            @unlink($tmp_zip);
            if (!is_string($zip_bin) || $zip_bin === '') {
                wp_die(__('Generated ZIP is empty.', 'ds'));
            }

            nocache_headers();
            header('Content-Type: application/zip');
            header('Content-Disposition: attachment; filename="' . self::asset_pack_filename($kind, 'zip', $mode) . '"');
            header('Content-Length: ' . strlen($zip_bin));
            echo $zip_bin;
            exit;
        }

        nocache_headers();
        header('Content-Type: application/json; charset=utf-8');
        header('Content-Disposition: attachment; filename="' . self::asset_pack_filename($kind, 'json', $mode) . '"');
        header('Content-Length: ' . strlen($json));
        echo $json;
        exit;
    }

    private static function save_post() {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') return;
        if (!current_user_can('manage_options')) return;

        check_admin_referer('ds_settings_save');

        $action = isset($_POST['ds_action']) ? sanitize_text_field(wp_unslash($_POST['ds_action'])) : 'save_settings';
        if ($action !== 'save_settings') return;

        $antispam = isset($_POST['antispam_enabled']) ? 1 : 0;
        $interval = isset($_POST['interval_sec']) ? max(0, intval($_POST['interval_sec'])) : 5;
        $hour = isset($_POST['hour_limit']) ? max(0, intval($_POST['hour_limit'])) : 60;
        $origins = isset($_POST['allowed_origins']) ? sanitize_textarea_field(wp_unslash($_POST['allowed_origins'])) : '';
        $removebg_api_key = isset($_POST['removebg_api_key']) ? sanitize_text_field(wp_unslash($_POST['removebg_api_key'])) : '';
        $texture_library_urls = isset($_POST['texture_library_urls']) ? sanitize_textarea_field(wp_unslash($_POST['texture_library_urls'])) : '';
        $frame_library_urls = isset($_POST['frame_library_urls']) ? sanitize_textarea_field(wp_unslash($_POST['frame_library_urls'])) : '';
        $custom_icon_groups_raw = isset($_POST['custom_icon_groups_json']) ? (string) wp_unslash($_POST['custom_icon_groups_json']) : '';
        $custom_emoticon_groups_raw = isset($_POST['custom_emoticon_groups_json']) ? (string) wp_unslash($_POST['custom_emoticon_groups_json']) : '';
        $icon_pack_file = (isset($_FILES['custom_icon_groups_pack']) && is_array($_FILES['custom_icon_groups_pack'])) ? $_FILES['custom_icon_groups_pack'] : null;
        $emoticon_pack_file = (isset($_FILES['custom_emoticon_groups_pack']) && is_array($_FILES['custom_emoticon_groups_pack'])) ? $_FILES['custom_emoticon_groups_pack'] : null;

        $brand_header_mode = isset($_POST['brand_header_mode']) ? sanitize_text_field(wp_unslash($_POST['brand_header_mode'])) : 'logo';
        if (!in_array($brand_header_mode, ['logo', 'text'], true)) $brand_header_mode = 'logo';

        $brand_header_enabled = isset($_POST['brand_header_enabled']) ? 1 : 0;
        $brand_logo_url = isset($_POST['brand_logo_url']) ? esc_url_raw(wp_unslash($_POST['brand_logo_url'])) : '';
        $brand_title_text = isset($_POST['brand_title_text']) ? sanitize_text_field(wp_unslash($_POST['brand_title_text'])) : 'Pressio';
        $brand_subtitle_text = isset($_POST['brand_subtitle_text']) ? sanitize_text_field(wp_unslash($_POST['brand_subtitle_text'])) : 'Precision - Power - Scale';

        $brand_subtitle_position = isset($_POST['brand_subtitle_position']) ? sanitize_text_field(wp_unslash($_POST['brand_subtitle_position'])) : 'right';
        if (!in_array($brand_subtitle_position, ['top', 'bottom', 'left', 'right'], true)) $brand_subtitle_position = 'right';

        $ui_primary_color = isset($_POST['ui_primary_color']) ? sanitize_hex_color(wp_unslash($_POST['ui_primary_color'])) : '#0a7f73';
        if (!$ui_primary_color) $ui_primary_color = '#0a7f73';

        $ui_accent_color = isset($_POST['ui_accent_color']) ? sanitize_hex_color(wp_unslash($_POST['ui_accent_color'])) : '#f89f25';
        if (!$ui_accent_color) $ui_accent_color = '#f89f25';

        $ui_secondary_accent_color = isset($_POST['ui_secondary_accent_color']) ? sanitize_hex_color(wp_unslash($_POST['ui_secondary_accent_color'])) : '#2f80ed';
        if (!$ui_secondary_accent_color) $ui_secondary_accent_color = '#2f80ed';

        $ui_bg_color = isset($_POST['ui_bg_color']) ? sanitize_hex_color(wp_unslash($_POST['ui_bg_color'])) : '#f3f8ff';
        if (!$ui_bg_color) $ui_bg_color = '#f3f8ff';

        $ui_text_color = isset($_POST['ui_text_color']) ? sanitize_hex_color(wp_unslash($_POST['ui_text_color'])) : '#122033';
        if (!$ui_text_color) $ui_text_color = '#122033';
        $ui_text_font = isset($_POST['ui_text_font']) ? sanitize_key(wp_unslash($_POST['ui_text_font'])) : 'segoe_ui';
        $font_choices = self::ui_text_font_choices();
        if (!isset($font_choices[$ui_text_font])) $ui_text_font = 'segoe_ui';
        $ui_panel_color = isset($_POST['ui_panel_color']) ? sanitize_hex_color(wp_unslash($_POST['ui_panel_color'])) : '#ffffff';
        if (!$ui_panel_color) $ui_panel_color = '#ffffff';
        $ui_button_color = isset($_POST['ui_button_color']) ? sanitize_hex_color(wp_unslash($_POST['ui_button_color'])) : '#ffffff';
        if (!$ui_button_color) $ui_button_color = '#ffffff';
        $ui_line_color = isset($_POST['ui_line_color']) ? sanitize_hex_color(wp_unslash($_POST['ui_line_color'])) : '#d3dde8';
        if (!$ui_line_color) $ui_line_color = '#d3dde8';
        $ui_muted_color = isset($_POST['ui_muted_color']) ? sanitize_hex_color(wp_unslash($_POST['ui_muted_color'])) : '#5f7188';
        if (!$ui_muted_color) $ui_muted_color = '#5f7188';

        $current_colors = self::normalize_appearance_colors([
            'ui_primary_color' => $ui_primary_color,
            'ui_accent_color' => $ui_accent_color,
            'ui_secondary_accent_color' => $ui_secondary_accent_color,
            'ui_bg_color' => $ui_bg_color,
            'ui_text_color' => $ui_text_color,
            'ui_text_font' => $ui_text_font,
            'ui_panel_color' => $ui_panel_color,
            'ui_button_color' => $ui_button_color,
            'ui_line_color' => $ui_line_color,
            'ui_muted_color' => $ui_muted_color,
        ]);

        $current = self::get();
        $system_presets = self::appearance_system_presets();
        $custom_presets = self::decode_custom_appearance_presets((string) ($current['ui_custom_presets_json'] ?? ''));

        $ui_active_preset = isset($_POST['ui_active_preset'])
            ? sanitize_text_field(wp_unslash($_POST['ui_active_preset']))
            : (string) ($current['ui_active_preset'] ?? 'system:lorandit_teal');
        if ($ui_active_preset === '') $ui_active_preset = 'system:lorandit_teal';

        $known_preset_ids = array_keys($system_presets);
        foreach ($custom_presets as $preset_item) {
            if (!empty($preset_item['id'])) $known_preset_ids[] = (string) $preset_item['id'];
        }
        if (!in_array($ui_active_preset, $known_preset_ids, true)) $ui_active_preset = 'system:lorandit_teal';

        $ui_save_current_preset = isset($_POST['ui_save_current_preset']) ? 1 : 0;
        $ui_save_preset_name = isset($_POST['ui_save_preset_name']) ? sanitize_text_field(wp_unslash($_POST['ui_save_preset_name'])) : '';
        if ($ui_save_current_preset) {
            if ($ui_save_preset_name === '') {
                add_settings_error('ds_settings_messages', 'ds_ui_preset_name_missing', __('Preset name is required.', 'ds'), 'error');
            } else {
                $preset_key = sanitize_key($ui_save_preset_name);
                if ($preset_key === '') $preset_key = 'preset_' . gmdate('Ymd_His');
                $custom_id = 'custom:' . $preset_key;
                $updated = false;
                foreach ($custom_presets as &$preset_item) {
                    if (!is_array($preset_item)) continue;
                    if ((string) ($preset_item['id'] ?? '') !== $custom_id) continue;
                    $preset_item['name'] = $ui_save_preset_name;
                    $preset_item['colors'] = $current_colors;
                    $updated = true;
                    break;
                }
                unset($preset_item);
                if (!$updated) {
                    $custom_presets[] = [
                        'id' => $custom_id,
                        'name' => $ui_save_preset_name,
                        'colors' => $current_colors,
                    ];
                }
                $ui_active_preset = $custom_id;
                add_settings_error('ds_settings_messages', 'ds_ui_preset_saved', __('Appearance preset saved.', 'ds'), 'updated');
            }
        }
        $ui_custom_presets_json = self::encode_custom_appearance_presets($custom_presets);
        if ($icon_pack_file) {
            $icon_pack_error = '';
            $imported_icons_json = self::read_uploaded_asset_pack($icon_pack_file, 'icons', $icon_pack_error);
            if (is_string($imported_icons_json)) {
                $custom_icon_groups_raw = $imported_icons_json;
                add_settings_error('ds_settings_messages', 'ds_custom_icon_pack_imported', __('Icon pack imported successfully.', 'ds'), 'updated');
            } elseif ($imported_icons_json === false) {
                add_settings_error('ds_settings_messages', 'ds_custom_icon_pack_failed', sprintf(__('Icon pack import failed: %s', 'ds'), $icon_pack_error !== '' ? $icon_pack_error : __('Invalid file.', 'ds')), 'error');
            }
        }
        if ($emoticon_pack_file) {
            $emoticon_pack_error = '';
            $imported_emoticons_json = self::read_uploaded_asset_pack($emoticon_pack_file, 'emoticons', $emoticon_pack_error);
            if (is_string($imported_emoticons_json)) {
                $custom_emoticon_groups_raw = $imported_emoticons_json;
                add_settings_error('ds_settings_messages', 'ds_custom_emoticon_pack_imported', __('Emoticon pack imported successfully.', 'ds'), 'updated');
            } elseif ($imported_emoticons_json === false) {
                add_settings_error('ds_settings_messages', 'ds_custom_emoticon_pack_failed', sprintf(__('Emoticon pack import failed: %s', 'ds'), $emoticon_pack_error !== '' ? $emoticon_pack_error : __('Invalid file.', 'ds')), 'error');
            }
        }
        $custom_icon_groups_json = self::sanitize_custom_asset_groups_json($custom_icon_groups_raw, 'icons');
        if ($custom_icon_groups_json === null) {
            $custom_icon_groups_json = (string) ($current['custom_icon_groups_json'] ?? '');
            add_settings_error('ds_settings_messages', 'ds_custom_icon_groups_invalid', __('Custom icon groups JSON is invalid. Previous value is kept.', 'ds'), 'error');
        }
        $custom_emoticon_groups_json = self::sanitize_custom_asset_groups_json($custom_emoticon_groups_raw, 'emoticons');
        if ($custom_emoticon_groups_json === null) {
            $custom_emoticon_groups_json = (string) ($current['custom_emoticon_groups_json'] ?? '');
            add_settings_error('ds_settings_messages', 'ds_custom_emoticon_groups_invalid', __('Custom emoticon groups JSON is invalid. Previous value is kept.', 'ds'), 'error');
        }

        $ui_modern_icon_color = (string) ($current['ui_modern_icon_color'] ?? '#ffffff');
        if (!sanitize_hex_color($ui_modern_icon_color)) $ui_modern_icon_color = '#ffffff';

        $ui_variant = 'classic';

        $ui_classic_palette = isset($_POST['ui_classic_palette']) ? sanitize_text_field(wp_unslash($_POST['ui_classic_palette'])) : 'arctic_mint';
        if (!in_array($ui_classic_palette, ['arctic_mint', 'warm_paper', 'steel_blue', 'sunset_blush'], true)) $ui_classic_palette = 'arctic_mint';

        $ui_modern_right_panel_position = 'right';

        $preview_hide_white_zone_bg = isset($_POST['preview_hide_white_zone_bg']) ? 1 : 0;
        $protection_license_key = (string) ($current['protection_license_key'] ?? '');
        $protection_license_api_url = (string) ($current['protection_license_api_url'] ?? 'https://license.lorandit.com/v1');
        if ($protection_license_api_url === '') $protection_license_api_url = 'https://license.lorandit.com/v1';

        $protection_license_api_token = (string) ($current['protection_license_api_token'] ?? '');
        $protection_license_bootstrap_token = (string) ($current['protection_license_bootstrap_token'] ?? '');
        $protection_license_install_token = (string) ($current['protection_license_install_token'] ?? '');
        if ($protection_license_install_token === '') {
            $protection_license_install_token = (string) Protection::resolve_install_token($current);
        }
        $protection_bootstrap_autofill = !isset($current['protection_bootstrap_autofill']) ? 1 : (int) $current['protection_bootstrap_autofill'];

        $save = [
            'antispam_enabled' => $antispam,
            'interval_sec' => $interval,
            'hour_limit' => $hour,
            'allowed_origins' => $origins,
            'removebg_api_key' => $removebg_api_key,
            'texture_library_urls' => $texture_library_urls,
            'frame_library_urls' => $frame_library_urls,
            'custom_icon_groups_json' => $custom_icon_groups_json,
            'custom_emoticon_groups_json' => $custom_emoticon_groups_json,
            'brand_header_mode' => $brand_header_mode,
            'brand_header_enabled' => $brand_header_enabled,
            'brand_logo_url' => $brand_logo_url,
            'brand_title_text' => $brand_title_text,
            'brand_subtitle_text' => $brand_subtitle_text,
            'brand_subtitle_position' => $brand_subtitle_position,
            'ui_primary_color' => $ui_primary_color,
            'ui_accent_color' => $ui_accent_color,
            'ui_secondary_accent_color' => $ui_secondary_accent_color,
            'ui_bg_color' => $ui_bg_color,
            'ui_text_color' => $ui_text_color,
            'ui_text_font' => $ui_text_font,
            'ui_panel_color' => $ui_panel_color,
            'ui_button_color' => $ui_button_color,
            'ui_line_color' => $ui_line_color,
            'ui_muted_color' => $ui_muted_color,
            'ui_modern_icon_color' => $ui_modern_icon_color,
            'ui_active_preset' => $ui_active_preset,
            'ui_custom_presets_json' => $ui_custom_presets_json,
            'ui_variant' => $ui_variant,
            'ui_classic_palette' => $ui_classic_palette,
            'ui_modern_right_panel_position' => $ui_modern_right_panel_position,
            'preview_hide_white_zone_bg' => $preview_hide_white_zone_bg,
            'protection_enabled' => 1,
            'protection_trial_total' => 0,
            'protection_license_api_url' => $protection_license_api_url,
            'protection_license_api_token' => $protection_license_api_token,
            'protection_license_bootstrap_token' => $protection_license_bootstrap_token,
            'protection_license_install_token' => $protection_license_install_token,
            'protection_license_key' => $protection_license_key,
            'protection_bootstrap_autofill' => $protection_bootstrap_autofill,
            'protection_license_cache_min' => 15,
        ];

        update_option(self::OPTION_KEY, $save);
        self::flush_license_status_cache();
        add_settings_error('ds_settings_messages', 'ds_settings_saved', __('Settings saved.', 'ds'), 'updated');
    }

    private static function sanitize_custom_asset_groups_json($raw, $kind) {
        $text = trim((string) $raw);
        if ($text === '') return '';
        $decoded = json_decode($text, true);
        if (!is_array($decoded)) return null;

        $is_icons = ((string) $kind === 'icons');
        $groups_out = [];
        $group_index = 0;

        foreach ($decoded as $group) {
            if (!is_array($group)) continue;
            $name = sanitize_text_field((string) ($group['name'] ?? $group['label'] ?? $group['group'] ?? ''));
            if ($name === '') continue;

            $group_id = sanitize_key((string) ($group['id'] ?? ''));
            if ($group_id === '') $group_id = 'group_' . $group_index;
            $group_index++;

            $items_raw = isset($group['items']) && is_array($group['items']) ? $group['items'] : [];
            $items_out = [];
            $item_index = 0;

            foreach ($items_raw as $item) {
                if (!is_array($item)) continue;
                if ($is_icons) {
                    $icon_key = sanitize_key((string) ($item['icon'] ?? $item['name'] ?? ''));
                    if ($icon_key === '') continue;
                    $label = sanitize_text_field((string) ($item['label'] ?? $icon_key));
                    if ($label === '') $label = $icon_key;
                    $glyph = sanitize_text_field((string) ($item['glyph'] ?? ''));
                    $item_id = sanitize_key((string) ($item['id'] ?? ('icon_' . $icon_key . '_' . $item_index)));
                    if ($item_id === '') $item_id = 'icon_' . $item_index;
                    $items_out[] = [
                        'id' => $item_id,
                        'label' => $label,
                        'icon' => $icon_key,
                        'glyph' => $glyph,
                    ];
                } else {
                    $emoji = sanitize_text_field((string) ($item['emoji'] ?? $item['glyph'] ?? ''));
                    if ($emoji === '') continue;
                    $label = sanitize_text_field((string) ($item['label'] ?? $emoji));
                    if ($label === '') $label = $emoji;
                    $item_id = sanitize_key((string) ($item['id'] ?? ('emoji_' . $item_index)));
                    if ($item_id === '') $item_id = 'emoji_' . $item_index;
                    $items_out[] = [
                        'id' => $item_id,
                        'label' => $label,
                        'emoji' => $emoji,
                    ];
                }
                $item_index++;
            }

            if (empty($items_out)) continue;

            $groups_out[] = [
                'id' => $group_id,
                'name' => $name,
                'items' => $items_out,
            ];
        }

        if (empty($groups_out)) return '';
        return (string) wp_json_encode($groups_out, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    }

    private static function is_list_array($value) {
        if (!is_array($value)) return false;
        $i = 0;
        foreach (array_keys($value) as $key) {
            if ((int) $key !== $i) return false;
            $i++;
        }
        return true;
    }

    private static function read_uploaded_asset_pack($file, $kind, &$error_message = '') {
        $error_message = '';
        if (!is_array($file) || empty($file['name'])) return null;
        $upload_error = isset($file['error']) ? (int) $file['error'] : UPLOAD_ERR_NO_FILE;
        if ($upload_error === UPLOAD_ERR_NO_FILE) return null;
        if ($upload_error !== UPLOAD_ERR_OK) {
            $error_message = __('Upload failed. Please try again.', 'ds');
            return false;
        }

        $tmp_path = isset($file['tmp_name']) ? (string) $file['tmp_name'] : '';
        if ($tmp_path === '' || !is_file($tmp_path)) {
            $error_message = __('Uploaded file is not readable.', 'ds');
            return false;
        }

        $name = isset($file['name']) ? (string) $file['name'] : '';
        $ext = strtolower((string) pathinfo($name, PATHINFO_EXTENSION));

        $payloads = [];
        if ($ext === 'json') {
            $content = @file_get_contents($tmp_path);
            if (!is_string($content) || $content === '') {
                $error_message = __('JSON file is empty or unreadable.', 'ds');
                return false;
            }
            $payloads[] = $content;
        } elseif ($ext === 'zip') {
            if (!class_exists('\ZipArchive')) {
                $error_message = __('ZIP import requires the PHP Zip extension.', 'ds');
                return false;
            }
            $zip = new \ZipArchive();
            $open_ok = $zip->open($tmp_path);
            if ($open_ok !== true) {
                $error_message = __('Could not open ZIP file.', 'ds');
                return false;
            }
            $max_files = min((int) $zip->numFiles, 300);
            for ($i = 0; $i < $max_files; $i++) {
                $entry_name = (string) $zip->getNameIndex($i);
                if ($entry_name === '' || substr($entry_name, -1) === '/') continue;
                $entry_ext = strtolower((string) pathinfo($entry_name, PATHINFO_EXTENSION));
                if ($entry_ext !== 'json') continue;
                $content = $zip->getFromIndex($i);
                if (is_string($content) && $content !== '') $payloads[] = $content;
            }
            $zip->close();
            if (empty($payloads)) {
                $error_message = __('ZIP does not contain any JSON files.', 'ds');
                return false;
            }
        } else {
            $error_message = __('Only .json or .zip packs are supported.', 'ds');
            return false;
        }

        $merged_groups = [];
        foreach ($payloads as $payload) {
            $decoded = json_decode((string) $payload, true);
            if (is_array($decoded)) {
                if (isset($decoded['groups']) && is_array($decoded['groups'])) {
                    $decoded = $decoded['groups'];
                } elseif ((string) $kind === 'icons' && isset($decoded['icons']) && is_array($decoded['icons'])) {
                    $decoded = $decoded['icons'];
                } elseif ((string) $kind === 'emoticons' && isset($decoded['emoticons']) && is_array($decoded['emoticons'])) {
                    $decoded = $decoded['emoticons'];
                } elseif (!self::is_list_array($decoded) && isset($decoded[0]) && is_array($decoded[0])) {
                    // keep as list-like array with numeric keys
                } elseif (!self::is_list_array($decoded)) {
                    // unsupported object payload
                    continue;
                }
                if (is_array($decoded)) {
                    foreach ($decoded as $group) {
                        if (is_array($group)) $merged_groups[] = $group;
                    }
                }
            }
        }

        if (empty($merged_groups)) {
            $error_message = __('No valid groups found in uploaded pack.', 'ds');
            return false;
        }

        $normalized = self::sanitize_custom_asset_groups_json(wp_json_encode($merged_groups), $kind);
        if ($normalized === null) {
            $error_message = __('Uploaded pack has invalid structure.', 'ds');
            return false;
        }
        return $normalized;
    }

    private static function render_custom_asset_builder($kind, $field_id, $field_name, $raw_value) {
        $is_icons = ((string) $kind === 'icons');
        $quick_placeholder = $is_icons
            ? "Instagram | camera | I\nTikTok | play | T\nStar | star | *"
            : "Smile | 🙂\nFire | 🔥\nHeart | ❤️";
        $quick_help = $is_icons
            ? __('Format per line: Label | icon_key | glyph (optional).', 'ds')
            : __('Format per line: Label | emoji.', 'ds');
        $adv_help = $is_icons
            ? __('Optional advanced JSON format: [{"name":"Group","items":[{"label":"...","icon":"icon_key","glyph":"optional"}]}]', 'ds')
            : __('Optional advanced JSON format: [{"name":"Group","items":[{"label":"...","emoji":"text_or_emoji"}]}]', 'ds');
        ?>
        <div class="ds-asset-builder" data-kind="<?php echo esc_attr($kind); ?>" data-target="#<?php echo esc_attr($field_id); ?>">
            <div class="ds-asset-builder-top">
                <button type="button" class="button button-secondary" data-action="add-group"><?php _e('Add Group', 'ds'); ?></button>
                <span class="description"><?php _e('Create groups and items without writing JSON.', 'ds'); ?></span>
            </div>
            <div class="ds-asset-builder-groups" data-role="groups"></div>
            <div class="ds-asset-builder-quick">
                <label><?php _e('Quick Paste', 'ds'); ?></label>
                <textarea rows="4" data-role="quick-paste" placeholder="<?php echo esc_attr($quick_placeholder); ?>"></textarea>
                <p class="description"><?php echo esc_html($quick_help); ?></p>
                <div class="ds-asset-builder-quick-actions">
                    <button type="button" class="button" data-action="quick-paste"><?php _e('Paste To Last Group', 'ds'); ?></button>
                    <button type="button" class="button-link-delete" data-action="clear-groups"><?php _e('Clear All', 'ds'); ?></button>
                </div>
            </div>
            <details class="ds-asset-builder-advanced">
                <summary><?php _e('Advanced JSON (optional)', 'ds'); ?></summary>
                <textarea rows="8" data-role="advanced-json"><?php echo esc_textarea((string) $raw_value); ?></textarea>
                <p class="description"><?php echo esc_html($adv_help); ?></p>
                <button type="button" class="button" data-action="import-json"><?php _e('Import JSON', 'ds'); ?></button>
            </details>
        </div>
        <textarea id="<?php echo esc_attr($field_id); ?>" name="<?php echo esc_attr($field_name); ?>" hidden><?php echo esc_textarea((string) $raw_value); ?></textarea>
        <?php
    }

    private static function render_admin_scripts() {
        ?>
        <script>
        (function() {
            const builders = Array.from(document.querySelectorAll(".ds-asset-builder[data-kind][data-target]"));
            if (!builders.length) return;

            const form = document.querySelector(".ds-admin-shell form");
            const states = new Map();

            function escHtml(v) {
                return String(v == null ? "" : v)
                    .replace(/&/g, "&amp;")
                    .replace(/</g, "&lt;")
                    .replace(/>/g, "&gt;")
                    .replace(/"/g, "&quot;")
                    .replace(/'/g, "&#039;");
            }

            function sanitizeIconKey(v) {
                return String(v || "").trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
            }

            function parseJsonSafe(text) {
                const raw = String(text || "").trim();
                if (!raw) return [];
                try {
                    const parsed = JSON.parse(raw);
                    return Array.isArray(parsed) ? parsed : [];
                } catch (e) {
                    return null;
                }
            }

            function normalizeGroups(inputGroups, kind) {
                const isIcons = kind === "icons";
                const out = [];
                const groups = Array.isArray(inputGroups) ? inputGroups : [];
                groups.forEach((group) => {
                    if (!group || typeof group !== "object") return;
                    const name = String(group.name || group.label || group.group || "").trim();
                    if (!name) return;
                    const itemsIn = Array.isArray(group.items) ? group.items : [];
                    const itemsOut = [];
                    itemsIn.forEach((item) => {
                        if (!item || typeof item !== "object") return;
                        if (isIcons) {
                            const icon = sanitizeIconKey(item.icon || item.name || "");
                            if (!icon) return;
                            const label = String(item.label || icon).trim() || icon;
                            const glyph = String(item.glyph || "").trim();
                            itemsOut.push({ label: label, icon: icon, glyph: glyph });
                        } else {
                            const emoji = String(item.emoji || item.glyph || "").trim();
                            if (!emoji) return;
                            const label = String(item.label || emoji).trim() || emoji;
                            itemsOut.push({ label: label, emoji: emoji });
                        }
                    });
                    if (!itemsOut.length) return;
                    out.push({ name: name, items: itemsOut });
                });
                return out;
            }

            function createEmptyItem(kind) {
                return kind === "icons"
                    ? { label: "", icon: "", glyph: "" }
                    : { label: "", emoji: "" };
            }

            function createEmptyGroup(kind) {
                return { name: "", items: [createEmptyItem(kind)] };
            }

            function toSerializable(groups, kind) {
                const isIcons = kind === "icons";
                const out = [];
                (Array.isArray(groups) ? groups : []).forEach((group, groupIndex) => {
                    const name = String(group && group.name ? group.name : "").trim();
                    if (!name) return;
                    const itemsOut = [];
                    const itemsIn = group && Array.isArray(group.items) ? group.items : [];
                    itemsIn.forEach((item, itemIndex) => {
                        if (!item || typeof item !== "object") return;
                        if (isIcons) {
                            const icon = sanitizeIconKey(item.icon || "");
                            if (!icon) return;
                            const label = String(item.label || icon).trim() || icon;
                            const glyph = String(item.glyph || "").trim();
                            itemsOut.push({
                                id: "icon_" + groupIndex + "_" + itemIndex,
                                label: label,
                                icon: icon,
                                glyph: glyph
                            });
                        } else {
                            const emoji = String(item.emoji || "").trim();
                            if (!emoji) return;
                            const label = String(item.label || emoji).trim() || emoji;
                            itemsOut.push({
                                id: "emoji_" + groupIndex + "_" + itemIndex,
                                label: label,
                                emoji: emoji
                            });
                        }
                    });
                    if (!itemsOut.length) return;
                    out.push({
                        id: "group_" + groupIndex,
                        name: name,
                        items: itemsOut
                    });
                });
                return out;
            }

            function syncHidden(builder) {
                const targetSel = String(builder.getAttribute("data-target") || "");
                const hidden = targetSel ? document.querySelector(targetSel) : null;
                const state = states.get(builder);
                if (!hidden || !state) return;
                const payload = toSerializable(state.groups, state.kind);
                hidden.value = payload.length ? JSON.stringify(payload) : "";
            }

            function renderBuilder(builder) {
                const state = states.get(builder);
                if (!state) return;
                const groupsHost = builder.querySelector('[data-role="groups"]');
                if (!groupsHost) return;
                const isIcons = state.kind === "icons";
                const rows = [];

                if (!state.groups.length) {
                    rows.push('<div class="ds-asset-builder-empty">No groups yet. Click "Add Group".</div>');
                }

                state.groups.forEach((group, gIdx) => {
                    rows.push('<div class="ds-asset-group-card" data-group-index="' + gIdx + '">');
                    rows.push('<div class="ds-asset-group-head">');
                    rows.push('<input type="text" class="regular-text" data-field="group-name" data-group-index="' + gIdx + '" placeholder="Group name" value="' + escHtml(group.name || "") + '">');
                    rows.push('<button type="button" class="button-link-delete" data-action="remove-group" data-group-index="' + gIdx + '">Remove Group</button>');
                    rows.push('</div>');
                    rows.push('<div class="ds-asset-item-list">');

                    (Array.isArray(group.items) ? group.items : []).forEach((item, iIdx) => {
                        rows.push('<div class="ds-asset-item-row">');
                        rows.push('<input type="text" data-field="label" data-group-index="' + gIdx + '" data-item-index="' + iIdx + '" placeholder="Label" value="' + escHtml(item.label || "") + '">');
                        if (isIcons) {
                            rows.push('<input type="text" data-field="icon" data-group-index="' + gIdx + '" data-item-index="' + iIdx + '" placeholder="icon key (e.g. camera)" value="' + escHtml(item.icon || "") + '">');
                            rows.push('<input type="text" data-field="glyph" data-group-index="' + gIdx + '" data-item-index="' + iIdx + '" placeholder="glyph (optional)" value="' + escHtml(item.glyph || "") + '">');
                        } else {
                            rows.push('<input type="text" data-field="emoji" data-group-index="' + gIdx + '" data-item-index="' + iIdx + '" placeholder="emoji (e.g. :))" value="' + escHtml(item.emoji || "") + '">');
                        }
                        rows.push('<button type="button" class="button-link-delete" data-action="remove-item" data-group-index="' + gIdx + '" data-item-index="' + iIdx + '">x</button>');
                        rows.push('</div>');
                    });

                    rows.push('</div>');
                    rows.push('<div class="ds-asset-group-foot">');
                    rows.push('<button type="button" class="button button-small" data-action="add-item" data-group-index="' + gIdx + '">Add Item</button>');
                    rows.push('</div>');
                    rows.push('</div>');
                });

                groupsHost.innerHTML = rows.join("");
                syncHidden(builder);
            }

            function applyQuickPaste(builder) {
                const state = states.get(builder);
                if (!state) return;
                const area = builder.querySelector('[data-role="quick-paste"]');
                if (!area) return;
                const raw = String(area.value || "");
                const lines = raw.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
                if (!lines.length) return;
                if (!state.groups.length) state.groups.push(createEmptyGroup(state.kind));
                const group = state.groups[state.groups.length - 1];
                const isIcons = state.kind === "icons";

                lines.forEach((line) => {
                    const parts = line.split("|").map((p) => String(p || "").trim()).filter((p) => p !== "");
                    if (!parts.length) return;
                    if (isIcons) {
                        let label = "";
                        let icon = "";
                        let glyph = "";
                        if (parts.length === 1) {
                            label = parts[0];
                            icon = sanitizeIconKey(parts[0]);
                        } else if (parts.length >= 2) {
                            label = parts[0];
                            icon = sanitizeIconKey(parts[1]);
                            glyph = parts[2] || "";
                        }
                        if (!icon) return;
                        group.items.push({ label: label || icon, icon: icon, glyph: glyph });
                    } else {
                        let label = "";
                        let emoji = "";
                        if (parts.length === 1) {
                            label = parts[0];
                            emoji = parts[0];
                        } else {
                            label = parts[0];
                            emoji = parts[1];
                        }
                        emoji = String(emoji || "").trim();
                        if (!emoji) return;
                        group.items.push({ label: label || emoji, emoji: emoji });
                    }
                });

                group.items = group.items.filter((item) => {
                    if (isIcons) return sanitizeIconKey(item.icon || "") !== "";
                    return String(item.emoji || "").trim() !== "";
                });
                area.value = "";
                renderBuilder(builder);
            }

            function importAdvancedJson(builder) {
                const state = states.get(builder);
                if (!state) return;
                const area = builder.querySelector('[data-role="advanced-json"]');
                if (!area) return;
                const parsed = parseJsonSafe(area.value);
                if (parsed === null) {
                    window.alert("Invalid JSON.");
                    return;
                }
                const groups = normalizeGroups(parsed, state.kind);
                state.groups = groups;
                renderBuilder(builder);
            }

            builders.forEach((builder) => {
                const kind = String(builder.getAttribute("data-kind") || "icons").toLowerCase() === "icons" ? "icons" : "emoticons";
                const targetSel = String(builder.getAttribute("data-target") || "");
                const hidden = targetSel ? document.querySelector(targetSel) : null;
                const initialRaw = hidden ? String(hidden.value || "") : "";
                const parsed = parseJsonSafe(initialRaw);
                const groups = normalizeGroups(parsed === null ? [] : parsed, kind);
                states.set(builder, { kind: kind, groups: groups });
                renderBuilder(builder);
            });

            builders.forEach((builder) => {
                builder.addEventListener("input", (e) => {
                    const state = states.get(builder);
                    if (!state) return;
                    const t = e.target;
                    if (!(t instanceof HTMLElement)) return;
                    const field = String(t.getAttribute("data-field") || "");
                    const gIdx = parseInt(String(t.getAttribute("data-group-index") || "-1"), 10);
                    const iIdx = parseInt(String(t.getAttribute("data-item-index") || "-1"), 10);
                    if (field === "group-name" && state.groups[gIdx]) {
                        state.groups[gIdx].name = String(t.value || "");
                        syncHidden(builder);
                        return;
                    }
                    if (!state.groups[gIdx] || !state.groups[gIdx].items || !state.groups[gIdx].items[iIdx]) return;
                    if (field === "label") state.groups[gIdx].items[iIdx].label = String(t.value || "");
                    if (field === "icon") state.groups[gIdx].items[iIdx].icon = sanitizeIconKey(t.value || "");
                    if (field === "glyph") state.groups[gIdx].items[iIdx].glyph = String(t.value || "");
                    if (field === "emoji") state.groups[gIdx].items[iIdx].emoji = String(t.value || "");
                    syncHidden(builder);
                });

                builder.addEventListener("click", (e) => {
                    const btn = e.target && e.target.closest ? e.target.closest("[data-action]") : null;
                    if (!btn) return;
                    const state = states.get(builder);
                    if (!state) return;
                    const action = String(btn.getAttribute("data-action") || "");
                    const gIdx = parseInt(String(btn.getAttribute("data-group-index") || "-1"), 10);
                    const iIdx = parseInt(String(btn.getAttribute("data-item-index") || "-1"), 10);
                    if (action === "add-group") {
                        state.groups.push(createEmptyGroup(state.kind));
                        renderBuilder(builder);
                        return;
                    }
                    if (action === "remove-group") {
                        if (gIdx >= 0) state.groups.splice(gIdx, 1);
                        renderBuilder(builder);
                        return;
                    }
                    if (action === "add-item") {
                        if (gIdx >= 0 && state.groups[gIdx]) state.groups[gIdx].items.push(createEmptyItem(state.kind));
                        renderBuilder(builder);
                        return;
                    }
                    if (action === "remove-item") {
                        if (gIdx >= 0 && state.groups[gIdx] && iIdx >= 0) state.groups[gIdx].items.splice(iIdx, 1);
                        renderBuilder(builder);
                        return;
                    }
                    if (action === "quick-paste") {
                        applyQuickPaste(builder);
                        return;
                    }
                    if (action === "clear-groups") {
                        if (!window.confirm("Clear all groups and items?")) return;
                        state.groups = [];
                        renderBuilder(builder);
                        return;
                    }
                    if (action === "import-json") {
                        importAdvancedJson(builder);
                    }
                });
            });

            if (form) {
                form.addEventListener("submit", () => {
                    builders.forEach((builder) => syncHidden(builder));
                });
            }
        })();

        (function() {
            const presetSelect = document.getElementById("ui_active_preset");
            if (!presetSelect) return;

            const colorFieldIds = [
                "ui_primary_color",
                "ui_accent_color",
                "ui_secondary_accent_color",
                "ui_bg_color",
                "ui_text_color",
                "ui_panel_color",
                "ui_button_color",
                "ui_line_color",
                "ui_muted_color"
            ];

            function applyPreset(optionEl) {
                if (!optionEl) return;
                const raw = String(optionEl.getAttribute("data-preset-colors") || "").trim();
                if (!raw) return;
                let colors = null;
                try {
                    colors = JSON.parse(raw);
                } catch (e) {
                    colors = null;
                }
                if (!colors || typeof colors !== "object") return;
                colorFieldIds.forEach((id) => {
                    const input = document.getElementById(id);
                    if (!input) return;
                    const val = String(colors[id] || "").trim();
                    if (/^#[0-9a-f]{6}$/i.test(val)) input.value = val;
                });
            }

            presetSelect.addEventListener("change", () => {
                const optionEl = presetSelect.options[presetSelect.selectedIndex] || null;
                applyPreset(optionEl);
            });

            const savePresetBtn = document.getElementById("ui-save-preset-btn");
            const savePresetName = document.getElementById("ui_save_preset_name");
            if (savePresetBtn && savePresetName) {
                savePresetBtn.addEventListener("click", (e) => {
                    if (String(savePresetName.value || "").trim() !== "") return;
                    e.preventDefault();
                    window.alert("Enter preset name first.");
                    savePresetName.focus();
                });
            }
        })();
        </script>
        <?php
    }

    private static function render_admin_styles() {
        ?>
        <style>
            .ds-admin-shell { max-width: 1220px; }
            .ds-admin-intro {
                display:flex; align-items:center; justify-content:space-between;
                gap:12px; margin:12px 0 18px;
            }
            .ds-admin-intro p { margin: 4px 0 0; color:#4b5563; }
            .ds-admin-layout {
                display:grid;
                grid-template-columns: 240px minmax(0, 1fr);
                gap:16px;
            }
            .ds-admin-quick {
                display:grid;
                grid-template-columns: repeat(3, minmax(0, 1fr));
                gap:10px;
                margin: 0 0 16px;
            }
            .ds-admin-quick a {
                display:block;
                text-decoration:none;
                background:#fff;
                border:1px solid #dcdcde;
                border-radius:10px;
                padding:12px;
                font-weight:600;
                color:#111827;
            }
            .ds-admin-quick a small {
                display:block;
                margin-top:4px;
                color:#6b7280;
                font-weight:400;
            }
            .ds-admin-quick a:hover { border-color:#aab0b6; }
            .ds-admin-side {
                position: sticky; top: 42px; align-self:start;
                background:#fff; border:1px solid #dcdcde; border-radius:10px; padding:10px;
            }
            .ds-admin-side a {
                display:block; padding:8px 10px; border-radius:8px; text-decoration:none; color:#1f2937;
                font-weight:600;
            }
            .ds-admin-side a:hover { background:#f6f7f7; }
            .ds-admin-main { display:grid; gap:14px; }
            .ds-admin-card {
                background:#fff; border:1px solid #dcdcde; border-radius:10px; padding:16px;
            }
            .ds-admin-card h2 { margin:0 0 12px; font-size:16px; }
            .ds-grid-2 {
                display:grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap:12px;
            }
            .ds-field label { display:block; font-weight:600; margin-bottom:6px; }
            .ds-field .description { margin-top:6px; color:#646970; }
            .ds-preset-actions { margin-top:8px; }
            .ds-field input[type="text"],
            .ds-field input[type="url"],
            .ds-field input[type="number"],
            .ds-field select,
            .ds-field textarea {
                width:100%;
            }
            .ds-admin-pill {
                display:inline-block; padding:4px 10px; border-radius:999px;
                font-size:12px; font-weight:700;
                background:#eef2ff; color:#1e3a8a;
            }
            .ds-admin-actions {
                display:flex; justify-content:flex-end; margin-top:12px;
            }
            .ds-asset-builder {
                border: 1px solid #dcdcde;
                border-radius: 10px;
                padding: 10px;
                background: #fcfcfd;
            }
            .ds-asset-builder-top {
                display: flex;
                align-items: center;
                justify-content: space-between;
                gap: 10px;
                margin-bottom: 10px;
            }
            .ds-asset-builder-groups {
                display: grid;
                gap: 10px;
            }
            .ds-asset-builder-empty {
                padding: 10px;
                border: 1px dashed #c3c4c7;
                border-radius: 8px;
                color: #646970;
                background: #fff;
            }
            .ds-asset-group-card {
                border: 1px solid #dcdcde;
                border-radius: 8px;
                background: #fff;
                padding: 10px;
            }
            .ds-asset-group-head {
                display: grid;
                grid-template-columns: minmax(0, 1fr) auto;
                gap: 8px;
                align-items: center;
                margin-bottom: 8px;
            }
            .ds-asset-item-list {
                display: grid;
                gap: 6px;
            }
            .ds-asset-item-row {
                display: grid;
                grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr) auto;
                gap: 6px;
                align-items: center;
            }
            .ds-asset-builder[data-kind="emoticons"] .ds-asset-item-row {
                grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
            }
            .ds-asset-item-row input[type="text"] {
                width: 100%;
            }
            .ds-asset-group-foot {
                margin-top: 8px;
                display: flex;
                justify-content: flex-end;
            }
            .ds-asset-builder-quick {
                margin-top: 10px;
                padding-top: 10px;
                border-top: 1px solid #e5e7eb;
            }
            .ds-asset-builder-quick-actions {
                display: flex;
                align-items: center;
                gap: 8px;
                margin-top: 8px;
            }
            .ds-asset-builder-advanced {
                margin-top: 10px;
                padding-top: 8px;
                border-top: 1px solid #e5e7eb;
            }
            .ds-asset-builder-advanced summary {
                cursor: pointer;
                font-weight: 600;
                margin-bottom: 8px;
            }
            .ds-asset-builder-advanced textarea {
                width: 100%;
                margin-top: 8px;
            }
            .ds-pack-upload-row {
                margin-bottom: 10px;
                padding: 8px 10px;
                border: 1px dashed #c3c4c7;
                border-radius: 8px;
                background: #fff;
            }
            .ds-pack-upload-row input[type="file"] {
                width: 100%;
            }
            .ds-pack-upload-row .description {
                margin: 6px 0 0;
            }
            .ds-pack-download-links {
                display: flex;
                flex-wrap: wrap;
                gap: 8px;
                margin-top: 8px;
            }
            @media (max-width: 1100px) {
                .ds-admin-layout { grid-template-columns: 1fr; }
                .ds-admin-side { position: static; }
                .ds-grid-2 { grid-template-columns: 1fr; }
                .ds-admin-quick { grid-template-columns: 1fr; }
                .ds-asset-item-row,
                .ds-asset-builder[data-kind="emoticons"] .ds-asset-item-row {
                    grid-template-columns: 1fr;
                }
            }
        </style>
        <?php
    }

    private static function flush_license_status_cache() {
        delete_transient(self::LICENSE_STATUS_CACHE_KEY);
    }

    private static function bootstrap_meta_defaults() {
        return [
            'state' => 'idle', // idle|initializing|connected|reconnect_required
            'attempts' => 0,
            'last_attempt_at' => 0,
            'next_attempt_at' => 0,
            'last_success_at' => 0,
            'last_error_code' => '',
            'last_error_message' => '',
        ];
    }

    private static function bootstrap_meta() {
        $raw = get_option(self::LICENSE_BOOTSTRAP_META_KEY, []);
        $meta = wp_parse_args(is_array($raw) ? $raw : [], self::bootstrap_meta_defaults());
        $meta['state'] = sanitize_key((string) $meta['state']);
        if ($meta['state'] === '') $meta['state'] = 'idle';
        $meta['attempts'] = max(0, (int) $meta['attempts']);
        $meta['last_attempt_at'] = max(0, (int) $meta['last_attempt_at']);
        $meta['next_attempt_at'] = max(0, (int) $meta['next_attempt_at']);
        $meta['last_success_at'] = max(0, (int) $meta['last_success_at']);
        $meta['last_error_code'] = sanitize_key((string) $meta['last_error_code']);
        $meta['last_error_message'] = sanitize_text_field((string) $meta['last_error_message']);
        return $meta;
    }

    private static function update_bootstrap_meta($patch) {
        $curr = self::bootstrap_meta();
        if (!is_array($patch)) $patch = [];
        $next = array_merge($curr, $patch);
        update_option(self::LICENSE_BOOTSTRAP_META_KEY, $next, false);
        return self::bootstrap_meta();
    }

    private static function bootstrap_retry_schedule_seconds() {
        return [60, 300, 900, 3600];
    }

    private static function next_bootstrap_delay_seconds($attempts) {
        $schedule = self::bootstrap_retry_schedule_seconds();
        $idx = max(0, min(count($schedule) - 1, max(0, (int) $attempts) - 1));
        return (int) $schedule[$idx];
    }

    private static function license_status_text(array $opts) {
        $state = self::license_status_state($opts);
        return (string) ($state['label'] ?? __('Unknown', 'ds'));
    }

    private static function license_can_upgrade(array $state) {
        $kind = (string) ($state['state'] ?? '');
        return $kind !== 'active';
    }

    private static function license_status_state(array $opts, $force = false) {
        $license_key = trim((string) ($opts['protection_license_key'] ?? ''));
        $api_url = trim((string) ($opts['protection_license_api_url'] ?? ''));
        $api_token = trim((string) ($opts['protection_license_api_token'] ?? ''));

        if ($license_key === '') {
            $meta = self::bootstrap_meta();
            $install_token = (string) Protection::resolve_install_token($opts);
            if (($meta['state'] ?? '') === 'reconnect_required') {
                $details = __('Automatic license initialization failed. Retry bootstrap or reconnect this install from distribution.', 'ds');
                if (!empty($meta['last_error_message'])) {
                    $details .= ' ' . sprintf(__('Last error: %s', 'ds'), (string) $meta['last_error_message']);
                }
                return [
                    'state' => 'reconnect_required',
                    'severity' => 'warning',
                    'label' => __('Reconnect required', 'ds'),
                    'details' => $details,
                    'bootstrap_meta' => $meta,
                ];
            }
            if (($meta['state'] ?? '') === 'initializing' || (int) ($meta['attempts'] ?? 0) > 0 || $install_token !== '') {
                $details = __('License is initializing in background. Designer remains usable while retries continue.', 'ds');
                $next_at = max(0, (int) ($meta['next_attempt_at'] ?? 0));
                if ($next_at > time()) {
                    $details .= ' ' . sprintf(__('Next retry in ~%s.', 'ds'), human_time_diff(time(), $next_at));
                }
                if (!empty($meta['last_error_message'])) {
                    $details .= ' ' . sprintf(__('Last error: %s', 'ds'), (string) $meta['last_error_message']);
                }
                return [
                    'state' => 'initializing',
                    'severity' => 'info',
                    'label' => __('Initializing', 'ds'),
                    'details' => $details,
                    'bootstrap_meta' => $meta,
                ];
            }
            return [
                'state' => 'not_configured',
                'severity' => 'neutral',
                'label' => __('Not configured', 'ds'),
                'details' => __('No license key configured for this site.', 'ds'),
                'bootstrap_meta' => $meta,
            ];
        }

        if ($api_url === '' || $api_token === '') {
            return [
                'state' => 'awaiting_sync',
                'severity' => 'warning',
                'label' => __('License key set (awaiting sync)', 'ds'),
                'details' => __('License API URL or service token is missing.', 'ds'),
            ];
        }

        $fingerprint = self::license_status_fingerprint($opts);
        if (!$force) {
            $cached = get_transient(self::LICENSE_STATUS_CACHE_KEY);
            if (is_array($cached)) {
                $cached_fp = isset($cached['fingerprint']) ? (string) $cached['fingerprint'] : '';
                $cached_state = isset($cached['state']) && is_array($cached['state']) ? $cached['state'] : null;
                if ($cached_fp === $fingerprint && is_array($cached_state)) {
                    return $cached_state;
                }
            }
        }

        $state = self::license_remote_status($opts);
        set_transient(self::LICENSE_STATUS_CACHE_KEY, [
            'fingerprint' => $fingerprint,
            'state' => $state,
        ], self::LICENSE_STATUS_CACHE_TTL);

        return $state;
    }

    private static function license_status_fingerprint(array $opts) {
        return md5(implode('|', [
            trim((string) ($opts['protection_license_api_url'] ?? '')),
            trim((string) ($opts['protection_license_api_token'] ?? '')),
            trim((string) ($opts['protection_license_key'] ?? '')),
            home_url('/'),
            self::license_instance_id(),
        ]));
    }

    private static function license_remote_status(array $opts) {
        $api_url = trim((string) ($opts['protection_license_api_url'] ?? ''));
        $api_token = trim((string) ($opts['protection_license_api_token'] ?? ''));
        $license_key = trim((string) ($opts['protection_license_key'] ?? ''));
        $endpoint = self::license_check_endpoint($api_url);

        if ($endpoint === '') {
            return [
                'state' => 'unavailable',
                'severity' => 'warning',
                'label' => __('Service unavailable', 'ds'),
                'details' => __('Invalid license API URL.', 'ds'),
            ];
        }

        $payload = [
            'license_key' => $license_key,
            'site_url' => home_url('/'),
            'domain' => self::license_site_domain(),
            'instance_id' => self::license_instance_id(),
            'timestamp' => time(),
        ];

        $res = wp_remote_post($endpoint, [
            'timeout' => 8,
            'headers' => [
                'Content-Type' => 'application/json',
                'Authorization' => 'Bearer ' . $api_token,
            ],
            'body' => wp_json_encode($payload),
        ]);

        if (is_wp_error($res)) {
            return [
                'state' => 'unavailable',
                'severity' => 'warning',
                'label' => __('Service unavailable', 'ds'),
                'details' => sprintf(__('Could not reach license service: %s', 'ds'), $res->get_error_message()),
            ];
        }

        $code = (int) wp_remote_retrieve_response_code($res);
        $body = wp_remote_retrieve_body($res);
        $json = json_decode((string) $body, true);
        if ($code < 200 || $code >= 300 || !is_array($json)) {
            $msg = '';
            if (is_array($json) && !empty($json['message'])) $msg = (string) $json['message'];
            if ($msg === '') $msg = __('Invalid response from license service.', 'ds');
            $msg_l = strtolower(trim($msg));
            if ($code === 403 && strpos($msg_l, 'installation is disabled') !== false) {
                return [
                    'state' => 'installation_disabled',
                    'severity' => 'error',
                    'label' => __('Site disabled', 'ds'),
                    'details' => __('This site was disabled in Admin Plane. Re-enable the installation to restore protected actions and updates.', 'ds'),
                ];
            }
            return [
                'state' => 'unavailable',
                'severity' => 'warning',
                'label' => __('Service unavailable', 'ds'),
                'details' => $msg,
            ];
        }

        $status = strtolower(trim((string) ($json['status'] ?? '')));
        $reason = strtolower(trim((string) ($json['reason'] ?? '')));
        $is_pro = !empty($json['is_pro']);
        $allowed = !empty($json['allowed']);
        $plan = strtolower(trim((string) ($json['plan'] ?? '')));
        $trial_total = max(0, (int) ($json['trial_total'] ?? 0));
        $trial_used = max(0, (int) ($json['trial_used'] ?? 0));
        $trial_remaining = max(0, (int) ($json['trial_remaining'] ?? max(0, $trial_total - $trial_used)));
        $trial_enabled = !array_key_exists('trial_enabled', $json) ? true : !empty($json['trial_enabled']);
        $trial_actions_allowed = !array_key_exists('trial_actions_allowed', $json) ? true : !empty($json['trial_actions_allowed']);
        $trial_period_days = max(0, (int) ($json['trial_period_days'] ?? 0));
        $trial_days_remaining = isset($json['trial_days_remaining']) ? max(0, (int) $json['trial_days_remaining']) : null;
        $trial_period_expired = !empty($json['trial_period_expired']);

        if ($status === 'revoked' || $reason === 'license_revoked') {
            return [
                'state' => 'revoked',
                'severity' => 'error',
                'label' => __('Revoked', 'ds'),
                'details' => __('License is revoked. Pro features and updates are disabled.', 'ds'),
            ];
        }

        if ($status === 'expired' || $reason === 'license_expired') {
            return [
                'state' => 'expired',
                'severity' => 'error',
                'label' => __('Expired', 'ds'),
                'details' => __('License is expired. Renew to re-enable Pro features and updates.', 'ds'),
            ];
        }

        if ($status === 'not_found' || $reason === 'license_not_found') {
            return [
                'state' => 'not_found',
                'severity' => 'warning',
                'label' => __('Not found', 'ds'),
                'details' => __('License key was not found in Admin Plane.', 'ds'),
            ];
        }

        if ($is_pro && $allowed) {
            return [
                'state' => 'active',
                'severity' => 'success',
                'label' => __('Active (Pro)', 'ds'),
                'details' => $plan !== '' ? sprintf(__('Plan: %s', 'ds'), strtoupper($plan)) : __('License is active and valid.', 'ds'),
            ];
        }

        if ($status === 'trial' || ($allowed && !$is_pro)) {
            if (!$trial_enabled) {
                return [
                    'state' => 'trial_disabled',
                    'severity' => 'warning',
                    'label' => __('Trial disabled', 'ds'),
                    'details' => __('Trial is disabled by Admin Plane policy. Assign or upgrade to a Pro license to continue protected actions.', 'ds'),
                ];
            }
            if ($trial_period_expired) {
                return [
                    'state' => 'trial_period_expired',
                    'severity' => 'warning',
                    'label' => __('Trial period expired', 'ds'),
                    'details' => __('Trial period has expired. Upgrade to Pro to continue protected actions.', 'ds'),
                ];
            }
            if (!$trial_actions_allowed || $trial_remaining <= 0) {
                return [
                    'state' => 'trial_exhausted',
                    'severity' => 'warning',
                    'label' => __('Trial exhausted', 'ds'),
                    'details' => __('Trial limit reached. Upgrade to Pro to continue protected actions.', 'ds'),
                ];
            }
            if ($trial_total > 0) {
                $details = sprintf(
                    __('Trial usage: %1$d/%2$d used (%3$d remaining).', 'ds'),
                    $trial_used,
                    $trial_total,
                    $trial_remaining
                );
                if ($trial_period_days > 0 && $trial_days_remaining !== null) {
                    $details .= ' ' . sprintf(
                        __('Time remaining: %1$d day(s) of %2$d.', 'ds'),
                        $trial_days_remaining,
                        $trial_period_days
                    );
                }
                return [
                    'state' => 'trial',
                    'severity' => 'warning',
                    'label' => __('Active (Trial/Basic)', 'ds'),
                    'details' => $details,
                ];
            }
            return [
                'state' => 'trial',
                'severity' => 'warning',
                'label' => __('Active (Trial/Basic)', 'ds'),
                'details' => __('License is valid but Pro entitlements are not active.', 'ds'),
            ];
        }

        if (!$allowed) {
            return [
                'state' => 'restricted',
                'severity' => 'warning',
                'label' => __('Restricted', 'ds'),
                'details' => __('License policy currently blocks protected actions.', 'ds'),
            ];
        }

        return [
            'state' => 'connected',
            'severity' => 'info',
            'label' => __('Connected', 'ds'),
            'details' => __('License service is reachable.', 'ds'),
        ];
    }

    private static function license_check_endpoint($raw) {
        $u = rtrim((string) $raw, '/');
        if ($u === '') return '';
        if (preg_match('#/v1/license/(check|activate|heartbeat)$#i', $u)) {
            $u = preg_replace('#/(check|activate|heartbeat)$#i', '/check', $u);
            return (string) $u;
        }
        if (preg_match('#/v1$#i', $u)) return $u . '/license/check';
        return $u . '/v1/license/check';
    }

    private static function license_site_domain() {
        $host = wp_parse_url(home_url('/'), PHP_URL_HOST);
        return is_string($host) ? strtolower($host) : '';
    }

    private static function license_instance_id() {
        $k = 'ds_protection_instance_id';
        $v = (string) get_option($k, '');
        if ($v !== '') return sanitize_key($v);
        $raw = function_exists('wp_generate_uuid4') ? wp_generate_uuid4() : uniqid('ds', true);
        $id = sanitize_key(str_replace('-', '', (string) $raw));
        if ($id === '') $id = md5(home_url('/') . '|' . wp_salt('auth'));
        update_option($k, $id, false);
        return $id;
    }

    private static function license_settings_url() {
        return admin_url('admin.php?page=ds-settings#ds-license');
    }

    private static function license_refresh_url($return_url = '') {
        if (!is_string($return_url) || $return_url === '') {
            $return_url = self::license_settings_url();
        }
        return wp_nonce_url(
            add_query_arg([
                'ds_license_refresh' => '1',
                'ds_license_return' => $return_url,
            ], admin_url('admin.php')),
            'ds_license_refresh'
        );
    }

    private static function license_retry_bootstrap_url($return_url = '') {
        if (!is_string($return_url) || $return_url === '') {
            $return_url = self::license_settings_url();
        }
        return wp_nonce_url(
            add_query_arg([
                'ds_license_retry_bootstrap' => '1',
                'ds_license_return' => $return_url,
            ], admin_url('admin.php')),
            'ds_license_retry_bootstrap'
        );
    }

    private static function license_upgrade_url($return_url = '') {
        if (!is_string($return_url) || $return_url === '') {
            $return_url = self::license_settings_url();
        }
        return wp_nonce_url(
            add_query_arg([
                'ds_license_upgrade' => '1',
                'ds_license_return' => $return_url,
            ], admin_url('admin.php')),
            'ds_license_upgrade'
        );
    }

    private static function checkout_endpoint_url($api_url) {
        $u = rtrim(trim((string) $api_url), '/');
        if ($u === '') $u = 'https://license.lorandit.com/v1';
        if (preg_match('#/v1/billing/lemonsqueezy/checkout$#i', $u)) return $u;
        if (preg_match('#/v1$#i', $u)) return $u . '/billing/lemonsqueezy/checkout';
        return $u . '/v1/billing/lemonsqueezy/checkout';
    }

    private static function is_safe_checkout_url($url) {
        $u = trim((string) $url);
        if ($u === '' || stripos($u, 'https://') !== 0) return false;
        $host = strtolower((string) wp_parse_url($u, PHP_URL_HOST));
        if ($host === '') return false;
        return (bool) preg_match('/(^|\.)lemonsqueezy\.com$/', $host);
    }

    private static function request_license_upgrade_checkout() {
        $opts = self::get();
        $api_url = trim((string) ($opts['protection_license_api_url'] ?? 'https://license.lorandit.com/v1'));
        $license_key = trim((string) ($opts['protection_license_key'] ?? ''));
        $endpoint = self::checkout_endpoint_url($api_url);
        $payload = [
            'plan' => 'pro',
            'source' => 'wp_plugin_settings',
            'license_key' => $license_key,
            'instance_id' => self::license_instance_id(),
            'domain' => self::license_site_domain(),
            'site_url' => home_url('/'),
            'page_url' => self::license_settings_url(),
            'landing_path' => '/wp-admin/admin.php?page=ds-settings',
        ];

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

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

        $code = (int) wp_remote_retrieve_response_code($response);
        $body = json_decode((string) wp_remote_retrieve_body($response), true);
        if ($code < 200 || $code >= 300 || empty($body['checkout_url'])) {
            return new \WP_Error('ds_checkout_failed', __('Could not open checkout. Please try again later.', 'ds'));
        }

        $checkout_url = (string) $body['checkout_url'];
        if (!self::is_safe_checkout_url($checkout_url)) {
            return new \WP_Error('ds_checkout_url_invalid', __('Checkout URL is invalid.', 'ds'));
        }

        return $checkout_url;
    }

    public static function handle_admin_actions() {
        if (!is_admin() || !current_user_can('manage_options')) return;

        // Background bootstrap on admin load (non-blocking).
        self::maybe_auto_bootstrap_license(false);

        if (isset($_GET['ds_license_retry_bootstrap'])) {
            check_admin_referer('ds_license_retry_bootstrap');
            self::update_bootstrap_meta([
                'state' => 'initializing',
                'attempts' => 0,
                'next_attempt_at' => 0,
                'last_error_code' => '',
                'last_error_message' => '',
            ]);
            self::maybe_auto_bootstrap_license(true);
            self::flush_license_status_cache();

            $return = isset($_GET['ds_license_return']) ? (string) wp_unslash($_GET['ds_license_return']) : '';
            if (!is_string($return) || $return === '') {
                $return = self::license_settings_url();
            }
            $admin_base = admin_url();
            if (strpos($return, $admin_base) !== 0) {
                $return = self::license_settings_url();
            }
            $return = remove_query_arg(['ds_license_retry_bootstrap', 'ds_license_return', '_wpnonce', 'ds_license_bootstrap_retried'], $return);
            $return = add_query_arg(['ds_license_bootstrap_retried' => '1'], $return);
            wp_safe_redirect($return);
            exit;
        }

        if (isset($_GET['ds_license_upgrade'])) {
            check_admin_referer('ds_license_upgrade');
            $checkout_url = self::request_license_upgrade_checkout();
            if (!is_wp_error($checkout_url)) {
                wp_redirect($checkout_url);
                exit;
            }

            $return = isset($_GET['ds_license_return']) ? (string) wp_unslash($_GET['ds_license_return']) : '';
            if (!is_string($return) || $return === '') {
                $return = self::license_settings_url();
            }
            $admin_base = admin_url();
            if (strpos($return, $admin_base) !== 0) {
                $return = self::license_settings_url();
            }
            $return = remove_query_arg(['ds_license_upgrade', 'ds_license_return', '_wpnonce', 'ds_license_upgrade_error'], $return);
            $return = add_query_arg(['ds_license_upgrade_error' => '1'], $return);
            wp_safe_redirect($return);
            exit;
        }

        if (!isset($_GET['ds_license_refresh'])) return;

        check_admin_referer('ds_license_refresh');

        self::flush_license_status_cache();
        self::license_status_state(self::get(), true);

        $return = isset($_GET['ds_license_return']) ? (string) wp_unslash($_GET['ds_license_return']) : '';
        if (!is_string($return) || $return === '') {
            $return = self::license_settings_url();
        }
        $admin_base = admin_url();
        if (strpos($return, $admin_base) !== 0) {
            $return = self::license_settings_url();
        }
        $return = remove_query_arg(['ds_license_refresh', 'ds_license_return', '_wpnonce', 'ds_license_refreshed'], $return);
        $return = add_query_arg(['ds_license_refreshed' => '1'], $return);
        wp_safe_redirect($return);
        exit;
    }

    private static function maybe_auto_bootstrap_license($force = false) {
        $opts = self::get();
        $license_key = trim((string) ($opts['protection_license_key'] ?? ''));
        if ($license_key !== '') {
            self::update_bootstrap_meta([
                'state' => 'connected',
                'attempts' => 0,
                'next_attempt_at' => 0,
                'last_success_at' => time(),
                'last_error_code' => '',
                'last_error_message' => '',
            ]);
            return true;
        }

        $autofill = !isset($opts['protection_bootstrap_autofill']) ? 1 : (int) $opts['protection_bootstrap_autofill'];
        if ($autofill !== 1) return false;

        $api_url = trim((string) ($opts['protection_license_api_url'] ?? ''));
        if ($api_url === '') return false;
        $install_token = (string) Protection::resolve_install_token($opts);
        $legacy_bootstrap_token = trim((string) ($opts['protection_license_bootstrap_token'] ?? ''));
        if ($install_token === '' && $legacy_bootstrap_token === '') {
            self::update_bootstrap_meta([
                'state' => 'reconnect_required',
                'last_error_code' => 'missing_install_token',
                'last_error_message' => __('Install token is missing.', 'ds'),
                'next_attempt_at' => 0,
            ]);
            self::flush_license_status_cache();
            return false;
        }

        $meta = self::bootstrap_meta();
        $now = time();
        $next_attempt_at = max(0, (int) ($meta['next_attempt_at'] ?? 0));
        if (!$force && $next_attempt_at > $now) return false;
        $attempts = max(0, (int) ($meta['attempts'] ?? 0)) + 1;
        self::update_bootstrap_meta([
            'state' => 'initializing',
            'attempts' => $attempts,
            'last_attempt_at' => $now,
        ]);

        $boot = Protection::ensure_bootstrap_license_key($opts);
        if (is_wp_error($boot) || !is_array($boot)) {
            $reason = '';
            if (is_wp_error($boot)) {
                $edata = $boot->get_error_data();
                if (is_array($edata)) {
                    $body = isset($edata['body']) && is_array($edata['body']) ? $edata['body'] : [];
                    $reason = sanitize_key((string) ($body['reason'] ?? ''));
                }
            }
            $code = $reason !== '' ? $reason : (is_wp_error($boot) ? sanitize_key((string) $boot->get_error_code()) : 'bootstrap_failed');
            $message = is_wp_error($boot) ? sanitize_text_field((string) $boot->get_error_message()) : __('License bootstrap failed.', 'ds');
            $permanent_codes = [
                'ds_bootstrap_install_missing_token',
                'ds_bootstrap_missing_key',
                'token_invalid',
                'token_expired',
                'token_revoked',
                'token_already_used',
            ];
            if (in_array($code, $permanent_codes, true)) {
                self::update_bootstrap_meta([
                    'state' => 'reconnect_required',
                    'last_error_code' => $code,
                    'last_error_message' => $message,
                    'next_attempt_at' => 0,
                ]);
            } else {
                $delay = self::next_bootstrap_delay_seconds($attempts);
                self::update_bootstrap_meta([
                    'state' => 'initializing',
                    'last_error_code' => $code,
                    'last_error_message' => $message,
                    'next_attempt_at' => $now + $delay,
                ]);
            }
            self::flush_license_status_cache();
            return false;
        }

        self::update_bootstrap_meta([
            'state' => 'connected',
            'attempts' => 0,
            'next_attempt_at' => 0,
            'last_success_at' => time(),
            'last_error_code' => '',
            'last_error_message' => '',
        ]);

        self::flush_license_status_cache();
        return true;
    }

    public static function render_license_admin_notice() {
        if (!is_admin() || !current_user_can('manage_options')) return;

        $state = self::license_status_state(self::get());
        $kind = (string) ($state['state'] ?? '');
        $details = trim((string) ($state['details'] ?? ''));
        $label = trim((string) ($state['label'] ?? ''));
        $did_refresh = isset($_GET['ds_license_refreshed']) && (string) $_GET['ds_license_refreshed'] === '1';
        $did_retry = isset($_GET['ds_license_bootstrap_retried']) && (string) $_GET['ds_license_bootstrap_retried'] === '1';
        $did_upgrade_error = isset($_GET['ds_license_upgrade_error']) && (string) $_GET['ds_license_upgrade_error'] === '1';

        if ($did_refresh) {
            $refresh_class = ($kind === 'revoked' || $kind === 'expired' || $kind === 'not_found' || $kind === 'unavailable' || $kind === 'installation_disabled' || $kind === 'trial_exhausted' || $kind === 'trial_disabled' || $kind === 'trial_period_expired' || $kind === 'reconnect_required')
                ? 'notice notice-warning is-dismissible'
                : 'notice notice-success is-dismissible';
            $refresh_text = __('License status refreshed.', 'ds');
            if ($label !== '') {
                $refresh_text .= ' ' . sprintf(__('Current status: %s', 'ds'), $label);
            }
            echo '<div class="' . esc_attr($refresh_class) . '"><p>' . esc_html($refresh_text) . '</p></div>';
        }

        if ($did_retry) {
            echo '<div class="notice notice-info is-dismissible"><p>' . esc_html__('Bootstrap retry triggered. Status will update after the next check.', 'ds') . '</p></div>';
        }

        if ($did_upgrade_error) {
            echo '<div class="notice notice-error is-dismissible"><p>' . esc_html__('Could not open checkout. Please try again later.', 'ds') . '</p></div>';
        }

        $show_notice_kinds = [
            'revoked',
            'expired',
            'not_found',
            'unavailable',
            'installation_disabled',
            'trial_exhausted',
            'trial_disabled',
            'trial_period_expired',
            'trial',
            'restricted',
            'initializing',
            'reconnect_required',
        ];
        if (!in_array($kind, $show_notice_kinds, true)) {
            return;
        }

        $class = ($kind === 'revoked' || $kind === 'expired' || $kind === 'installation_disabled')
            ? 'notice notice-error'
            : (($kind === 'trial' || $kind === 'initializing') ? 'notice notice-info' : 'notice notice-warning');
        $settings_url = self::license_settings_url();
        $refresh_url = self::license_refresh_url($settings_url);
        $retry_url = self::license_retry_bootstrap_url($settings_url);
        $upgrade_url = self::license_upgrade_url($settings_url);
        $can_upgrade = self::license_can_upgrade($state);

        $headline = __('Design Studio License requires attention.', 'ds');
        if ($kind === 'revoked' || $kind === 'expired' || $kind === 'installation_disabled') {
            $headline = __('Pro features and updates are currently disabled.', 'ds');
        } elseif ($kind === 'trial_exhausted' || $kind === 'trial_disabled' || $kind === 'trial_period_expired') {
            $headline = __('Protected actions are blocked until license policy is updated.', 'ds');
        } elseif ($kind === 'trial') {
            $headline = __('Site is using trial/basic policy.', 'ds');
        } elseif ($kind === 'initializing') {
            $headline = __('License is initializing in background.', 'ds');
        } elseif ($kind === 'reconnect_required') {
            $headline = __('License onboarding needs reconnection.', 'ds');
        }

        echo '<div class="' . esc_attr($class) . '">';
        echo '<p><strong>' . esc_html__('Design Studio License:', 'ds') . '</strong> ' . esc_html($headline) . '</p>';
        if ($details !== '') {
            echo '<p>' . esc_html($details) . '</p>';
        }
        echo '<p>';
        echo '<a class="button button-primary" href="' . esc_url($settings_url) . '">' . esc_html__('Open License Settings', 'ds') . '</a> ';
        echo '<a class="button" href="' . esc_url($refresh_url) . '">' . esc_html__('Re-check now', 'ds') . '</a> ';
        echo '<a class="button" href="' . esc_url($retry_url) . '">' . esc_html__('Retry bootstrap', 'ds') . '</a> ';
        if ($can_upgrade) {
            echo '<a class="button button-primary" href="' . esc_url($upgrade_url) . '">' . esc_html__('Upgrade to Pro', 'ds') . '</a>';
        }
        echo '</p>';
        echo '</div>';
    }

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

        self::save_post();
        settings_errors('ds_settings_messages');

        $opts = self::get();
        $license_state = self::license_status_state($opts);
        $license_status_text = self::license_status_text($opts);
        $license_status_details = trim((string) ($license_state['details'] ?? ''));
        $bootstrap_meta = self::bootstrap_meta();
        $license_instance_id = self::license_instance_id();
        $license_domain = self::license_site_domain();
        $bootstrap_last_success_at = max(0, (int) ($bootstrap_meta['last_success_at'] ?? 0));
        $bootstrap_next_attempt_at = max(0, (int) ($bootstrap_meta['next_attempt_at'] ?? 0));
        $bootstrap_attempts = max(0, (int) ($bootstrap_meta['attempts'] ?? 0));
        $license_retry_url = self::license_retry_bootstrap_url(self::license_settings_url());
        $license_refresh_url = self::license_refresh_url(self::license_settings_url());
        $license_upgrade_url = self::license_upgrade_url(self::license_settings_url());
        $license_can_upgrade = self::license_can_upgrade($license_state);
        $appearance_system_presets = self::appearance_system_presets();
        $appearance_custom_presets = self::decode_custom_appearance_presets((string) ($opts['ui_custom_presets_json'] ?? ''));
        $ui_text_font_choices = self::ui_text_font_choices();
        $ui_active_preset = (string) ($opts['ui_active_preset'] ?? 'system:lorandit_teal');
        if ($ui_active_preset === '') $ui_active_preset = 'system:lorandit_teal';
        $available_preset_ids = array_keys($appearance_system_presets);
        foreach ($appearance_custom_presets as $preset_item) {
            if (!empty($preset_item['id'])) $available_preset_ids[] = (string) $preset_item['id'];
        }
        if (!in_array($ui_active_preset, $available_preset_ids, true)) $ui_active_preset = 'system:lorandit_teal';

        self::render_admin_styles();
        ?>
        <div class="wrap ds-admin-shell">
            <div class="ds-admin-intro">
                <div>
                    <h1 style="margin:0;"><?php _e('Lorandit DS Pro Settings', 'ds'); ?></h1>
                    <p><?php _e('Central place for license, security, branding, libraries and appearance settings.', 'ds'); ?></p>
                </div>
                <span class="ds-admin-pill"><?php echo esc_html($license_status_text); ?></span>
            </div>

            <div class="ds-admin-quick">
                <a href="<?php echo esc_url(admin_url('edit.php?post_type=ds_template')); ?>">
                    <?php _e('Templates', 'ds'); ?>
                    <small><?php _e('Create and manage design templates.', 'ds'); ?></small>
                </a>
                <a href="<?php echo esc_url(admin_url('edit.php?post_type=ds_design')); ?>">
                    <?php _e('Designs', 'ds'); ?>
                    <small><?php _e('Browse saved customer designs.', 'ds'); ?></small>
                </a>
                <a href="<?php echo esc_url(admin_url('edit.php?post_type=ds_design&ds_printed=0')); ?>">
                    <?php _e('Print Queue', 'ds'); ?>
                    <small><?php _e('Open unprinted items directly.', 'ds'); ?></small>
                </a>
            </div>

            <form method="post" enctype="multipart/form-data">
                <?php wp_nonce_field('ds_settings_save'); ?>
                <input type="hidden" name="ds_action" value="save_settings" />

                <div class="ds-admin-layout">
                    <nav class="ds-admin-side" aria-label="Settings sections">
                        <a href="#ds-license"><?php _e('License', 'ds'); ?></a>
                        <a href="#ds-security"><?php _e('Security', 'ds'); ?></a>
                        <a href="#ds-libraries"><?php _e('Libraries', 'ds'); ?></a>
                        <a href="#ds-branding"><?php _e('Branding', 'ds'); ?></a>
                        <a href="#ds-appearance"><?php _e('Appearance', 'ds'); ?></a>
                    </nav>

                    <div class="ds-admin-main">
                        <section class="ds-admin-card" id="ds-license">
                            <h2><?php _e('License', 'ds'); ?></h2>
                            <div class="ds-grid-2">
                                <div class="ds-field">
                                    <label><?php _e('License Status', 'ds'); ?></label>
                                    <div><strong><?php echo esc_html($license_status_text); ?></strong></div>
                                    <?php if ($license_status_details !== ''): ?>
                                        <p class="description"><?php echo esc_html($license_status_details); ?></p>
                                    <?php endif; ?>
                                    <div class="description" style="margin-top:8px;">
                                        <strong><?php _e('Domain:', 'ds'); ?></strong>
                                        <?php echo esc_html($license_domain !== '' ? $license_domain : '-'); ?>
                                    </div>
                                    <div class="description">
                                        <strong><?php _e('Instance ID:', 'ds'); ?></strong>
                                        <code><?php echo esc_html($license_instance_id); ?></code>
                                    </div>
                                    <p class="description"><?php _e('License is managed by Admin Plane. Manual key editing is disabled in this panel.', 'ds'); ?></p>
                                </div>
                                <div class="ds-field">
                                    <?php
                                    $last_sync_text = $bootstrap_last_success_at > 0
                                        ? wp_date('Y-m-d H:i:s', $bootstrap_last_success_at)
                                        : __('Never', 'ds');
                                    $retry_state_text = ucwords(str_replace('_', ' ', sanitize_key((string) ($bootstrap_meta['state'] ?? 'idle'))));
                                    if ($retry_state_text === '') $retry_state_text = __('Idle', 'ds');
                                    $next_retry_text = __('None scheduled', 'ds');
                                    if ($bootstrap_next_attempt_at > time()) {
                                        $next_retry_text = sprintf(
                                            __('In ~%s (%s)', 'ds'),
                                            human_time_diff(time(), $bootstrap_next_attempt_at),
                                            wp_date('Y-m-d H:i:s', $bootstrap_next_attempt_at)
                                        );
                                    } elseif ($bootstrap_next_attempt_at > 0) {
                                        $next_retry_text = wp_date('Y-m-d H:i:s', $bootstrap_next_attempt_at);
                                    }
                                    ?>
                                    <label><?php _e('Bootstrap State', 'ds'); ?></label>
                                    <div class="description"><strong><?php _e('State:', 'ds'); ?></strong> <?php echo esc_html($retry_state_text); ?></div>
                                    <div class="description"><strong><?php _e('Attempts:', 'ds'); ?></strong> <?php echo esc_html((string) $bootstrap_attempts); ?></div>
                                    <div class="description"><strong><?php _e('Last sync:', 'ds'); ?></strong> <?php echo esc_html($last_sync_text); ?></div>
                                    <div class="description"><strong><?php _e('Next retry:', 'ds'); ?></strong> <?php echo esc_html($next_retry_text); ?></div>
                                    <?php if (!empty($bootstrap_meta['last_error_message'])): ?>
                                        <p class="description"><strong><?php _e('Last error:', 'ds'); ?></strong> <?php echo esc_html((string) $bootstrap_meta['last_error_message']); ?></p>
                                    <?php endif; ?>
                                    <p style="margin-top:10px;">
                                        <a class="button" href="<?php echo esc_url($license_refresh_url); ?>"><?php _e('Refresh status', 'ds'); ?></a>
                                        <a class="button button-secondary" href="<?php echo esc_url($license_retry_url); ?>"><?php _e('Retry bootstrap', 'ds'); ?></a>
                                        <?php if ($license_can_upgrade): ?>
                                            <a class="button button-primary" href="<?php echo esc_url($license_upgrade_url); ?>"><?php _e('Upgrade to Pro', 'ds'); ?></a>
                                        <?php endif; ?>
                                    </p>
                                    <p class="description"><?php _e('Security policy is managed centrally from Admin Plane. Local protection overrides are disabled.', 'ds'); ?></p>
                                </div>
                            </div>
                        </section>

                        <section class="ds-admin-card" id="ds-security">
                            <h2><?php _e('Security', 'ds'); ?></h2>
                            <div class="ds-field" style="margin-bottom:12px;">
                                <label>
                                    <input type="checkbox" name="antispam_enabled" value="1" <?php checked($opts['antispam_enabled']); ?> />
                                    <?php _e('Enable anti-spam (rate limit + origin checks)', 'ds'); ?>
                                </label>
                            </div>
                            <div class="ds-grid-2">
                                <div class="ds-field">
                                    <label for="interval_sec"><?php _e('Min interval (seconds)', 'ds'); ?></label>
                                    <input type="number" id="interval_sec" name="interval_sec" min="0" value="<?php echo esc_attr($opts['interval_sec']); ?>" />
                                    <p class="description"><?php _e('Minimal spacing between saves per IP.', 'ds'); ?></p>
                                </div>
                                <div class="ds-field">
                                    <label for="hour_limit"><?php _e('Hourly limit (per IP)', 'ds'); ?></label>
                                    <input type="number" id="hour_limit" name="hour_limit" min="0" value="<?php echo esc_attr($opts['hour_limit']); ?>" />
                                    <p class="description"><?php _e('0 disables the hourly cap.', 'ds'); ?></p>
                                </div>
                            </div>
                            <div class="ds-field" style="margin-top:12px;">
                                <label for="allowed_origins"><?php _e('Allowed origins (optional)', 'ds'); ?></label>
                                <textarea id="allowed_origins" name="allowed_origins" rows="3" placeholder="https://example.com, https://studio.example.com"><?php echo esc_textarea($opts['allowed_origins']); ?></textarea>
                                <p class="description"><?php _e('Comma-separated list of allowed Origins. If empty, only the site origin is allowed.', 'ds'); ?></p>
                            </div>
                            <div class="ds-field" style="margin-top:12px;">
                                <label for="removebg_api_key"><?php _e('AI Background Remove API Key', 'ds'); ?></label>
                                <input type="text" id="removebg_api_key" name="removebg_api_key" value="<?php echo esc_attr($opts['removebg_api_key']); ?>" autocomplete="off" />
                                <p class="description"><?php _e('remove.bg API key used by the editor action "AI Background Remove".', 'ds'); ?></p>
                            </div>
                        </section>

                        <section class="ds-admin-card" id="ds-libraries">
                            <h2><?php _e('Libraries', 'ds'); ?></h2>
                            <div class="ds-field" style="margin-bottom:12px;">
                                <label for="texture_library_urls"><?php _e('Texture Library URLs', 'ds'); ?></label>
                                <textarea id="texture_library_urls" name="texture_library_urls" rows="5" placeholder="https://example.com/texture1.png&#10;https://example.com/texture2.jpg"><?php echo esc_textarea($opts['texture_library_urls']); ?></textarea>
                                <p class="description"><?php _e('One image URL per line. Added to the texture library in the editor.', 'ds'); ?></p>
                            </div>
                            <div class="ds-field">
                                <label for="frame_library_urls"><?php _e('Frame Library URLs', 'ds'); ?></label>
                                <textarea id="frame_library_urls" name="frame_library_urls" rows="5" placeholder="https://example.com/frame1.png&#10;https://example.com/frame2.png"><?php echo esc_textarea($opts['frame_library_urls']); ?></textarea>
                                <p class="description"><?php _e('One transparent PNG/SVG URL per line. Added to the frame library in the editor.', 'ds'); ?></p>
                            </div>
                            <div class="ds-grid-2" style="margin-top:12px;">
                                <div class="ds-field">
                                    <label><?php _e('Custom Icon Groups', 'ds'); ?></label>
                                    <div class="ds-pack-upload-row">
                                        <input type="file" id="custom_icon_groups_pack" name="custom_icon_groups_pack" accept=".json,.zip,application/json,application/zip,application/x-zip-compressed" />
                                        <p class="description"><?php _e('Import a JSON or ZIP pack. ZIP can include one or more JSON files. Icon entries should use internal icon keys (for example: camera, star, heart).', 'ds'); ?></p>
                                        <div class="ds-pack-download-links">
                                            <a class="button button-small" href="<?php echo esc_url(self::sample_asset_pack_download_url('icons', 'json')); ?>"><?php _e('Download Sample JSON', 'ds'); ?></a>
                                            <a class="button button-small" href="<?php echo esc_url(self::sample_asset_pack_download_url('icons', 'zip')); ?>"><?php _e('Download Sample ZIP', 'ds'); ?></a>
                                            <a class="button button-small" href="<?php echo esc_url(self::asset_pack_download_url('icons', 'json', 'current')); ?>"><?php _e('Export Current JSON', 'ds'); ?></a>
                                            <a class="button button-small" href="<?php echo esc_url(self::asset_pack_download_url('icons', 'zip', 'current')); ?>"><?php _e('Export Current ZIP', 'ds'); ?></a>
                                        </div>
                                    </div>
                                    <?php self::render_custom_asset_builder('icons', 'custom_icon_groups_json', 'custom_icon_groups_json', (string) ($opts['custom_icon_groups_json'] ?? '')); ?>
                                </div>
                                <div class="ds-field">
                                    <label><?php _e('Custom Emoticon Groups', 'ds'); ?></label>
                                    <div class="ds-pack-upload-row">
                                        <input type="file" id="custom_emoticon_groups_pack" name="custom_emoticon_groups_pack" accept=".json,.zip,application/json,application/zip,application/x-zip-compressed" />
                                        <p class="description"><?php _e('Import a JSON or ZIP pack. ZIP can include one or more JSON files.', 'ds'); ?></p>
                                        <div class="ds-pack-download-links">
                                            <a class="button button-small" href="<?php echo esc_url(self::sample_asset_pack_download_url('emoticons', 'json')); ?>"><?php _e('Download Sample JSON', 'ds'); ?></a>
                                            <a class="button button-small" href="<?php echo esc_url(self::sample_asset_pack_download_url('emoticons', 'zip')); ?>"><?php _e('Download Sample ZIP', 'ds'); ?></a>
                                            <a class="button button-small" href="<?php echo esc_url(self::asset_pack_download_url('emoticons', 'json', 'current')); ?>"><?php _e('Export Current JSON', 'ds'); ?></a>
                                            <a class="button button-small" href="<?php echo esc_url(self::asset_pack_download_url('emoticons', 'zip', 'current')); ?>"><?php _e('Export Current ZIP', 'ds'); ?></a>
                                        </div>
                                    </div>
                                    <?php self::render_custom_asset_builder('emoticons', 'custom_emoticon_groups_json', 'custom_emoticon_groups_json', (string) ($opts['custom_emoticon_groups_json'] ?? '')); ?>
                                </div>
                            </div>
                        </section>

                        <section class="ds-admin-card" id="ds-branding">
                            <h2><?php _e('Branding', 'ds'); ?></h2>
                            <div class="ds-field" style="margin-bottom:12px;">
                                <label>
                                    <input type="checkbox" name="brand_header_enabled" value="1" <?php checked(!empty($opts['brand_header_enabled'])); ?> />
                                    <?php _e('Show fullscreen header', 'ds'); ?>
                                </label>
                            </div>
                            <div class="ds-grid-2">
                                <div class="ds-field">
                                    <label for="brand_header_mode"><?php _e('Fullscreen Header Mode', 'ds'); ?></label>
                                    <select id="brand_header_mode" name="brand_header_mode">
                                        <option value="logo" <?php selected($opts['brand_header_mode'], 'logo'); ?>><?php _e('Logo', 'ds'); ?></option>
                                        <option value="text" <?php selected($opts['brand_header_mode'], 'text'); ?>><?php _e('Text title', 'ds'); ?></option>
                                    </select>
                                </div>
                                <div class="ds-field">
                                    <label for="brand_subtitle_position"><?php _e('Subtitle Position', 'ds'); ?></label>
                                    <select id="brand_subtitle_position" name="brand_subtitle_position">
                                        <option value="top" <?php selected($opts['brand_subtitle_position'], 'top'); ?>><?php _e('Top', 'ds'); ?></option>
                                        <option value="bottom" <?php selected($opts['brand_subtitle_position'], 'bottom'); ?>><?php _e('Bottom', 'ds'); ?></option>
                                        <option value="left" <?php selected($opts['brand_subtitle_position'], 'left'); ?>><?php _e('Left', 'ds'); ?></option>
                                        <option value="right" <?php selected($opts['brand_subtitle_position'], 'right'); ?>><?php _e('Right', 'ds'); ?></option>
                                    </select>
                                </div>
                            </div>
                            <div class="ds-grid-2" style="margin-top:12px;">
                                <div class="ds-field">
                                    <label for="brand_logo_url"><?php _e('Header Logo URL', 'ds'); ?></label>
                                    <input type="url" id="brand_logo_url" name="brand_logo_url" value="<?php echo esc_attr($opts['brand_logo_url']); ?>" placeholder="https://example.com/logo.png" />
                                </div>
                                <div class="ds-field">
                                    <label for="brand_title_text"><?php _e('Header Title Text', 'ds'); ?></label>
                                    <input type="text" id="brand_title_text" name="brand_title_text" value="<?php echo esc_attr($opts['brand_title_text']); ?>" />
                                </div>
                            </div>
                            <div class="ds-field" style="margin-top:12px;">
                                <label for="brand_subtitle_text"><?php _e('Header Subtitle Text', 'ds'); ?></label>
                                <input type="text" id="brand_subtitle_text" name="brand_subtitle_text" value="<?php echo esc_attr($opts['brand_subtitle_text']); ?>" />
                            </div>
                        </section>

                        <section class="ds-admin-card" id="ds-appearance">
                            <h2><?php _e('Appearance', 'ds'); ?></h2>
                            <div class="ds-grid-2" style="margin-top:12px;">
                                <div class="ds-field">
                                    <label for="ui_active_preset"><?php _e('Appearance Preset', 'ds'); ?></label>
                                    <select id="ui_active_preset" name="ui_active_preset">
                                        <optgroup label="<?php esc_attr_e('System Presets', 'ds'); ?>">
                                            <?php foreach ($appearance_system_presets as $preset_id => $preset_cfg): ?>
                                                <option
                                                    value="<?php echo esc_attr((string) $preset_id); ?>"
                                                    data-preset-colors="<?php echo esc_attr((string) wp_json_encode($preset_cfg['colors'] ?? [])); ?>"
                                                    <?php selected($ui_active_preset, (string) $preset_id); ?>
                                                ><?php echo esc_html((string) ($preset_cfg['label'] ?? $preset_id)); ?></option>
                                            <?php endforeach; ?>
                                        </optgroup>
                                        <?php if (!empty($appearance_custom_presets)): ?>
                                            <optgroup label="<?php esc_attr_e('Saved Presets', 'ds'); ?>">
                                                <?php foreach ($appearance_custom_presets as $preset_cfg): ?>
                                                    <option
                                                        value="<?php echo esc_attr((string) ($preset_cfg['id'] ?? '')); ?>"
                                                        data-preset-colors="<?php echo esc_attr((string) wp_json_encode($preset_cfg['colors'] ?? [])); ?>"
                                                        <?php selected($ui_active_preset, (string) ($preset_cfg['id'] ?? '')); ?>
                                                    ><?php echo esc_html((string) ($preset_cfg['name'] ?? '')); ?></option>
                                                <?php endforeach; ?>
                                            </optgroup>
                                        <?php endif; ?>
                                    </select>
                                    <p class="description"><?php _e('Selecting a preset updates the color pickers instantly.', 'ds'); ?></p>
                                </div>
                                <div class="ds-field">
                                    <label for="ui_save_preset_name"><?php _e('Save Current As Preset', 'ds'); ?></label>
                                    <input type="text" id="ui_save_preset_name" name="ui_save_preset_name" value="" placeholder="<?php esc_attr_e('My Brand Theme', 'ds'); ?>" />
                                    <div class="ds-preset-actions">
                                        <button type="submit" class="button" id="ui-save-preset-btn" name="ui_save_current_preset" value="1"><?php _e('Save Preset', 'ds'); ?></button>
                                    </div>
                                    <p class="description"><?php _e('Saves current color values under the name above.', 'ds'); ?></p>
                                </div>
                            </div>

                            <div class="ds-grid-2" style="margin-top:12px;">
                                <div class="ds-field">
                                    <label for="ui_primary_color"><?php _e('Designer Primary Color', 'ds'); ?></label>
                                    <input type="color" id="ui_primary_color" name="ui_primary_color" value="<?php echo esc_attr($opts['ui_primary_color']); ?>" />
                                </div>
                                <div class="ds-field">
                                    <label for="ui_accent_color"><?php _e('Designer Accent Color', 'ds'); ?></label>
                                    <input type="color" id="ui_accent_color" name="ui_accent_color" value="<?php echo esc_attr($opts['ui_accent_color']); ?>" />
                                </div>
                                <div class="ds-field">
                                    <label for="ui_secondary_accent_color"><?php _e('Designer Secondary Accent Color', 'ds'); ?></label>
                                    <input type="color" id="ui_secondary_accent_color" name="ui_secondary_accent_color" value="<?php echo esc_attr($opts['ui_secondary_accent_color'] ?? '#2f80ed'); ?>" />
                                </div>
                                <div class="ds-field">
                                    <label for="ui_bg_color"><?php _e('Designer Background Color', 'ds'); ?></label>
                                    <input type="color" id="ui_bg_color" name="ui_bg_color" value="<?php echo esc_attr($opts['ui_bg_color']); ?>" />
                                </div>
                                <div class="ds-field">
                                    <label for="ui_text_color"><?php _e('Designer Text Color', 'ds'); ?></label>
                                    <input type="color" id="ui_text_color" name="ui_text_color" value="<?php echo esc_attr($opts['ui_text_color']); ?>" />
                                </div>
                                <div class="ds-field">
                                    <label for="ui_text_font"><?php _e('Designer Text Font', 'ds'); ?></label>
                                    <select id="ui_text_font" name="ui_text_font">
                                        <?php foreach ($ui_text_font_choices as $font_key => $font_cfg): ?>
                                            <option value="<?php echo esc_attr((string) $font_key); ?>" <?php selected((string) ($opts['ui_text_font'] ?? 'segoe_ui'), (string) $font_key); ?>>
                                                <?php echo esc_html((string) ($font_cfg['label'] ?? $font_key)); ?>
                                            </option>
                                        <?php endforeach; ?>
                                    </select>
                                </div>
                                <div class="ds-field">
                                    <label for="ui_panel_color"><?php _e('Panel Surface Color', 'ds'); ?></label>
                                    <input type="color" id="ui_panel_color" name="ui_panel_color" value="<?php echo esc_attr($opts['ui_panel_color'] ?? '#ffffff'); ?>" />
                                </div>
                                <div class="ds-field">
                                    <label for="ui_button_color"><?php _e('Button Surface Color', 'ds'); ?></label>
                                    <input type="color" id="ui_button_color" name="ui_button_color" value="<?php echo esc_attr($opts['ui_button_color'] ?? '#ffffff'); ?>" />
                                </div>
                                <div class="ds-field">
                                    <label for="ui_line_color"><?php _e('Border/Line Color', 'ds'); ?></label>
                                    <input type="color" id="ui_line_color" name="ui_line_color" value="<?php echo esc_attr($opts['ui_line_color'] ?? '#d3dde8'); ?>" />
                                </div>
                                <div class="ds-field">
                                    <label for="ui_muted_color"><?php _e('Muted Text Color', 'ds'); ?></label>
                                    <input type="color" id="ui_muted_color" name="ui_muted_color" value="<?php echo esc_attr($opts['ui_muted_color'] ?? '#5f7188'); ?>" />
                                </div>
                            </div>

                            <div class="ds-grid-2" style="margin-top:12px;">
                                <div class="ds-field">
                                    <label for="ui_classic_palette"><?php _e('Desktop Classic Palette', 'ds'); ?></label>
                                    <select id="ui_classic_palette" name="ui_classic_palette">
                                        <option value="arctic_mint" <?php selected($opts['ui_classic_palette'] ?? 'arctic_mint', 'arctic_mint'); ?>><?php _e('Arctic Mint', 'ds'); ?></option>
                                        <option value="warm_paper" <?php selected($opts['ui_classic_palette'] ?? 'arctic_mint', 'warm_paper'); ?>><?php _e('Warm Paper', 'ds'); ?></option>
                                        <option value="steel_blue" <?php selected($opts['ui_classic_palette'] ?? 'arctic_mint', 'steel_blue'); ?>><?php _e('Steel Blue', 'ds'); ?></option>
                                        <option value="sunset_blush" <?php selected($opts['ui_classic_palette'] ?? 'arctic_mint', 'sunset_blush'); ?>><?php _e('Sunset Blush', 'ds'); ?></option>
                                    </select>
                                    <p class="description"><?php _e('Applies to Desktop Classic workspace gradients only.', 'ds'); ?></p>
                                </div>
                                <div class="ds-field">
                                    <label><?php _e('Preview', 'ds'); ?></label>
                                    <label>
                                        <input type="checkbox" name="preview_hide_white_zone_bg" value="1" <?php checked(!empty($opts['preview_hide_white_zone_bg'])); ?> />
                                        <?php _e('Hide white zone background in preview overlays', 'ds'); ?>
                                    </label>
                                </div>
                            </div>

                        </section>

                        <div class="ds-admin-actions">
                            <?php submit_button(__('Save Settings', 'ds'), 'primary', 'submit', false); ?>
                        </div>
                    </div>
                </div>
            </form>
        </div>
        <?php
        self::render_admin_scripts();
    }
}
