<?php
namespace DS_Plugin;

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

class Frontend {
    private static $forced_assets_printed = false;
    private static $latest_boot_json = '';
    private static function editor_i18n_strings() {
        return [
            'ui' => [
                'choose_template_option' => __('Choose Template', 'ds'),
                'template_fallback' => __('Template #%s', 'ds'),
                'no_templates_found' => __('No templates found.', 'ds'),
                'no_image' => __('No image', 'ds'),
                'selected_template_named' => __('Selected template: %s', 'ds'),
                'selected_template_none' => __('Selected template: none', 'ds'),
                'template_selected_click_next' => __('Template selected. Click Next.', 'ds'),
                'start_choose_template_first' => __('Start Designing: choose a template first.', 'ds'),
                'step1_title' => __('STEP 1: CHOOSE TEMPLATE', 'ds'),
                'step1_help' => __('Select template to continue.', 'ds'),
                'choose_template_first_canvas_load' => __('Choose template first. Canvas will load after template selection.', 'ds'),
                'template_label' => __('Template', 'ds'),
                'canvas' => __('Canvas', 'ds'),
                'insert' => __('Insert', 'ds'),
                'text' => __('Text', 'ds'),
                'image' => __('Image', 'ds'),
                'library' => __('Library', 'ds'),
                'preview' => __('Preview', 'ds'),
                'desktop_classic_sheet_nav' => __('Desktop Classic Sheet Nav', 'ds'),
                'landscape_classic_rail' => __('Landscape Classic Rail', 'ds'),
                'close' => __('Close', 'ds'),
                'edit' => __('Edit', 'ds'),
                'style' => __('Style', 'ds'),
                'arrange' => __('Arrange', 'ds'),
                'unselect' => __('Unselect', 'ds'),
                'start_designing' => __('Start Designing', 'ds'),
                'add_first_element_to_canvas' => __('Add your first element to the canvas.', 'ds'),
                'add_image' => __('Add Image', 'ds'),
                'add_text' => __('Add Text', 'ds'),
                'for_more_visual_elements_go_to_insert_menu' => __('For more visual elements, go to Insert menu.', 'ds'),
                'next' => __('Next', 'ds'),
                'refresh' => __('Refresh', 'ds'),
                'back' => __('Back', 'ds'),
                'undo' => __('Undo', 'ds'),
                'redo' => __('Redo', 'ds'),
                'change_template' => __('Change Template', 'ds'),
                'save' => __('Save', 'ds'),
                'save_design' => __('Save Design', 'ds'),
                'final_save' => __('Final Save', 'ds'),
                'export' => __('Export', 'ds'),
                'apply_to_product' => __('Apply to Product', 'ds'),
                'step2_title' => __('STEP 2: LOAD OR CREATE', 'ds'),
                'step2_help' => __('Load an existing design or create a new one.', 'ds'),
                'create_new_design' => __('Create New Design', 'ds'),
                'load_design' => __('Load Design', 'ds'),
                'step2_load_title' => __('STEP 2: LOAD DESIGN', 'ds'),
                'step2_load_help' => __('Choose a saved design for the selected template.', 'ds'),
                'step3_choose_print_areas' => __('Step 3: Choose Print Areas', 'ds'),
                'step3_open_zone_tools_help' => __('Open Zone Tools and choose print areas.', 'ds'),
                'step3_required_zone_tools' => __('Step 3 required: choose print areas in Zone Tools.', 'ds'),
                'my_designs' => __('My Designs', 'ds'),
                'public_designs' => __('Public Designs', 'ds'),
                'open_zone_tools' => __('Open Zone Tools', 'ds'),
                'zone_tools' => __('Zone Tools', 'ds'),
                'zones' => __('Zones', 'ds'),
                'choose_print_areas' => __('Choose print areas', 'ds'),
                'zone_picker_help' => __('Select the print areas you want to use for this template. Close returns to Step 2.', 'ds'),
                'apply_selection' => __('Apply Selection', 'ds'),
                'choose_at_least_one_print_area' => __('Choose at least one print area.', 'ds'),
                'choose_template_first' => __('Choose template first.', 'ds'),
                'loading' => __('Loading...', 'ds'),
                'no_designs' => __('No designs.', 'ds'),
                'design_fallback' => __('Design #%s', 'ds'),
                'set_zones_count' => __('Set zones: %s', 'ds'),
                'zone_label' => __('Zone %s', 'ds'),
                'use_on_product' => __('Use on product', 'ds'),
                'load' => __('Load', 'ds'),
                'step_status_1' => __('Step 1: Choose template and click Next.', 'ds'),
                'step_status_2' => __('Step 2: Load design or create new.', 'ds'),
                'step_status_2_load' => __('Step 2: Choose a saved design.', 'ds'),
                'choose_print_areas_start_designing' => __('Choose print areas to start designing.', 'ds'),
                'editor_ready' => __('Editor ready.', 'ds'),
                'error_loading_designs' => __('Error loading designs.', 'ds'),
                'this_action' => __('This action', 'ds'),
                'available_registered_users' => __('%s is available only for registered users.%s', 'ds'),
                'please_log_in_continue' => __(' Please log in to continue.', 'ds'),
                'login_required_short' => __(' Login required.', 'ds'),
                'login_required' => __('Login required', 'ds'),
                'log_in' => __('Log in', 'ds'),
                'to_enable_save_export' => __('to enable Save/Export and My Designs.', 'ds'),
                'log_in_enable_save_export' => __('Log in to enable Save/Export and My Designs.', 'ds'),
                'guest_mode_login_required' => __('Guest mode: design tools are enabled. Save/Export require login.', 'ds'),
                'guest_mode_login_required_url' => __('Guest mode: design tools are enabled. Save/Export require login: %s', 'ds'),
                'loaded_design_named' => __('Loaded design: %s', 'ds'),
                'design_selected_for_product' => __('Design "%s" is selected for this product.', 'ds'),
                'my_designs_title' => __('My Designs', 'ds'),
                'load_design_title' => __('Load Design', 'ds'),
                'no_designs_found' => __('No designs found.', 'ds'),
                'different_template' => __('Different template', 'ds'),
                'product_fallback' => __('Product #%s', 'ds'),
                'no_compatible_products' => __('No compatible products found for this template.', 'ds'),
                'open_product' => __('Open Product', 'ds'),
                'error_loading_products' => __('Error loading products.', 'ds'),
                'empty_print_areas' => __('Empty Print Areas', 'ds'),
                'empty_print_areas_help' => __('Selected print areas are empty: %s.', 'ds'),
                'continue_anyway' => __('Continue Anyway', 'ds'),
                'go_to_empty_zone' => __('Go To Empty Zone', 'ds'),
                'remove_empty_zones' => __('Remove Empty Zones', 'ds'),
                'cancel' => __('Cancel', 'ds'),
                'asset_library' => __('Asset Library', 'ds'),
                'textures_library' => __('Textures Library', 'ds'),
                'frames_library' => __('Frames Library', 'ds'),
                'shapes_library' => __('Shapes Library', 'ds'),
                'emoticons_library' => __('Emoticons Library', 'ds'),
                'icons_library' => __('Icons Library', 'ds'),
                'set_as_background' => __('Set as background', 'ds'),
                'no_assets_available' => __('No assets available.', 'ds'),
                'default_group' => __('Default', 'ds'),
                'group_label' => __('Group', 'ds'),
                'icon_label' => __('Icon', 'ds'),
                'emoticon_label' => __('Emoticon', 'ds'),
                'frame_inserted' => __('Frame inserted.', 'ds'),
                'texture_inserted' => __('Texture inserted.', 'ds'),
                'zone_preview_alt' => __('Zone preview', 'ds'),
                'zone_label_single' => __('Zone', 'ds'),
                'mockup_single' => __('Mockup', 'ds'),
                'mockup_numbered' => __('Mockup %s', 'ds'),
                'other_areas' => __('Other Areas', 'ds'),
                'design_visibility_now' => __('Design #%s is now %s.', 'ds'),
                'visibility_shared' => __('Shared', 'ds'),
                'visibility_private' => __('Private', 'ds'),
                'mug_preview_short' => __('Mug', 'ds'),
                'bottle_preview_short' => __('Bottle', 'ds'),
                'tshirt_preview' => __('T-Shirt Preview', 'ds'),
                'bottle_preview' => __('Bottle Preview', 'ds'),
                'mug_preview' => __('Mug Preview', 'ds'),
                'preview_3d' => __('3D Preview', 'ds'),
                'active_mockup' => __('Active mockup', 'ds'),
                'active_canvas' => __('Active Canvas', 'ds'),
                'select' => __('Select', 'ds'),
                'select_layer' => __('Select layer', 'ds'),
                'layer_single' => __('Layer', 'ds'),
                'move_layer_up' => __('Move layer up', 'ds'),
                'move_layer_down' => __('Move layer down', 'ds'),
                'no_layers_yet' => __('No layers yet.', 'ds'),
                'canvas_orientation' => __('Canvas orientation: %s', 'ds'),
                'landscape' => __('Landscape', 'ds'),
                'portrait' => __('Portrait', 'ds'),
                'collapse_preview' => __('Collapse preview', 'ds'),
                'expand_preview' => __('Expand preview', 'ds'),
                'fullscreen' => __('Fullscreen', 'ds'),
                'exit_fullscreen' => __('Exit Fullscreen', 'ds'),
                'quick_actions' => __('Quick Actions', 'ds'),
                'preview_product' => __('Preview product', 'ds'),
                'preview_product_title' => __('Preview Product', 'ds'),
                'showing_active_print_areas' => __('Showing active print areas on selected mockups.', 'ds'),
                'preview_approx_note' => __('Preview is approximate and for print placement guidance only. Final product may differ.', 'ds'),
                'product_preview_after_template_init' => __('Product preview is available after template initialization.', 'ds'),
                'no_active_print_areas_selected' => __('No active print areas selected yet.', 'ds'),
                'no_active_mockups_to_preview' => __('No active mockups to preview.', 'ds'),
                'zone_conflict_pair' => __('%s and %s cannot be used together (conflict rule).', 'ds'),
                'no_print_areas_found' => __('No print areas found.', 'ds'),
                'open_3d_preview' => __('Open 3D Preview', 'ds'),
                'choose_zones' => __('Choose zones...', 'ds'),
                'no_zone' => __('No zone', 'ds'),
                'canvas_preview' => __('Canvas preview', 'ds'),
                'design_saved' => __('Design Saved', 'ds'),
                'what_next_after_save' => __('What do you want to do next?', 'ds'),
                'continue_editing' => __('Continue Editing', 'ds'),
                'open_new_template' => __('Open New Template', 'ds'),
                'design_default_prefix' => __('Design', 'ds'),
                'save_design_first' => __('Save Design First', 'ds'),
                'enter_design_name_then_save_apply' => __('Enter design name, then save and apply to product.', 'ds'),
                'design_name' => __('Design Name', 'ds'),
                'edit_text' => __('Edit Text', 'ds'),
                'apply' => __('Apply', 'ds'),
                'done' => __('Done', 'ds'),
                'exit_designer' => __('Exit Designer', 'ds'),
                'exit_designer_confirm' => __('Exit Designer?', 'ds'),
                'exit_designer_help' => __('Before leaving, choose what you want to do with this design.', 'ds'),
                'exit_without_saving' => __('Exit Without Saving', 'ds'),
                'refresh_designer' => __('Refresh Designer', 'ds'),
                'refresh_designer_confirm' => __('Refresh Designer?', 'ds'),
                'unsaved_changes_will_be_lost' => __('Unsaved changes will be lost.', 'ds'),
                'ok' => __('OK', 'ds'),
                'loading_fonts' => __('Loading fonts...', 'ds'),
                'group' => __('Group', 'ds'),
                'mask' => __('Mask', 'ds'),
                'ungroup' => __('Ungroup', 'ds'),
                'mask_bake' => __('Mask Bake', 'ds'),
                'crop' => __('Crop', 'ds'),
                'ai_background_remove' => __('AI Background Remove', 'ds'),
                'ai_remove_bg' => __('AI Remove BG', 'ds'),
                'rotate' => __('Rotate', 'ds'),
                'duplicate' => __('Duplicate', 'ds'),
                'delete' => __('Delete', 'ds'),
                'crop_cancel' => __('Crop Cancel', 'ds'),
                'fit_canvas' => __('Fit Canvas', 'ds'),
                'zoom_reset_100' => __('100% Zoom', 'ds'),
                'export_png' => __('Export PNG', 'ds'),
                'save_zone' => __('Save Zone', 'ds'),
                'apply_template' => __('Apply Template', 'ds'),
                'close_preview' => __('Close Preview', 'ds'),
                'reset_view' => __('Reset View', 'ds'),
                'object' => __('Object', 'ds'),
                'font' => __('Font', 'ds'),
                'size' => __('Size', 'ds'),
                'text_align' => __('Text Align', 'ds'),
                'left' => __('Left', 'ds'),
                'center' => __('Center', 'ds'),
                'right' => __('Right', 'ds'),
                'fill' => __('Fill', 'ds'),
                'the_color' => __('The color', 'ds'),
                'stroke' => __('Stroke', 'ds'),
                'width' => __('Width', 'ds'),
                'side' => __('Side', 'ds'),
                'inside' => __('Inside', 'ds'),
                'outside' => __('Outside', 'ds'),
                'opacity' => __('Opacity', 'ds'),
                'stroke_width_short' => __('Stroke W', 'ds'),
                'stroke_side' => __('Stroke Side', 'ds'),
                'corners' => __('Corners', 'ds'),
                'layers' => __('Layers', 'ds'),
                'horizontal' => __('Horizontal', 'ds'),
                'vertical' => __('Vertical', 'ds'),
                'align_left' => __('Align Left', 'ds'),
                'align_center' => __('Align Center', 'ds'),
                'align_right' => __('Align Right', 'ds'),
                'align' => __('Align', 'ds'),
                'align_top' => __('Align Top', 'ds'),
                'align_middle' => __('Align Middle', 'ds'),
                'align_bottom' => __('Align Bottom', 'ds'),
                'layer_up' => __('Layer Up', 'ds'),
                'layer_down' => __('Layer Down', 'ds'),
                'bring_front' => __('Bring Front', 'ds'),
                'send_back' => __('Send Back', 'ds'),
                'object_properties' => __('Object Properties', 'ds'),
                'more' => __('More', 'ds'),
                'tool' => __('Tool', 'ds'),
                'added_with_size' => __('Added %s (%sx%s mm).', 'ds'),
                'print_areas_updated_selected' => __('Print areas updated (%s selected).', 'ds'),
                'zone_ready_with_size' => __('Zone ready (%sx%s mm).', 'ds'),
                'zone_saved_locally' => __('Zone saved locally.', 'ds'),
                'placement_moved' => __('Placement moved.', 'ds'),
                'zoom_percent' => __('Zoom: %s%%', 'ds'),
                'apply_crop' => __('Apply Crop', 'ds'),
                'choose_font' => __('Choose font', 'ds'),
                'choose_font_title' => __('Choose Font', 'ds'),
                'print_width_prompt' => __('Print width in mm (max A4)', 'ds'),
                'print_height_prompt' => __('Print height in mm (max A4)', 'ds'),
                'top_options_exclusive' => __('Top options: you can use only one of topLeft, topRight, topCenter.', 'ds'),
                'loading_design' => __('Loading design...', 'ds'),
                'preparing_previews' => __('Preparing previews...', 'ds'),
                'saving_design' => __('Saving design...', 'ds'),
                'saving_zones' => __('Saving zones...', 'ds'),
                'building_previews' => __('Building previews...', 'ds'),
                'uploading_design' => __('Uploading design...', 'ds'),
                'retrying_save' => __('Retrying save...', 'ds'),
                'preparing_save' => __('Preparing save...', 'ds'),
                'request_timeout' => __('Request timeout.', 'ds'),
                'save_failed_status_code' => __('Save failed (%s)', 'ds'),
                'saved_fallback' => __('Saved (fallback): %s', 'ds'),
                'saved_base' => __('Saved: %s', 'ds'),
                'base_zone_pngs_saving' => __('Base zone PNGs: %s/%s | Saving zones...', 'ds'),
                'saved_zone_files' => __('Saved zone files: %s', 'ds'),
                'saved_files_pngs' => __('Saved files: %s/%s | PNGs: %s/%s (inactive: %s)', 'ds'),
                'active_list' => __('Active: %s', 'ds'),
                'saving_zone_count' => __('Saving... (%s zones)', 'ds'),
                'design_saved_successfully' => __('Design saved successfully.', 'ds'),
            ],
            'status' => [
                'Invalid design selection.' => __('Invalid design selection.', 'ds'),
                'Choose template and click Apply.' => __('Choose template and click Apply.', 'ds'),
                'Enter a design name.' => __('Enter a design name.', 'ds'),
                'Trial limit reached. Upgrade to Pro to continue.' => __('Trial limit reached. Upgrade to Pro to continue.', 'ds'),
                'Action blocked by protection.' => __('Action blocked by protection.', 'ds'),
                'Select one object, then click Object.' => __('Select one object, then click Object.', 'ds'),
                'Enter a valid Design ID.' => __('Enter a valid Design ID.', 'ds'),
                'Loading design...' => __('Loading design...', 'ds'),
                'Design JSON not found.' => __('Design JSON not found.', 'ds'),
                'This design belongs to a different template. Select the matching template first.' => __('This design belongs to a different template. Select the matching template first.', 'ds'),
                'Load error.' => __('Load error.', 'ds'),
                'My Designs load error.' => __('My Designs load error.', 'ds'),
                'Load Design error.' => __('Load Design error.', 'ds'),
                'At least one print area must remain selected.' => __('At least one print area must remain selected.', 'ds'),
                'Product list load error.' => __('Product list load error.', 'ds'),
                'Could not load texture.' => __('Could not load texture.', 'ds'),
                'Texture set as canvas background.' => __('Texture set as canvas background.', 'ds'),
                'Could not load asset.' => __('Could not load asset.', 'ds'),
                'Frame inserted.' => __('Frame inserted.', 'ds'),
                'Texture inserted.' => __('Texture inserted.', 'ds'),
                'Visibility update failed.' => __('Visibility update failed.', 'ds'),
                'Decal projection not available for this model. Using plane fallback.' => __('Decal projection not available for this model. Using plane fallback.', 'ds'),
                '3D model loaded (decal mode).' => __('3D model loaded (decal mode).', 'ds'),
                '3D model loaded (mesh-wrap mode).' => __('3D model loaded (mesh-wrap mode).', 'ds'),
                '3D model loaded (plane fallback).' => __('3D model loaded (plane fallback).', 'ds'),
                '3D model load failed. Using fallback.' => __('3D model load failed. Using fallback.', 'ds'),
                '3D model init error. Using fallback.' => __('3D model init error. Using fallback.', 'ds'),
                'Invalid size. Use numeric mm values.' => __('Invalid size. Use numeric mm values.', 'ds'),
                'Max print size is A4 (210x297 mm).' => __('Max print size is A4 (210x297 mm).', 'ds'),
                'No free space for this print area without overlap.' => __('No free space for this print area without overlap.', 'ds'),
                'Choose print areas to start designing.' => __('Choose print areas to start designing.', 'ds'),
                'Select at least one print area to continue.' => __('Select at least one print area to continue.', 'ds'),
                'Select at least one print area.' => __('Select at least one print area.', 'ds'),
                'Zoom: 100% actual.' => __('Zoom: 100% actual.', 'ds'),
                'Select one object first.' => __('Select one object first.', 'ds'),
                'AI service returned invalid image.' => __('AI service returned invalid image.', 'ds'),
                'AI result could not be loaded.' => __('AI result could not be loaded.', 'ds'),
                'AI Background Remove is currently unavailable.' => __('AI Background Remove is currently unavailable.', 'ds'),
                'Select one image first.' => __('Select one image first.', 'ds'),
                'AI Background Remove works only on images.' => __('AI Background Remove works only on images.', 'ds'),
                'REST endpoint is not configured.' => __('REST endpoint is not configured.', 'ds'),
                'Could not prepare image for background remove.' => __('Could not prepare image for background remove.', 'ds'),
                'Removing background...' => __('Removing background...', 'ds'),
                'AI background remove failed.' => __('AI background remove failed.', 'ds'),
                'Crop mode is not active.' => __('Crop mode is not active.', 'ds'),
                'Crop mode: adjust frame, then Apply Crop.' => __('Crop mode: adjust frame, then Apply Crop.', 'ds'),
                'Image cropped.' => __('Image cropped.', 'ds'),
                'Select one image object first.' => __('Select one image object first.', 'ds'),
                'Mask: select image and shape (Shift+click both).' => __('Mask: select image and shape (Shift+click both).', 'ds'),
                'Mask failed.' => __('Mask failed.', 'ds'),
                'Mask bake failed.' => __('Mask bake failed.', 'ds'),
                'Mask baked.' => __('Mask baked.', 'ds'),
                'Mask applied.' => __('Mask applied.', 'ds'),
                'Placement moved.' => __('Placement moved.', 'ds'),
                'Crop mode started. Adjust frame, then Apply Crop.' => __('Crop mode started. Adjust frame, then Apply Crop.', 'ds'),
                'Start Crop on an image first.' => __('Start Crop on an image first.', 'ds'),
                'Text edit mode.' => __('Text edit mode.', 'ds'),
                'Fit by long side.' => __('Fit by long side.', 'ds'),
                'Undo' => __('Undo', 'ds'),
                'Redo' => __('Redo', 'ds'),
                'Save design first, then use Apply to Product.' => __('Save design first, then use Apply to Product.', 'ds'),
                'Local draft is used only for multi-zone templates.' => __('Local draft is used only for multi-zone templates.', 'ds'),
                'No active zone.' => __('No active zone.', 'ds'),
                'Local draft save failed.' => __('Local draft save failed.', 'ds'),
                'Save is already in progress... (wait or retry in a moment)' => __('Save is already in progress... (wait or retry in a moment)', 'ds'),
                'Save timeout. Please try again.' => __('Save timeout. Please try again.', 'ds'),
                'Save failed.' => __('Save failed.', 'ds'),
                'Zone save failed.' => __('Zone save failed.', 'ds'),
                'Save error.' => __('Save error.', 'ds'),
                'Save retry (reduced payload)...' => __('Save retry (reduced payload)...', 'ds'),
                'PNG prepare failed.' => __('PNG prepare failed.', 'ds'),
                'Save in progress. Please wait until all zones are saved.' => __('Save in progress. Please wait until all zones are saved.', 'ds'),
                'Save design first, then apply to product.' => __('Save design first, then apply to product.', 'ds'),
                'Shape inserted.' => __('Shape inserted.', 'ds'),
                'Emoticon inserted.' => __('Emoticon inserted.', 'ds'),
                'Could not insert emoticon.' => __('Could not insert emoticon.', 'ds'),
                'Icon inserted.' => __('Icon inserted.', 'ds'),
                'Could not insert icon.' => __('Could not insert icon.', 'ds'),
                'Editor ready.' => __('Editor ready.', 'ds'),
            ],
        ];
    }
    private static function resolve_ui_text_font_stack($font_key) {
        $choices = Settings::ui_text_font_choices();
        $key = sanitize_key((string) $font_key);
        if (!isset($choices[$key])) $key = 'segoe_ui';
        $stack = isset($choices[$key]['stack']) ? (string) $choices[$key]['stack'] : '"Segoe UI", Tahoma, Arial, sans-serif';
        return [$key, $stack];
    }
    private static function current_asset_version() {
        $candidates = [
            DS_PLUGIN_DIR . 'assets/js/editor.js',
            DS_PLUGIN_DIR . 'assets/css/editor.css',
            DS_PLUGIN_DIR . 'assets/css/editor-desktop-classic.css',
            DS_PLUGIN_DIR . 'assets/css/editor-mobile-landscape.css',
            DS_PLUGIN_DIR . 'assets/css/editor-mobile-portrait.css',
            __FILE__,
        ];
        $max_mtime = 0;
        foreach ($candidates as $path) {
            if (!is_string($path) || $path === '') continue;
            $mtime = @filemtime($path);
            if ($mtime && $mtime > $max_mtime) $max_mtime = (int) $mtime;
        }
        return $max_mtime > 0 ? (string) $max_mtime : '0.9.299';
    }
    private static function parse_url_list($raw) {
        $lines = preg_split('/[\r\n,]+/', (string) $raw);
        if (!is_array($lines)) return [];
        $out = [];
        foreach ($lines as $line) {
            $u = esc_url_raw(trim((string) $line));
            if ($u === '') continue;
            $out[] = $u;
        }
        return array_values(array_unique($out));
    }

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

        $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 = sanitize_key((string) ($item['icon'] ?? $item['name'] ?? ''));
                    if ($icon === '') continue;
                    $label = sanitize_text_field((string) ($item['label'] ?? $icon));
                    if ($label === '') $label = $icon;
                    $glyph = sanitize_text_field((string) ($item['glyph'] ?? ''));
                    $id = sanitize_key((string) ($item['id'] ?? ('icon_' . $icon . '_' . $item_index)));
                    if ($id === '') $id = 'icon_' . $item_index;
                    $items_out[] = [
                        'id' => $id,
                        'label' => $label,
                        'icon' => $icon,
                        '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;
                    $id = sanitize_key((string) ($item['id'] ?? ('emoji_' . $item_index)));
                    if ($id === '') $id = 'emoji_' . $item_index;
                    $items_out[] = [
                        'id' => $id,
                        'label' => $label,
                        'emoji' => $emoji,
                    ];
                }
                $item_index++;
            }

            if (empty($items_out)) continue;
            $groups_out[] = [
                'id' => $group_id,
                'name' => $name,
                'items' => $items_out,
            ];
        }

        return $groups_out;
    }
    private static function vendor_script_url($file_name, $cdn_url = '') {
        $file_name = trim((string) $file_name);
        if ($file_name === '') return '';
        $local_path = DS_PLUGIN_DIR . 'assets/vendor/' . $file_name;
        if (file_exists($local_path)) {
            return DS_PLUGIN_URL . 'assets/vendor/' . $file_name;
        }
        // Default OFF for production/wp.org-safe packaging; can be overridden via filter.
        $allow_cdn_fallback = (bool) apply_filters('ds_allow_cdn_vendor_fallback', false, $file_name);
        if ($allow_cdn_fallback && is_string($cdn_url) && $cdn_url !== '') {
            return $cdn_url;
        }
        return '';
    }
    public static function init() {
        add_shortcode('design_studio', [__CLASS__, 'shortcode']);
        add_action('wp_enqueue_scripts', [__CLASS__, 'register_assets']);
        add_action('wp_footer', [__CLASS__, 'print_boot_json' ], 1);
        add_filter('query_vars', [__CLASS__, 'register_query_vars']);
        add_filter('redirect_canonical', [__CLASS__, 'maybe_skip_canonical_redirect'], 10, 2);
    }

    public static function register_query_vars($vars) {
        $vars[] = 'ds_template';
        $vars[] = 'ds_design';
        $vars[] = 'ds_product';
        $vars[] = 'ds_pick';
        $vars[] = '_dsr';
        return $vars;
    }

    public static function maybe_skip_canonical_redirect($redirect_url, $requested_url) {
        if (isset($_GET['ds_template']) || isset($_GET['ds_design']) || isset($_GET['ds_product']) || isset($_GET['ds_pick']) || isset($_GET['_dsr'])) {
            return false;
        }
        return $redirect_url;
    }

    
    public static function print_boot_json() {
        if (!self::$latest_boot_json) return;
        echo '<script>window.DS_BOOT = window.DS_BOOT || ' . self::$latest_boot_json . ';</script>';
    }
    public static function register_assets() {
        // Prefer local vendor libs; CDN fallback can be disabled via filter.
        $fabric_handle = 'ds-fabric-js';
        $three_handle = 'ds-three-js';
        $gltf_loader_handle = 'ds-gltf-loader';
        $decal_handle = 'ds-decal-geometry';
        $iro_handle = 'ds-iro-js';
        $asset_ver = self::current_asset_version();
        $fabric_url = self::vendor_script_url('fabric.min.js', 'https://cdn.jsdelivr.net/npm/fabric@5.3.0/dist/fabric.min.js');
        $three_url = self::vendor_script_url('three.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js');
        // Keep legacy non-module URLs for compatibility when local files are not bundled yet.
        $gltf_url = self::vendor_script_url('GLTFLoader.min.js', 'https://cdn.jsdelivr.net/gh/mrdoob/three.js@r128/examples/js/loaders/GLTFLoader.js');
        $decal_url = self::vendor_script_url('DecalGeometry.min.js', 'https://cdn.jsdelivr.net/gh/mrdoob/three.js@r128/examples/js/geometries/DecalGeometry.js');
        $iro_url = self::vendor_script_url('iro.min.js', 'https://cdn.jsdelivr.net/npm/@jaames/iro@5/dist/iro.min.js');

        if ($fabric_url !== '') {
            wp_register_script($fabric_handle, $fabric_url . (strpos($fabric_url, '?') === false ? ('?ver=' . rawurlencode($asset_ver)) : ''), [], '5.3.0', true);
        }
        if ($three_url !== '') {
            wp_register_script($three_handle, $three_url . (strpos($three_url, '?') === false ? ('?ver=' . rawurlencode($asset_ver)) : ''), [], '0.128.0', false);
        }
        if ($gltf_url !== '') {
            wp_register_script($gltf_loader_handle, $gltf_url . (strpos($gltf_url, '?') === false ? ('?ver=' . rawurlencode($asset_ver)) : ''), [$three_handle], '0.128.0', false);
        }
        if ($decal_url !== '') {
            wp_register_script($decal_handle, $decal_url . (strpos($decal_url, '?') === false ? ('?ver=' . rawurlencode($asset_ver)) : ''), [$three_handle], '0.128.0', false);
        }
        if ($iro_url !== '') {
            wp_register_script($iro_handle, $iro_url . (strpos($iro_url, '?') === false ? ('?ver=' . rawurlencode($asset_ver)) : ''), [], '5.5.2', true);
        }
        // Editor script
        wp_register_script('ds-editor', DS_PLUGIN_URL . 'assets/js/editor.js?ver=' . rawurlencode($asset_ver), [$fabric_handle, $three_handle, $gltf_loader_handle, $decal_handle, $iro_handle], $asset_ver, true);
        wp_register_style('ds-editor', DS_PLUGIN_URL . 'assets/css/editor.css?ver=' . rawurlencode($asset_ver), [], $asset_ver);
        wp_register_style('ds-editor-desktop-classic', DS_PLUGIN_URL . 'assets/css/editor-desktop-classic.css?ver=' . rawurlencode($asset_ver), ['ds-editor'], $asset_ver);
        wp_register_style('ds-editor-mobile-landscape', DS_PLUGIN_URL . 'assets/css/editor-mobile-landscape.css?ver=' . rawurlencode($asset_ver), ['ds-editor'], $asset_ver);
        wp_register_style('ds-editor-mobile-portrait', DS_PLUGIN_URL . 'assets/css/editor-mobile-portrait.css?ver=' . rawurlencode($asset_ver), ['ds-editor'], $asset_ver);
    }

    public static function shortcode($atts) {
        $asset_ver = self::current_asset_version();
        $atts = shortcode_atts([
            'template' => '', // slug or ID
            'design_id' => 0, // optional ds_design post ID to preload
        ], $atts, 'design_studio');

        $template_id = 0;
        // Strict flow: no default template unless explicitly allowed by integrator.
        $allow_shortcode_template_default = (bool) apply_filters('ds_allow_shortcode_template_default', false, $atts);
        if ($allow_shortcode_template_default) {
            if (is_numeric($atts['template'])) {
                $template_id = intval($atts['template']);
            } elseif (!empty($atts['template'])) {
                $post = get_page_by_path(sanitize_title($atts['template']), OBJECT, 'ds_template');
                if ($post) $template_id = $post->ID;
            }
        }

        $requested_design_id = isset($_GET['ds_design']) ? absint(wp_unslash($_GET['ds_design'])) : 0;
        $requested_product_id = isset($_GET['ds_product']) ? absint(wp_unslash($_GET['ds_product'])) : 0;
        $requested_pick = isset($_GET['ds_pick']) ? sanitize_text_field(wp_unslash($_GET['ds_pick'])) : '';
        $allow_forced_template = ($requested_design_id > 0) || ($requested_product_id > 0) || ($requested_pick === '1');
        $product_template_ids = [];
        if ($requested_product_id > 0 && class_exists('\DS_Plugin\Woo') && method_exists('\DS_Plugin\Woo', 'get_product_template_ids')) {
            $ids = \DS_Plugin\Woo::get_product_template_ids((int) $requested_product_id);
            if (is_array($ids)) {
                foreach ($ids as $tid_raw) {
                    $tid = (int) $tid_raw;
                    if ($tid > 0 && !in_array($tid, $product_template_ids, true)) $product_template_ids[] = $tid;
                }
            }
        }

        $forced_template = 0;
        if (isset($_GET['ds_template'])) {
            $forced_template = absint(wp_unslash($_GET['ds_template']));
        }
        if ($allow_forced_template && $forced_template > 0) {
            $forced_post = get_post($forced_template);
            if ($forced_post && $forced_post->post_type === 'ds_template') {
                $allowed_statuses = ['publish'];
                if (current_user_can('edit_post', $forced_template) || current_user_can('edit_posts')) {
                    $allowed_statuses = ['publish', 'private', 'draft', 'pending'];
                }
                $allowed_by_product = true;
                if ($requested_product_id > 0 && !empty($product_template_ids)) {
                    $allowed_by_product = in_array((int) $forced_template, $product_template_ids, true);
                }
                if ($allowed_by_product && in_array((string) $forced_post->post_status, $allowed_statuses, true)) {
                    $template_id = $forced_template;
                }
            }
        }

        $load_design_id = 0;
        if (!empty($atts['design_id']) && is_numeric($atts['design_id'])) {
            $load_design_id = (int) $atts['design_id'];
        } elseif (isset($_GET['ds_design'])) {
            $load_design_id = (int) sanitize_text_field(wp_unslash($_GET['ds_design']));
        }
        $embed_mode = isset($_GET['ds_embed']) && (string) sanitize_text_field(wp_unslash($_GET['ds_embed'])) === '1';
        $cart_preview_mode = isset($_GET['ds_cart_preview']) && (string) sanitize_text_field(wp_unslash($_GET['ds_cart_preview'])) === '1';

        $return_url = get_permalink();
        if (!$return_url) {
            $return_url = home_url('/');
        }
        $three_asset_url = self::vendor_script_url('three.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js');
        $gltf_asset_url = self::vendor_script_url('GLTFLoader.min.js', 'https://cdn.jsdelivr.net/gh/mrdoob/three.js@r128/examples/js/loaders/GLTFLoader.js');
        $decal_asset_url = self::vendor_script_url('DecalGeometry.min.js', 'https://cdn.jsdelivr.net/gh/mrdoob/three.js@r128/examples/js/geometries/DecalGeometry.js');

        $editor_templates = self::get_editor_templates();
        if (!empty($product_template_ids)) {
            $allowed_map = [];
            foreach ($product_template_ids as $tid) {
                $allowed_map[(int) $tid] = true;
            }
            $editor_templates = array_values(array_filter($editor_templates, function($tpl) use ($allowed_map) {
                $tid = isset($tpl['id']) ? (int) $tpl['id'] : 0;
                return $tid > 0 && isset($allowed_map[$tid]);
            }));
            if ($template_id > 0 && !isset($allowed_map[(int) $template_id])) {
                $template_id = 0;
            }
            if ($template_id <= 0 && count($editor_templates) === 1) {
                $template_id = (int) $editor_templates[0]['id'];
            }
        }
        $active_template_id = $template_id > 0 ? $template_id : 0;
        $active_title = $active_template_id > 0 ? get_the_title($active_template_id) : '';
        $active_width_mm = $active_template_id > 0 ? (float) get_post_meta($active_template_id, 'width_mm', true) : 0.0;
        $active_height_mm = $active_template_id > 0 ? (float) get_post_meta($active_template_id, 'height_mm', true) : 0.0;
        $active_bleed_mm = $active_template_id > 0 ? (float) get_post_meta($active_template_id, 'bleed_mm', true) : 0.0;
        $active_dpi = $active_template_id > 0 ? (int) get_post_meta($active_template_id, 'dpi', true) : 300;
        $active_preview_type = $active_template_id > 0 ? (((string) get_post_meta($active_template_id, 'preview_type', true)) ?: 'mug') : 'none';
        $active_preview_mode = $active_template_id > 0 ? (((string) get_post_meta($active_template_id, 'preview_mode', true)) ?: '') : '';
        if ($active_preview_mode === '') {
            $active_preview_mode = in_array($active_preview_type, ['mug', 'bottle', 'tshirt'], true) ? '3d' : '2d';
        }
        if (!in_array($active_preview_mode, ['2d', '3d'], true)) {
            $active_preview_mode = '2d';
        }
        $active_zones = $active_template_id > 0
            ? self::read_template_json_meta($active_template_id, 'zones_json', [
                [ 'id' => 'wrap', 'label' => 'Main Zone', 'width_mm' => 200, 'height_mm' => 90, 'x_mm' => 0, 'y_mm' => 0 ],
            ])
            : [];
        if ($active_preview_mode === '3d' && !empty($active_zones) && is_array($active_zones)) {
            $z0 = $active_zones[0];
            if (is_array($z0)) {
                $zw = isset($z0['width_mm']) ? (float) $z0['width_mm'] : 0.0;
                $zh = isset($z0['height_mm']) ? (float) $z0['height_mm'] : 0.0;
                if ($zw > 0) $active_width_mm = $zw;
                if ($zh > 0) $active_height_mm = $zh;
            }
        }
        $active_preview_config = $active_template_id > 0
            ? self::read_template_json_meta($active_template_id, 'preview_config_json', [])
            : [];

        $settings = \DS_Plugin\Settings::get();
        $has_removebg_key = !empty($settings['removebg_api_key']);
        $texture_urls = self::parse_url_list($settings['texture_library_urls'] ?? '');
        $frame_urls = self::parse_url_list($settings['frame_library_urls'] ?? '');
        $custom_icon_groups = self::parse_custom_asset_groups($settings['custom_icon_groups_json'] ?? '', 'icons');
        $custom_emoticon_groups = self::parse_custom_asset_groups($settings['custom_emoticon_groups_json'] ?? '', 'emoticons');
        $brand_mode = (string) ($settings['brand_header_mode'] ?? 'logo');
        if (!in_array($brand_mode, ['logo', 'text'], true)) $brand_mode = 'logo';
        $brand_header_enabled = !empty($settings['brand_header_enabled']);
        $brand_logo_url = (string) ($settings['brand_logo_url'] ?? '');
        if ($brand_logo_url === '') $brand_logo_url = DS_PLUGIN_URL . 'assets/images/lorandit-logo.png';
        $brand_title = (string) ($settings['brand_title_text'] ?? 'Pressio');
        if ($brand_title === '') $brand_title = 'Pressio';
        $brand_subtitle = (string) ($settings['brand_subtitle_text'] ?? 'Precision - Power - Scale');
        if ($brand_subtitle === '') $brand_subtitle = 'Precision - Power - Scale';
        $brand_subtitle_pos = (string) ($settings['brand_subtitle_position'] ?? 'right');
        if (!in_array($brand_subtitle_pos, ['top', 'bottom', 'left', 'right'], true)) $brand_subtitle_pos = 'right';
        $ui_variant = 'classic';
        $ui_classic_palette = (string) ($settings['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';
        list($ui_text_font_key, $ui_text_font_stack) = self::resolve_ui_text_font_stack((string) ($settings['ui_text_font'] ?? 'segoe_ui'));
        $ui_inline_vars = [
            '--accent' => (string) ($settings['ui_primary_color'] ?? '#0a7f73'),
            '--ds-primary' => (string) ($settings['ui_primary_color'] ?? '#0a7f73'),
            '--ds-accent-2' => (string) ($settings['ui_accent_color'] ?? '#f89f25'),
            '--ds-accent' => (string) ($settings['ui_accent_color'] ?? '#f89f25'),
            '--ds-accent-secondary' => (string) ($settings['ui_secondary_accent_color'] ?? '#2f80ed'),
            '--ds-bg-custom' => (string) ($settings['ui_bg_color'] ?? '#f3f8ff'),
            '--text' => (string) ($settings['ui_text_color'] ?? '#122033'),
            '--panel' => (string) ($settings['ui_panel_color'] ?? '#ffffff'),
            '--ds-button-surface' => (string) ($settings['ui_button_color'] ?? '#ffffff'),
            '--line' => (string) ($settings['ui_line_color'] ?? '#d3dde8'),
            '--muted' => (string) ($settings['ui_muted_color'] ?? '#5f7188'),
        ];
        $ui_inline_style = '';
        foreach ($ui_inline_vars as $var_name => $var_value) {
            $safe_val = sanitize_hex_color((string) $var_value);
            if ($safe_val) $ui_inline_style .= $var_name . ':' . $safe_val . ';';
        }
        $safe_font_stack = preg_replace('/[^a-zA-Z0-9,\-\s"\']+/', '', (string) $ui_text_font_stack);
        if (is_string($safe_font_stack) && $safe_font_stack !== '') $ui_inline_style .= '--ds-ui-font:' . trim($safe_font_stack) . ';';
        $data = [
            'template_id' => $active_template_id,
            'id' => $active_template_id,
            'title' => $active_title,
            'width_mm' => $active_width_mm,
            'height_mm' => $active_height_mm,
            'bleed_mm' => $active_bleed_mm,
            'dpi' => $active_dpi,
            'preview_type' => $active_preview_type,
            'preview_mode' => $active_preview_mode,
            'zones' => $active_zones,
            'preview_config' => $active_preview_config,
            'model_assets_base' => trailingslashit(DS_PLUGIN_URL . 'assets/models'),
            'templates' => $editor_templates,
            'user' => [
                'logged_in' => is_user_logged_in(),
                'login_url' => wp_login_url($return_url),
            ],
            'load_design_id' => $load_design_id,
            'editor_url' => $return_url,
            'home_url' => home_url('/'),
            'assets' => [
                'three_js' => $three_asset_url,
                'gltf_loader_js' => $gltf_asset_url,
                'decal_geometry_js' => $decal_asset_url,
                'editor_js' => DS_PLUGIN_URL . 'assets/js/editor.js?ver=' . rawurlencode($asset_ver),
            ],
            'rest' => [
                'base' => esc_url_raw(rest_url(Rest::NS)),
                'nonce' => wp_create_nonce('wp_rest'),
            ],
            'ai' => [
                'remove_bg_enabled' => $has_removebg_key,
            ],
            'library' => [
                'textures' => $texture_urls,
                'frames' => $frame_urls,
                'custom_icon_groups' => $custom_icon_groups,
                'custom_emoticon_groups' => $custom_emoticon_groups,
            ],
            'preview' => [
                'hide_white_zone_bg' => !empty($settings['preview_hide_white_zone_bg']) ? 1 : 0,
            ],
            'ui' => [
                'primary_color' => (string) ($settings['ui_primary_color'] ?? '#0a7f73'),
                'accent_color' => (string) ($settings['ui_accent_color'] ?? '#f89f25'),
                'secondary_accent_color' => (string) ($settings['ui_secondary_accent_color'] ?? '#2f80ed'),
                'bg_color' => (string) ($settings['ui_bg_color'] ?? '#f3f8ff'),
                'text_color' => (string) ($settings['ui_text_color'] ?? '#122033'),
                'text_font' => $ui_text_font_key,
                'font_family' => $ui_text_font_stack,
                'panel_color' => (string) ($settings['ui_panel_color'] ?? '#ffffff'),
                'button_color' => (string) ($settings['ui_button_color'] ?? '#ffffff'),
                'line_color' => (string) ($settings['ui_line_color'] ?? '#d3dde8'),
                'muted_color' => (string) ($settings['ui_muted_color'] ?? '#5f7188'),
                'modern_icon_color' => (string) ($settings['ui_modern_icon_color'] ?? '#ffffff'),
                'variant' => 'classic',
                'classic_palette' => $ui_classic_palette,
                'modern_right_panel_position' => 'right',
                'fullscreen_header_enabled' => $brand_header_enabled ? 1 : 0,
            ],
            'protection' => [
                'enabled' => 1,
                'trial_total' => 0,
            ],
            'i18n' => self::editor_i18n_strings(),
            'embed_mode' => $embed_mode,
            'cart_preview_mode' => $cart_preview_mode,
        ];
        $boot_json = wp_json_encode($data);
        self::$latest_boot_json = $boot_json;
        $critical_css = '.ds-studio{font-family:var(--ds-ui-font,"Segoe UI",Tahoma,Arial,sans-serif);color:#122033}.ds-toolbar{display:flex;flex-wrap:wrap;gap:6px;padding:6px;border:1px solid #d3dde8;border-radius:12px;background:#f8fbff}.ds-toolblock{display:flex;flex-direction:column;gap:4px;padding:4px;border:1px solid #ccdaea;border-radius:8px;background:#fff}.ds-group{display:flex;flex-wrap:wrap;gap:4px;padding:4px;border:1px solid #d3dde8;border-radius:7px;background:#fff}.ds-toolbar button,.ds-toolbar .ds-upload{height:28px;min-width:28px;border:1px solid #c5d1df;background:#fff;border-radius:6px;padding:0 8px;display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:600;cursor:pointer}.ds-upload input{display:none}.ds-workspace{margin-top:10px}.ds-canvas-shell{border:1px solid #d3dde8;border-radius:12px;background:#eef3f9;padding:10px}.ds-canvas-viewport{width:100%;height:68vh;overflow:auto}.ds-status{margin-top:10px;min-height:18px;color:#5f7188;font-size:12px}';

        $late_render = did_action('wp_head') > 0;
        wp_enqueue_style('ds-editor');
        wp_enqueue_style('ds-editor-mobile-portrait');
        wp_enqueue_style('ds-editor-mobile-landscape');
        wp_style_add_data('ds-editor-mobile-portrait', 'media', '(max-width: 1023px) and (orientation: portrait)');
        wp_style_add_data('ds-editor-mobile-landscape', 'media', '(orientation: landscape) and (pointer: coarse)');
        wp_enqueue_style('ds-editor-desktop-classic');
        wp_style_add_data('ds-editor-desktop-classic', 'media', '(min-width: 1024px)');
        wp_enqueue_style('dashicons');
        wp_enqueue_script('ds-editor');
        wp_localize_script('ds-editor', 'DS_BOOT', $data);

        $prepend_html = '';
        if ($late_render && !self::$forced_assets_printed) {
            self::$forced_assets_printed = true;
            $prepend_html .= '<style>' . $critical_css . '</style>';
            $styles = wp_styles();
            $scripts = wp_scripts();
            $ds_css = isset($styles->registered['ds-editor']) ? $styles->registered['ds-editor']->src : '';
            $ds_classic_css = isset($styles->registered['ds-editor-desktop-classic']) ? $styles->registered['ds-editor-desktop-classic']->src : '';
            $ds_mobile_landscape_css = isset($styles->registered['ds-editor-mobile-landscape']) ? $styles->registered['ds-editor-mobile-landscape']->src : '';
            $ds_mobile_portrait_css = isset($styles->registered['ds-editor-mobile-portrait']) ? $styles->registered['ds-editor-mobile-portrait']->src : '';
            $dashicons_css = isset($styles->registered['dashicons']) ? $styles->registered['dashicons']->src : '';
            $fabric_js = isset($scripts->registered['ds-fabric-js']) ? $scripts->registered['ds-fabric-js']->src : '';
            $three_js = isset($scripts->registered['ds-three-js']) ? $scripts->registered['ds-three-js']->src : '';
            $gltf_js = isset($scripts->registered['ds-gltf-loader']) ? $scripts->registered['ds-gltf-loader']->src : '';
            $decal_js = isset($scripts->registered['ds-decal-geometry']) ? $scripts->registered['ds-decal-geometry']->src : '';
            $editor_js = isset($scripts->registered['ds-editor']) ? $scripts->registered['ds-editor']->src : '';
            if ($dashicons_css) $prepend_html .= '<link rel="stylesheet" href="' . esc_url($dashicons_css) . '" />';
            if ($ds_css) $prepend_html .= '<link rel="stylesheet" href="' . esc_url($ds_css) . '" />';
            if ($ds_mobile_portrait_css) $prepend_html .= '<link rel="stylesheet" href="' . esc_url($ds_mobile_portrait_css) . '" media="(max-width: 1023px) and (orientation: portrait)" />';
            if ($ds_mobile_landscape_css) $prepend_html .= '<link rel="stylesheet" href="' . esc_url($ds_mobile_landscape_css) . '" media="(orientation: landscape) and (pointer: coarse)" />';
            if ($ui_variant === 'classic' && $ds_classic_css) $prepend_html .= '<link rel="stylesheet" href="' . esc_url($ds_classic_css) . '" media="(min-width: 1024px)" />';
            $prepend_html .= '<script>window.DS_BOOT = ' . $boot_json . ';</script>';
            if ($fabric_js) $prepend_html .= '<script src="' . esc_url($fabric_js) . '"></script>';
            if ($three_js) $prepend_html .= '<script src="' . esc_url($three_js) . '"></script>';
            if ($gltf_js) $prepend_html .= '<script src="' . esc_url($gltf_js) . '"></script>';
            if ($decal_js) $prepend_html .= '<script src="' . esc_url($decal_js) . '"></script>';
            if ($editor_js) $prepend_html .= '<script src="' . esc_url($editor_js) . '"></script>';
        }
        ob_start();
        ?>
        <?php echo $prepend_html; ?>
        <?php if ($embed_mode): ?>
            <style>
              html,body{margin:0!important;padding:0!important;min-height:100%!important;overflow:auto!important;background:#fff!important}
              body #wpadminbar{display:none!important}
              .ds-embed-root{margin:0!important;padding:0!important}
              .ds-embed-root .ds-studio{margin:0!important;border-radius:0!important;min-height:100vh!important}
              <?php if ($cart_preview_mode): ?>
              .ds-embed-root[data-ds-cart-preview="1"] .ds-topbar,
              .ds-embed-root[data-ds-cart-preview="1"] .ds-mobile-topbar,
              .ds-embed-root[data-ds-cart-preview="1"] .ds-toolbar,
              .ds-embed-root[data-ds-cart-preview="1"] .ds-left-rail,
              .ds-embed-root[data-ds-cart-preview="1"] .ds-mobile-bottom-nav,
              .ds-embed-root[data-ds-cart-preview="1"] .ds-mobile-apply-cta,
              .ds-embed-root[data-ds-cart-preview="1"] [data-role="mobile-sheets"],
              .ds-embed-root[data-ds-cart-preview="1"] [data-role="mobile-sheet-backdrop"],
              .ds-embed-root[data-ds-cart-preview="1"] .ds-status,
              .ds-embed-root[data-ds-cart-preview="1"] .ds-statusline{display:none!important}
              .ds-embed-root[data-ds-cart-preview="1"] .ds-desktop-main,
              .ds-embed-root[data-ds-cart-preview="1"] .ds-workspace,
              .ds-embed-root[data-ds-cart-preview="1"] .ds-workspace-grid,
              .ds-embed-root[data-ds-cart-preview="1"] .ds-center-stage,
              .ds-embed-root[data-ds-cart-preview="1"] .ds-canvas-shell{margin:0!important;padding:0!important;inset:auto!important;height:100vh!important;min-height:100vh!important;max-height:100vh!important;border-radius:0!important;border:0!important}
              <?php endif; ?>
            </style>
            <div class="ds-embed-root" data-ds-embed="1"<?php echo $cart_preview_mode ? ' data-ds-cart-preview="1"' : ''; ?>>
        <?php endif; ?>
        <div class="ds-studio<?php echo $active_template_id > 0 ? '' : ' ds-no-template'; ?>" data-template-id="<?php echo esc_attr($active_template_id); ?>" data-ds-ui-variant="<?php echo esc_attr($ui_variant); ?>" data-dc-palette="<?php echo esc_attr($ui_classic_palette); ?>" data-ds-icon-set="outline" style="<?php echo esc_attr($ui_inline_style); ?>">
            <script type="application/json" class="ds-boot-json"><?php echo wp_json_encode($data); ?></script>
            <script>
            (function() {
              window.DS_BOOT = window.DS_BOOT || <?php echo $boot_json; ?>;
            })();
            </script>
            <div class="ds-mobile-topbar" data-role="mobile-topbar" aria-label="Mobile Top Bar">
                <a
                    class="ds-mobile-topbar-back"
                    href="<?php echo esc_url($return_url); ?>"
                    data-mobile-action="exit-flow"
                    data-exit-fallback="<?php echo esc_url(home_url('/')); ?>"
                ><?php esc_html_e('Exit', 'ds'); ?></a>
                <div class="ds-mobile-topbar-center" aria-label="<?php esc_attr_e('Quick Actions', 'ds'); ?>">
                    <button type="button" class="ds-mobile-topbar-icon-btn" data-mobile-action="undo" aria-label="<?php esc_attr_e('Undo', 'ds'); ?>" title="<?php esc_attr_e('Undo', 'ds'); ?>">
                        <span class="dashicons dashicons-undo" aria-hidden="true"></span>
                        <span class="ds-mobile-topbar-icon-label"><?php esc_html_e('Undo', 'ds'); ?></span>
                    </button>
                    <button type="button" class="ds-mobile-topbar-icon-btn" data-mobile-action="hard-refresh" aria-label="<?php esc_attr_e('Hard Refresh', 'ds'); ?>" title="<?php esc_attr_e('Hard Refresh', 'ds'); ?>">
                        <span class="dashicons dashicons-update" aria-hidden="true"></span>
                        <span class="ds-mobile-topbar-icon-label"><?php esc_html_e('Refresh', 'ds'); ?></span>
                    </button>
                    <button type="button" class="ds-mobile-topbar-icon-btn" data-mobile-action="redo" aria-label="<?php esc_attr_e('Redo', 'ds'); ?>" title="<?php esc_attr_e('Redo', 'ds'); ?>">
                        <span class="dashicons dashicons-redo" aria-hidden="true"></span>
                        <span class="ds-mobile-topbar-icon-label"><?php esc_html_e('Redo', 'ds'); ?></span>
                    </button>
                </div>
                <button type="button" class="ds-mobile-topbar-btn ds-mobile-topbar-btn-primary ds-mobile-btn-with-icon" data-mobile-action="final-save">
                    <span class="ds-mobile-btn-ic" aria-hidden="true">
                        <svg viewBox="0 0 24 24" focusable="false" aria-hidden="true">
                            <path d="M6 3h9l4 4v13a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/>
                            <path d="M15 3v4h4" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/>
                            <path d="M8.5 14.5l2.2 2.2 4-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                        </svg>
                    </span>
                    <span><?php esc_html_e('Save Design', 'ds'); ?></span>
                </button>
            </div>
            <?php if ($brand_header_enabled): ?>
                <div class="ds-fullscreen-brand" aria-hidden="true">
                    <div class="ds-fullscreen-brand-mark">
                        <div class="ds-fullscreen-brand-copy ds-subtitle-<?php echo esc_attr($brand_subtitle_pos); ?>">
                            <?php if ($brand_mode === 'logo'): ?>
                                <img
                                    class="ds-fullscreen-brand-title-image"
                                    src="<?php echo esc_url($brand_logo_url); ?>"
                                    alt="<?php echo esc_attr($brand_title); ?>"
                                    onerror="this.style.display='none';"
                                />
                            <?php else: ?>
                                <h2 class="ds-fullscreen-brand-title-text"><?php echo esc_html($brand_title); ?></h2>
                            <?php endif; ?>
                            <p class="ds-fullscreen-brand-subtitle"><?php echo esc_html($brand_subtitle); ?></p>
                        </div>
                    </div>
                </div>
            <?php endif; ?>
            <div class="ds-toolbar">
                <div class="ds-toolblock ds-template-block">
                    <div class="ds-block-title">Template</div>
                    <div class="ds-group">
                        <form method="get" action="<?php echo esc_url($return_url); ?>" class="ds-template-form">
                            <select data-input="template-select" name="ds_template" aria-label="Choose template">
                                <option value=""><?php _e('Choose Template', 'ds'); ?></option>
                                <?php foreach ($editor_templates as $tpl): ?>
                                    <?php
                                    $tpl_id = isset($tpl['id']) ? (int) $tpl['id'] : 0;
                                    if ($tpl_id <= 0) continue;
                                    $tpl_title = isset($tpl['title']) ? (string) $tpl['title'] : ('Template #' . $tpl_id);
                                    $tpl_w = isset($tpl['width_mm']) ? (float) $tpl['width_mm'] : 0;
                                    $tpl_h = isset($tpl['height_mm']) ? (float) $tpl['height_mm'] : 0;
                                    $size = ($tpl_w > 0 && $tpl_h > 0) ? sprintf(' (%.0fx%.0f mm)', $tpl_w, $tpl_h) : '';
                                    ?>
                                    <option value="<?php echo esc_attr($tpl_id); ?>" <?php selected($active_template_id, $tpl_id); ?>>
                                        <?php echo esc_html($tpl_title . $size); ?>
                                    </option>
                                <?php endforeach; ?>
                            </select>
                            <button type="submit" data-action="template-apply"><?php _e('Apply', 'ds'); ?></button>
                        </form>
                    </div>
                </div>
                <div class="ds-toolblock ds-block-insert">
                    <div class="ds-block-title">Insert</div>
                    <div class="ds-group">
                        <button type="button" data-action="add-text">Text</button>
                        <label class="ds-upload ds-upload-image">
                            Image
                            <input type="file" accept="image/*" data-action="upload-image" />
                        </label>
                        <label class="ds-upload ds-upload-svg">
                            SVG
                            <input type="file" accept="image/svg+xml" data-action="upload-svg" />
                        </label>
                        <button type="button" data-action="open-textures">Textures</button>
                        <button type="button" data-action="open-frames">Frames</button>
                    </div>
                    <div class="ds-group ds-shapes">
                        <button type="button" data-shape="rect">Rect</button>
                        <button type="button" data-shape="circle">Circle</button>
                        <button type="button" data-shape="triangle">Triangle</button>
                        <button type="button" data-shape="star">Star</button>
                        <button type="button" data-shape="heart">Heart</button>
                        <button type="button" data-shape="polygon">Polygon</button>
                    </div>
                </div>
                <div class="ds-toolblock">
                    <div class="ds-block-title">Edit</div>
                    <div class="ds-group">
                        <button type="button" data-action="undo">Undo</button>
                        <button type="button" data-action="redo">Redo</button>
                        <button type="button" data-action="duplicate-object">Duplicate</button>
                        <button type="button" data-action="crop-image">Crop</button>
                        <button type="button" data-action="crop-apply">Apply Crop</button>
                        <button type="button" data-action="ai-remove-bg">AI BG Remove</button>
                        <button type="button" data-action="crop-cancel">Crop Cancel</button>
                        <button type="button" data-action="delete-object">Delete</button>
                        <button type="button" data-action="object-props">Object</button>
                    </div>
                    <div class="ds-group">
                        <button type="button" data-action="mask">Mask</button>
                        <button type="button" data-action="mask-bake">Mask Bake</button>
                    </div>
                </div>
                <div class="ds-toolblock">
                    <div class="ds-block-title">Arrange</div>
                    <div class="ds-group">
                        <button type="button" data-align="left">Align L</button>
                        <button type="button" data-align="center">Align C</button>
                        <button type="button" data-align="right">Align R</button>
                        <button type="button" data-align="top">Align T</button>
                        <button type="button" data-align="middle">Align M</button>
                        <button type="button" data-align="bottom">Align B</button>
                    </div>
                    <div class="ds-group">
                        <button type="button" data-action="group">Group</button>
                        <button type="button" data-action="ungroup">Ungroup</button>
                        <button type="button" data-action="layer-up">Up</button>
                        <button type="button" data-action="layer-down">Down</button>
                        <button type="button" data-action="layer-front">Front</button>
                        <button type="button" data-action="layer-back">Back</button>
                    </div>
                </div>
                <div class="ds-toolblock">
                    <div class="ds-block-title">View</div>
                    <div class="ds-group">
                        <button type="button" data-action="fit-canvas">Fit</button>
                        <button type="button" data-action="zoom-reset">100%</button>
                        <button type="button" data-action="toggle-fullscreen">Fullscreen</button>
                        <label class="ds-zoom">
                            Zoom
                            <input type="range" min="25" max="300" step="5" value="100" data-input="zoom" />
                        </label>
                        <span class="ds-zoom-label" data-role="zoom-label">100%</span>
                    </div>
                    <div class="ds-group">
                        <label class="ds-toggle"><input type="checkbox" data-opt="grid" /><span>Grid</span></label>
                        <label class="ds-toggle"><input type="checkbox" data-opt="snap" /><span>Snap</span></label>
                        <label class="ds-toggle"><input type="checkbox" data-opt="bleed-marks" /><span>Bleed</span></label>
                    </div>
                </div>
                <div class="ds-toolblock">
                    <div class="ds-block-title">Output</div>
                    <div class="ds-group">
                        <button type="button" data-action="export">Export PNG</button>
                        <button type="button" data-action="save">Save Zone</button>
                        <button type="button" data-action="final-save">Final Save</button>
                        <button type="button" data-action="apply-product">Apply to Product</button>
                    </div>
                    <div class="ds-group ds-auth-hint" data-role="output-auth-hint" style="display:none"></div>
                    <div class="ds-group">
                        <input type="number" min="1" placeholder="Design ID" data-input="design-id" value="<?php echo esc_attr($load_design_id ?: ''); ?>" />
                        <button type="button" data-action="load-design">Load</button>
                        <button type="button" data-action="my-designs">My Designs</button>
                    </div>
                </div>
            </div>
            <div class="ds-workspace">
                <!-- Desktop Pro Editing Top Bar: meta + existing top tools host -->
                <div class="ds-topbar">
                    <div class="ds-topbar-actions" aria-label="Top Quick Actions">
                        <button type="button" class="ds-topbar-action-btn" data-mobile-action="undo" aria-label="<?php esc_attr_e('Undo', 'ds'); ?>">
                            <span class="dashicons dashicons-undo" aria-hidden="true"></span>
                            <span><?php esc_html_e('Undo', 'ds'); ?></span>
                        </button>
                        <button type="button" class="ds-topbar-action-btn" data-mobile-action="hard-refresh" aria-label="<?php esc_attr_e('Refresh', 'ds'); ?>">
                            <span class="dashicons dashicons-update" aria-hidden="true"></span>
                            <span><?php esc_html_e('Refresh', 'ds'); ?></span>
                        </button>
                        <button type="button" class="ds-topbar-action-btn" data-mobile-action="redo" aria-label="<?php esc_attr_e('Redo', 'ds'); ?>">
                            <span class="dashicons dashicons-redo" aria-hidden="true"></span>
                            <span><?php esc_html_e('Redo', 'ds'); ?></span>
                        </button>
                        <button type="button" class="ds-topbar-action-btn ds-topbar-action-btn-primary" data-mobile-action="final-save" aria-label="<?php esc_attr_e('Save Design', 'ds'); ?>">
                            <span class="dashicons dashicons-saved" aria-hidden="true"></span>
                            <span><?php esc_html_e('Save Design', 'ds'); ?></span>
                        </button>
                    </div>
                    <div class="ds-topbar-center" data-role="desktop-top-center">
                        <div class="ds-tools-top" data-role="top-tools" aria-label="Top Tools"></div>
                    </div>
                    <div class="ds-topbar-meta">
                        <a
                            class="ds-topbar-back ds-topbar-exit"
                            href="<?php echo esc_url($return_url); ?>"
                            data-exit-fallback="<?php echo esc_url(home_url('/')); ?>"
                            aria-label="<?php esc_attr_e('Exit', 'ds'); ?>"
                            title="<?php esc_attr_e('Exit', 'ds'); ?>"
                        >X</a>
                    </div>
                </div>
                <div class="ds-desktop-main">
                <div class="ds-workspace-grid">
                    <!-- Left Creation Rail (source blocks docked by editor.js) -->
                    <aside class="ds-canvas-side ds-canvas-side-left ds-left-rail" aria-label="Quick Tools Left">
                        <div class="ds-canvas-side-inner" data-role="left-tools"></div>
                    </aside>
                    <!-- Center Canvas Stage -->
                    <div class="ds-canvas-shell ds-center-stage">
                        <div class="ds-canvas-wrap">
                            <div class="ds-ruler-corner" aria-hidden="true"></div>
                            <div class="ds-ruler ds-ruler-top" data-role="ruler-top" aria-hidden="true"></div>
                            <div class="ds-ruler ds-ruler-left" data-role="ruler-left" aria-hidden="true"></div>
                            <div class="ds-canvas-viewport">
                                <canvas id="ds-canvas"></canvas>
                            </div>
                        </div>
                    </div>
                    <!-- Right Inspector Panel -->
                    <aside class="ds-canvas-side ds-canvas-side-right ds-right-inspector" aria-label="Quick Tools Right">
                        <div class="ds-canvas-side-inner" data-role="right-tools">
                            <div class="ds-toolblock ds-previewblock">
                                <div class="ds-block-title">Preview</div>
                                <div class="ds-group ds-mug-mini-group">
                                    <div class="ds-preview-mini" data-role="mug-canvas-mini" title="Click for larger preview"></div>
                                </div>
                            </div>
                        </div>
                    </aside>
                </div>
                </div>
            </div>
            <div class="ds-mug-modal" data-role="mug-modal" aria-hidden="true">
                <div class="ds-mug-dialog" role="dialog" aria-modal="true" aria-label="Mug Preview">
                    <div class="ds-preview-head">
                        <h4>Mug Preview</h4>
                        <div class="ds-mug-actions">
                            <button type="button" data-action="mug-reset">Reset View</button>
                            <button type="button" data-action="mug-close">Close</button>
                        </div>
                    </div>
                    <div class="ds-preview-canvas ds-preview-canvas-large" data-role="mug-canvas-large"></div>
                    <p class="ds-layer-help">Drag to rotate. Design updates live.</p>
                    <p class="ds-preview-note"><?php esc_html_e('Preview is approximate and for print placement guidance only. Final product may differ.', 'ds'); ?></p>
                </div>
            </div>
            <div class="ds-props-modal" data-role="props-modal" aria-hidden="true">
                <div class="ds-props-dialog" role="dialog" aria-modal="true" aria-label="Object Properties">
                    <div class="ds-props-dialog-head">
                        <h3>Object Properties</h3>
                        <button type="button" data-action="close-object-props">Close</button>
                    </div>
                    <div class="ds-object-panel" aria-label="Object Controls">
                        <p class="ds-layer-help">Select layer to edit properties.</p>
                        <div class="ds-props" data-role="props">
                            <label>Text<input type="text" data-prop="text" /></label>
                            <label>Font Family
                                <select data-prop="fontFamily"></select>
                            </label>
                            <label>Font Size<input type="number" min="1" step="1" data-prop="fontSize" /></label>
                            <label>X<input type="number" step="1" data-prop="left" /></label>
                            <label>Y<input type="number" step="1" data-prop="top" /></label>
                            <label>Width<input type="number" min="1" step="1" data-prop="width" /></label>
                            <label>Height<input type="number" min="1" step="1" data-prop="height" /></label>
                            <label>Rotate<input type="number" step="1" data-prop="angle" /></label>
                            <label>Opacity %<input type="number" min="0" max="100" step="1" data-prop="opacity" /></label>
                            <label>Fill<input type="color" data-prop="fill" /></label>
                            <label>Stroke<input type="color" data-prop="stroke" /></label>
                            <label>Stroke W<input type="number" min="0" step="1" data-prop="strokeWidth" /></label>
                            <label>Corners<input type="number" min="0" step="1" data-prop="cornerRadius" /></label>
                            <label>Stroke Side
                                <select data-prop="strokeSide">
                                    <option value="inside">Inside</option>
                                    <option value="outside">Outside</option>
                                </select>
                            </label>
                        </div>
                        <h3>Layers</h3>
                        <p class="ds-layer-help">Drag to reorder. Top layer is first.</p>
                        <ul class="ds-layer-list" data-role="layer-list"></ul>
                    </div>
                </div>
            </div>
            <div class="ds-my-designs-modal" data-role="my-designs-modal" aria-hidden="true">
                <div class="ds-my-designs-dialog" role="dialog" aria-modal="true" aria-label="My Designs">
                    <div class="ds-props-dialog-head">
                        <h3>My Designs</h3>
                        <button type="button" data-action="close-my-designs">Close</button>
                    </div>
                    <p class="ds-layer-help">Load your saved designs and manage sharing permissions.</p>
                    <div class="ds-my-designs-list" data-role="my-designs-list"></div>
                </div>
            </div>
            <div class="ds-my-designs-modal" data-role="asset-library-modal" aria-hidden="true">
                <div class="ds-my-designs-dialog" role="dialog" aria-modal="true" aria-label="Asset Library">
                    <div class="ds-props-dialog-head">
                        <h3 data-role="asset-library-title">Asset Library</h3>
                        <button type="button" data-action="close-asset-library">Close</button>
                    </div>
                    <p class="ds-layer-help">Choose default and admin-added assets.</p>
                    <div class="ds-asset-library-grid" data-role="asset-library-list"></div>
                </div>
            </div>
            <div class="ds-my-designs-modal" data-role="apply-product-modal" aria-hidden="true">
                <div class="ds-my-designs-dialog" role="dialog" aria-modal="true" aria-label="Apply Design to Product">
                    <div class="ds-props-dialog-head">
                        <h3>Apply Design to Product</h3>
                        <button type="button" data-action="close-apply-product">Close</button>
                    </div>
                    <p class="ds-layer-help">Choose a compatible WooCommerce product and continue to product page.</p>
                    <div class="ds-my-designs-list" data-role="apply-product-list"></div>
                </div>
            </div>
            <div class="ds-status">
                <span class="ds-status-msg" data-role="status-text"></span>
                <a
                    class="ds-status-powered"
                    href="<?php echo esc_url('https://lorandit.com/'); ?>"
                    target="_blank"
                    rel="noopener noreferrer nofollow"
                    aria-label="<?php esc_attr_e('Powered by Lorandit Design Studio Pro', 'ds'); ?>"
                >
                    <img
                        class="ds-status-powered-logo"
                        src="<?php echo esc_url(DS_PLUGIN_URL . 'assets/images/lorandit-logo.png'); ?>"
                        alt="<?php esc_attr_e('Lorandit Design Studio Pro', 'ds'); ?>"
                        loading="lazy"
                        decoding="async"
                    />
                    <span class="ds-status-powered-text"><?php esc_html_e('Powered by Lorandit Design Studio Pro', 'ds'); ?></span>
                </a>
            </div>
            <div class="ds-mobile-bottom-nav" data-role="mobile-bottom-nav" aria-label="Mobile Bottom Navigation">
                <button type="button" data-mobile-action="insert" data-label="<?php esc_attr_e('Insert', 'ds'); ?>"><?php esc_html_e('Insert', 'ds'); ?></button>
                <button type="button" data-mobile-action="canvas" data-label="<?php esc_attr_e('Canvas', 'ds'); ?>"><?php esc_html_e('Canvas', 'ds'); ?></button>
                <button type="button" data-mobile-action="library" data-label="<?php esc_attr_e('Library', 'ds'); ?>"><?php esc_html_e('Library', 'ds'); ?></button>
                <button type="button" data-mobile-action="preview" data-label="<?php esc_attr_e('Preview', 'ds'); ?>"><?php esc_html_e('Preview', 'ds'); ?></button>
            </div>
            <div class="ds-mobile-sheet-backdrop" data-role="mobile-sheet-backdrop" aria-hidden="true"></div>
            <div class="ds-mobile-sheets" data-role="mobile-sheets" aria-hidden="true">
                <section class="ds-mobile-sheet" data-mobile-sheet="insert" aria-label="<?php esc_attr_e('Insert Tools', 'ds'); ?>">
                    <div class="ds-mobile-sheet-head">
                        <h4><?php esc_html_e('Insert', 'ds'); ?></h4>
                        <button type="button" data-mobile-sheet-close="1"><?php esc_html_e('Close', 'ds'); ?></button>
                    </div>
                    <div class="ds-mobile-sheet-grid">
                        <button type="button" class="ds-mobile-btn-primary ds-mobile-insert-btn" data-mobile-sheet-action="upload-image"><?php esc_html_e('Image', 'ds'); ?></button>
                        <button type="button" class="ds-mobile-btn-secondary ds-mobile-insert-btn" data-mobile-sheet-action="add-text"><?php esc_html_e('Text', 'ds'); ?></button>
                        <button type="button" class="ds-mobile-insert-btn" data-mobile-sheet-action="upload-svg"><?php esc_html_e('SVG', 'ds'); ?></button>
                        <button type="button" class="ds-mobile-insert-btn" data-mobile-sheet-action="open-textures"><?php esc_html_e('Textures', 'ds'); ?></button>
                        <button type="button" class="ds-mobile-insert-btn" data-mobile-sheet-action="open-frames"><?php esc_html_e('Frames', 'ds'); ?></button>
                        <button type="button" class="ds-mobile-insert-btn" data-mobile-sheet-action="open-shapes"><?php esc_html_e('Shapes', 'ds'); ?></button>
                        <button type="button" class="ds-mobile-insert-btn" data-mobile-sheet-action="open-emoticons"><?php esc_html_e('Emoticons', 'ds'); ?></button>
                        <button type="button" class="ds-mobile-insert-btn" data-mobile-sheet-action="open-icons"><?php esc_html_e('Icons', 'ds'); ?></button>
                    </div>
                </section>
                <section class="ds-mobile-sheet" data-mobile-sheet="emoticons" aria-label="<?php esc_attr_e('Emoticons', 'ds'); ?>">
                    <div class="ds-mobile-sheet-head">
                        <h4><?php esc_html_e('Emoticons', 'ds'); ?></h4>
                        <button type="button" data-mobile-sheet-close="1"><?php esc_html_e('Close', 'ds'); ?></button>
                    </div>
                    <div class="ds-mobile-sheet-grid ds-mobile-icons-grid">
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-smile" aria-label="<?php esc_attr_e('Smile', 'ds'); ?>">Ã°Å¸â„¢â€š</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-heart" aria-label="<?php esc_attr_e('Heart', 'ds'); ?>">Ã¢ÂÂ¤Ã¯Â¸Â</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-fire" aria-label="<?php esc_attr_e('Fire', 'ds'); ?>">Ã°Å¸â€Â¥</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-star" aria-label="<?php esc_attr_e('Star', 'ds'); ?>">Ã¢Â­Â</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-cool" aria-label="<?php esc_attr_e('Cool', 'ds'); ?>">Ã°Å¸ËœÅ½</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-party" aria-label="<?php esc_attr_e('Party', 'ds'); ?>">Ã°Å¸Å½â€°</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-check" aria-label="<?php esc_attr_e('Check', 'ds'); ?>">Ã¢Å“â€¦</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-thumbs-up" aria-label="<?php esc_attr_e('Thumbs Up', 'ds'); ?>">Ã°Å¸â€˜Â</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-lightbulb" aria-label="<?php esc_attr_e('Idea', 'ds'); ?>">Ã°Å¸â€™Â¡</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-rocket" aria-label="<?php esc_attr_e('Rocket', 'ds'); ?>">Ã°Å¸Å¡â‚¬</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-gift" aria-label="<?php esc_attr_e('Gift', 'ds'); ?>">Ã°Å¸Å½Â</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-camera" aria-label="<?php esc_attr_e('Camera', 'ds'); ?>">Ã°Å¸â€œÂ·</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-sparkles" aria-label="<?php esc_attr_e('Sparkles', 'ds'); ?>">Ã¢Å“Â¨</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-rainbow" aria-label="<?php esc_attr_e('Rainbow', 'ds'); ?>">Ã°Å¸Å’Ë†</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-sun" aria-label="<?php esc_attr_e('Sun', 'ds'); ?>">Ã¢Ëœâ‚¬Ã¯Â¸Â</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-moon" aria-label="<?php esc_attr_e('Moon', 'ds'); ?>">Ã°Å¸Å’â„¢</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-flower" aria-label="<?php esc_attr_e('Flower', 'ds'); ?>">Ã°Å¸Å’Â¸</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-leaf" aria-label="<?php esc_attr_e('Leaf', 'ds'); ?>">Ã°Å¸ÂÆ’</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-music" aria-label="<?php esc_attr_e('Music', 'ds'); ?>">Ã°Å¸Å½Âµ</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-headphones" aria-label="<?php esc_attr_e('Headphones', 'ds'); ?>">Ã°Å¸Å½Â§</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-coffee" aria-label="<?php esc_attr_e('Coffee', 'ds'); ?>">Ã¢Ëœâ€¢</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-lightning" aria-label="<?php esc_attr_e('Lightning', 'ds'); ?>">Ã¢Å¡Â¡</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-target" aria-label="<?php esc_attr_e('Target', 'ds'); ?>">Ã°Å¸Å½Â¯</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="emoji-trophy" aria-label="<?php esc_attr_e('Trophy', 'ds'); ?>">Ã°Å¸Ââ€ </button>
                    </div>
                </section>
                <section class="ds-mobile-sheet" data-mobile-sheet="icons" aria-label="<?php esc_attr_e('Icons', 'ds'); ?>">
                    <div class="ds-mobile-sheet-head">
                        <h4><?php esc_html_e('Icons', 'ds'); ?></h4>
                        <button type="button" data-mobile-sheet-close="1"><?php esc_html_e('Close', 'ds'); ?></button>
                    </div>
                    <div class="ds-mobile-sheet-grid ds-mobile-icons-grid">
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-heart" aria-label="<?php esc_attr_e('Heart Icon', 'ds'); ?>">Ã¢â„¢Â¥</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-star" aria-label="<?php esc_attr_e('Star Icon', 'ds'); ?>">Ã¢Ëœâ€¦</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-bolt" aria-label="<?php esc_attr_e('Bolt Icon', 'ds'); ?>">Ã¢Å¡Â¡</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-check" aria-label="<?php esc_attr_e('Check Icon', 'ds'); ?>">Ã¢Å“â€œ</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-arrow" aria-label="<?php esc_attr_e('Arrow Icon', 'ds'); ?>">Ã¢Å¾Å“</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-pin" aria-label="<?php esc_attr_e('Pin Icon', 'ds'); ?>">Ã¢Å’â€“</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-plus" aria-label="<?php esc_attr_e('Plus Icon', 'ds'); ?>">Ã¯Â¼â€¹</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-minus" aria-label="<?php esc_attr_e('Minus Icon', 'ds'); ?>">Ã¢Ë†â€™</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-close" aria-label="<?php esc_attr_e('Close Icon', 'ds'); ?>">Ã¢Å“â€¢</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-cart" aria-label="<?php esc_attr_e('Cart Icon', 'ds'); ?>">Ã°Å¸â€ºâ€™</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-user" aria-label="<?php esc_attr_e('User Icon', 'ds'); ?>">Ã°Å¸â€˜Â¤</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-mail" aria-label="<?php esc_attr_e('Mail Icon', 'ds'); ?>">Ã¢Å“â€°</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-home" aria-label="<?php esc_attr_e('Home Icon', 'ds'); ?>">Ã¢Å’â€š</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-search" aria-label="<?php esc_attr_e('Search Icon', 'ds'); ?>">Ã¢Å’â€¢</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-clock" aria-label="<?php esc_attr_e('Clock Icon', 'ds'); ?>">Ã°Å¸â€¢â€™</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-camera" aria-label="<?php esc_attr_e('Camera Icon', 'ds'); ?>">Ã°Å¸â€œÂ·</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-gift" aria-label="<?php esc_attr_e('Gift Icon', 'ds'); ?>">Ã°Å¸Å½Â</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-phone" aria-label="<?php esc_attr_e('Phone Icon', 'ds'); ?>">Ã¢ËœÅ½</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-globe" aria-label="<?php esc_attr_e('Globe Icon', 'ds'); ?>">Ã°Å¸Å’Â</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-tag" aria-label="<?php esc_attr_e('Tag Icon', 'ds'); ?>">Ã°Å¸ÂÂ·</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-lock" aria-label="<?php esc_attr_e('Lock Icon', 'ds'); ?>">Ã°Å¸â€â€™</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-unlock" aria-label="<?php esc_attr_e('Unlock Icon', 'ds'); ?>">Ã°Å¸â€â€œ</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-flag" aria-label="<?php esc_attr_e('Flag Icon', 'ds'); ?>">Ã¢Å¡â€˜</button>
                        <button type="button" class="ds-mobile-icon-tile" data-mobile-sheet-action="icon-location" aria-label="<?php esc_attr_e('Location Icon', 'ds'); ?>">Ã°Å¸â€œÂ</button>
                    </div>
                </section>
                <section class="ds-mobile-sheet" data-mobile-sheet="canvas" aria-label="<?php esc_attr_e('Canvas Tools', 'ds'); ?>">
                    <div class="ds-mobile-sheet-head">
                        <h4><?php esc_html_e('Canvas', 'ds'); ?></h4>
                        <button type="button" data-mobile-sheet-close="1"><?php esc_html_e('Close', 'ds'); ?></button>
                    </div>
                    <div class="ds-mobile-sheet-grid">
                        <div class="ds-mobile-sheet-section ds-mobile-canvas-view">
                            <h5><?php esc_html_e('View', 'ds'); ?></h5>
                            <button type="button" class="ds-mobile-canvas-btn-full" data-mobile-sheet-action="toggle-fullscreen"><?php esc_html_e('FullScreen', 'ds'); ?></button>
                            <div class="ds-mobile-canvas-row ds-mobile-canvas-row-two">
                                <button type="button" data-mobile-sheet-action="fit-canvas"><?php esc_html_e('Fit Canvas', 'ds'); ?></button>
                                <button type="button" data-mobile-sheet-action="zoom-reset"><?php esc_html_e('Zoom 100%', 'ds'); ?></button>
                            </div>
                            <label class="ds-mobile-sheet-zoom" aria-label="<?php esc_attr_e('Zoom', 'ds'); ?>">
                                <span><?php esc_html_e('Zoom', 'ds'); ?></span>
                                <input type="range" min="25" max="300" step="5" value="100" data-role="mobile-zoom-slider" />
                                <strong data-role="mobile-zoom-label">100%</strong>
                            </label>
                        </div>
                        <div class="ds-mobile-sheet-section">
                            <h5><?php esc_html_e('Helpers', 'ds'); ?></h5>
                            <div class="ds-mobile-inline-toggles ds-mobile-canvas-helpers" role="group" aria-label="<?php esc_attr_e('Canvas Helpers', 'ds'); ?>">
                                <button type="button" data-mobile-sheet-action="toggle-grid" aria-pressed="false"><?php esc_html_e('Grid', 'ds'); ?></button>
                                <button type="button" data-mobile-sheet-action="toggle-snap" aria-pressed="false"><?php esc_html_e('Snap', 'ds'); ?></button>
                                <button type="button" data-mobile-sheet-action="toggle-bleed-marks" aria-pressed="false"><?php esc_html_e('Bleed', 'ds'); ?></button>
                            </div>
                        </div>
                    </div>
                </section>
                <section class="ds-mobile-sheet" data-mobile-sheet="library" aria-label="<?php esc_attr_e('Design Library Tools', 'ds'); ?>">
                    <div class="ds-mobile-sheet-head">
                        <h4><?php esc_html_e('Design Library', 'ds'); ?></h4>
                        <button type="button" data-mobile-sheet-close="1"><?php esc_html_e('Close', 'ds'); ?></button>
                    </div>
                    <div class="ds-mobile-sheet-grid">
                        <div class="ds-mobile-sheet-section ds-mobile-save-product">
                            <h5><?php esc_html_e('Save & Product', 'ds'); ?></h5>
                            <button type="button" class="ds-mobile-save-product-btn ds-mobile-save-product-btn-save ds-mobile-btn-with-icon" data-mobile-sheet-action="final-save">
                                <span class="ds-mobile-btn-ic" aria-hidden="true">
                                    <svg viewBox="0 0 24 24" focusable="false" aria-hidden="true">
                                        <path d="M6 3h9l4 4v13a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/>
                                        <path d="M15 3v4h4" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/>
                                        <path d="M8.5 14.5l2.2 2.2 4-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                                    </svg>
                                </span>
                                <span><?php esc_html_e('Save Design', 'ds'); ?></span>
                            </button>
                            <button type="button" class="ds-mobile-save-product-btn ds-mobile-save-product-btn-apply ds-mobile-btn-with-icon" data-mobile-sheet-action="apply">
                                <span class="ds-mobile-btn-ic" aria-hidden="true">
                                    <svg viewBox="0 0 24 24" focusable="false" aria-hidden="true">
                                        <path d="M4 8h16l-1 11a1.5 1.5 0 0 1-1.5 1.4h-11A1.5 1.5 0 0 1 5 19L4 8z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/>
                                        <path d="M3 8l4.5-4h9L21 8" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
                                        <path d="M9.2 12.6l1.8 1.8 3.9-3.9" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                                    </svg>
                                </span>
                                <span><?php esc_html_e('Apply to Product', 'ds'); ?></span>
                            </button>
                        </div>
                        <div class="ds-mobile-sheet-section">
                            <h5><?php esc_html_e('Design Library', 'ds'); ?></h5>
                        <div class="ds-mobile-template-picker">
                            <label for="ds-mobile-template-select"><?php esc_html_e('Template', 'ds'); ?></label>
                            <select id="ds-mobile-template-select" data-role="mobile-template-select" aria-label="<?php esc_attr_e('Choose template', 'ds'); ?>">
                                <option value=""><?php esc_html_e('Choose Template', 'ds'); ?></option>
                                <?php foreach ($editor_templates as $tpl): ?>
                                    <?php
                                    $tpl_id = isset($tpl['id']) ? (int) $tpl['id'] : 0;
                                    if ($tpl_id <= 0) continue;
                                    $tpl_title = isset($tpl['title']) ? (string) $tpl['title'] : ('Template #' . $tpl_id);
                                    $tpl_w = isset($tpl['width_mm']) ? (float) $tpl['width_mm'] : 0;
                                    $tpl_h = isset($tpl['height_mm']) ? (float) $tpl['height_mm'] : 0;
                                    $tpl_size = ($tpl_w > 0 && $tpl_h > 0) ? sprintf(' (%.0fx%.0f mm)', $tpl_w, $tpl_h) : '';
                                    ?>
                                    <option value="<?php echo esc_attr($tpl_id); ?>" <?php selected($active_template_id, $tpl_id); ?>>
                                        <?php echo esc_html($tpl_title . $tpl_size); ?>
                                    </option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <input type="number" min="1" placeholder="<?php esc_attr_e('Design ID', 'ds'); ?>" data-role="mobile-design-id" value="<?php echo esc_attr($load_design_id ?: ''); ?>" />
                        <button type="button" data-mobile-sheet-action="my-designs"><?php esc_html_e('My Designs', 'ds'); ?></button>
                        <button type="button" data-mobile-sheet-action="load-design"><?php esc_html_e('Load Design', 'ds'); ?></button>
                        <button type="button" data-mobile-sheet-action="template-apply"><?php esc_html_e('Template', 'ds'); ?></button>
                        </div>
                    </div>
                </section>
                <section class="ds-mobile-sheet" data-mobile-sheet="preview" aria-label="<?php esc_attr_e('Preview & Zones Tools', 'ds'); ?>">
                    <div class="ds-mobile-sheet-head">
                        <h4><?php esc_html_e('Preview & Zones', 'ds'); ?></h4>
                        <button type="button" data-mobile-sheet-close="1"><?php esc_html_e('Close', 'ds'); ?></button>
                    </div>
                    <div class="ds-mobile-sheet-grid">
                        <div class="ds-mobile-sheet-section">
                            <h5><?php esc_html_e('Preview & Zones', 'ds'); ?></h5>
                            <button type="button" data-mobile-sheet-action="mug-open"><?php esc_html_e('Preview Product', 'ds'); ?></button>
                            <button type="button" data-mobile-sheet-action="open-zones"><?php esc_html_e('Zone Tools', 'ds'); ?></button>
                        </div>
                    </div>
                </section>
                <section class="ds-mobile-sheet" data-mobile-sheet="edit" aria-label="<?php esc_attr_e('Edit Tools', 'ds'); ?>">
                    <div class="ds-mobile-sheet-head">
                        <h4><?php esc_html_e('Edit', 'ds'); ?></h4>
                        <button type="button" data-mobile-sheet-close="1"><?php esc_html_e('Close', 'ds'); ?></button>
                    </div>
                    <div class="ds-mobile-sheet-grid"></div>
                </section>
                <section class="ds-mobile-sheet" data-mobile-sheet="style" aria-label="<?php esc_attr_e('Style Tools', 'ds'); ?>">
                    <div class="ds-mobile-sheet-head">
                        <h4><?php esc_html_e('Style', 'ds'); ?></h4>
                        <button type="button" data-mobile-sheet-close="1"><?php esc_html_e('Close', 'ds'); ?></button>
                    </div>
                    <div class="ds-mobile-sheet-grid"></div>
                </section>
                <section class="ds-mobile-sheet" data-mobile-sheet="arrange" aria-label="<?php esc_attr_e('Arrange Tools', 'ds'); ?>">
                    <div class="ds-mobile-sheet-head">
                        <h4><?php esc_html_e('Arrange', 'ds'); ?></h4>
                        <button type="button" data-mobile-sheet-close="1"><?php esc_html_e('Close', 'ds'); ?></button>
                    </div>
                    <div class="ds-mobile-sheet-grid">
                        <div class="ds-mobile-sheet-section">
                            <h5><?php esc_html_e('Horizontal', 'ds'); ?></h5>
                            <button type="button" data-mobile-sheet-action="align-left"><?php esc_html_e('Align Left', 'ds'); ?></button>
                            <button type="button" data-mobile-sheet-action="align-center"><?php esc_html_e('Align Center', 'ds'); ?></button>
                            <button type="button" data-mobile-sheet-action="align-right"><?php esc_html_e('Align Right', 'ds'); ?></button>
                        </div>
                        <div class="ds-mobile-sheet-section">
                            <h5><?php esc_html_e('Vertical', 'ds'); ?></h5>
                            <button type="button" data-mobile-sheet-action="align-top"><?php esc_html_e('Align Top', 'ds'); ?></button>
                            <button type="button" data-mobile-sheet-action="align-middle"><?php esc_html_e('Align Middle', 'ds'); ?></button>
                            <button type="button" data-mobile-sheet-action="align-bottom"><?php esc_html_e('Align Bottom', 'ds'); ?></button>
                        </div>
                        <div class="ds-mobile-sheet-section">
                            <h5><?php esc_html_e('Layers', 'ds'); ?></h5>
                            <button type="button" data-mobile-sheet-action="layer-up"><?php esc_html_e('Layer Up', 'ds'); ?></button>
                            <button type="button" data-mobile-sheet-action="layer-down"><?php esc_html_e('Layer Down', 'ds'); ?></button>
                            <button type="button" data-mobile-sheet-action="layer-front"><?php esc_html_e('Bring Front', 'ds'); ?></button>
                            <button type="button" data-mobile-sheet-action="layer-back"><?php esc_html_e('Send Back', 'ds'); ?></button>
                        </div>
                    </div>
                </section>
                <section class="ds-mobile-sheet" data-mobile-sheet="more" aria-label="<?php esc_attr_e('More Object Tools', 'ds'); ?>">
                    <div class="ds-mobile-sheet-head">
                        <h4><?php esc_html_e('More', 'ds'); ?></h4>
                        <button type="button" data-mobile-sheet-close="1"><?php esc_html_e('Close', 'ds'); ?></button>
                    </div>
                    <div class="ds-mobile-sheet-grid"></div>
                </section>
            </div>
            <script>
            (function() {
              var root = document.currentScript && document.currentScript.closest ? document.currentScript.closest('.ds-studio') : null;
              if (!root) return;
              // Emergency inline mobile bridge stays as backup only.
              // Handlers below already no-op when main editor bridge is ready.
              window.setTimeout(function() {
                if (root.__dsMobileBridgeReady) return;
                if (root.__dsMobileEmergencyBound) return;
                root.__dsMobileEmergencyBound = true;

                function isMobilePortrait() {
                  try {
                    return window.matchMedia('(max-width:900px)').matches;
                  } catch (e) { return false; }
                }
                function find(sel) { return root.querySelector(sel); }
                function click(sel) {
                  var el = find(sel);
                  if (!el || typeof el.click !== 'function') return false;
                  el.click();
                  return true;
                }
                function symbolGlyphForAction(action) {
                  var a = String(action || '');
                  var glyphMap = {
                    // Emoticons
                    'emoji-smile':'\uD83D\uDE42','emoji-heart':'\u2764\uFE0F','emoji-fire':'\uD83D\uDD25','emoji-star':'\u2B50',
                    'emoji-cool':'\uD83D\uDE0E','emoji-party':'\uD83C\uDF89','emoji-check':'\u2705','emoji-thumbs-up':'\uD83D\uDC4D',
                    'emoji-lightbulb':'\uD83D\uDCA1','emoji-rocket':'\uD83D\uDE80','emoji-gift':'\uD83C\uDF81','emoji-camera':'\uD83D\uDCF7',
                    'emoji-sparkles':'\u2728','emoji-rainbow':'\uD83C\uDF08','emoji-sun':'\u2600\uFE0F','emoji-moon':'\uD83C\uDF19',
                    'emoji-flower':'\uD83C\uDF38','emoji-leaf':'\uD83C\uDF43','emoji-music':'\uD83C\uDFB5','emoji-headphones':'\uD83C\uDFA7',
                    'emoji-coffee':'\u2615','emoji-lightning':'\u26A1','emoji-target':'\uD83C\uDFAF','emoji-trophy':'\uD83C\uDFC6',
                    // Icons
                    'icon-heart':'\u2665','icon-star':'\u2605','icon-bolt':'\u26A1','icon-check':'\u2713','icon-arrow':'\u279C',
                    'icon-pin':'\u2316','icon-plus':'\uFF0B','icon-minus':'\u2212','icon-close':'\u2715','icon-cart':'\uD83D\uDED2',
                    'icon-user':'\uD83D\uDC64','icon-mail':'\u2709','icon-home':'\u2302','icon-search':'\u2315','icon-clock':'\uD83D\uDD52',
                    'icon-camera':'\uD83D\uDCF7','icon-gift':'\uD83C\uDF81','icon-phone':'\u260E','icon-globe':'\uD83C\uDF10',
                    'icon-tag':'\uD83C\uDFF7','icon-lock':'\uD83D\uDD12','icon-unlock':'\uD83D\uDD13','icon-flag':'\u2691',
                    'icon-location':'\uD83D\uDCCD','icon-bell':'\uD83D\uDD14','icon-settings':'\u2699','icon-cloud':'\u2601',
                    'icon-download':'\u2B07','icon-upload':'\u2B06','icon-play':'\u25B6','icon-pause':'\u275A\u275A',
                    'icon-refresh':'\u21BB','icon-link':'\uD83D\uDD17','icon-shield':'\uD83D\uDEE1','icon-bookmark':'\uD83D\uDD16',
                    'icon-chat':'\uD83D\uDCAC','icon-calendar':'\uD83D\uDCC5','icon-lightbulb':'\uD83D\uDCA1','icon-print':'\uD83D\uDDA8',
                    'icon-box':'\uD83D\uDCE6'
                  };
                  return glyphMap[a] || '';
                }
                function normalizeSymbolSheetText() {
                  var all = root.querySelectorAll('[data-mobile-sheet="emoticons"] .ds-mobile-icon-tile[data-mobile-sheet-action], [data-mobile-sheet="icons"] .ds-mobile-icon-tile[data-mobile-sheet-action]');
                  if (!all || !all.length) return;
                  Array.prototype.forEach.call(all, function(btn) {
                    var a = String(btn.getAttribute('data-mobile-sheet-action') || '');
                    var g = symbolGlyphForAction(a);
                    if (!g) return;
                    btn.textContent = g;
                  });
                }
                function setPropValue(prop, value) {
                  var el = find('.ds-props [data-prop="' + String(prop || '') + '"]');
                  if (!el) return false;
                  el.value = value;
                  try { el.dispatchEvent(new Event('input', { bubbles: true })); } catch (e1) {}
                  try { el.dispatchEvent(new Event('change', { bubbles: true })); } catch (e2) {}
                  return true;
                }
                function insertGlyphViaText(glyph) {
                  var g = String(glyph || '');
                  if (!g) return false;
                  if (!click('[data-action="add-text"]')) return false;
                  window.setTimeout(function() {
                    setPropValue('text', g);
                  }, 20);
                  return true;
                }
                function trigger(action) {
                  var a = String(action || '');
                  if (!a) return false;
                  if (typeof root.__dsResolveMobileAction === 'function') {
                    try {
                      var delegated = root.__dsResolveMobileAction(a);
                      if (delegated) return true;
                    } catch (eDeleg) {}
                  }
                  if (a.indexOf('emoji-') === 0 || a.indexOf('icon-') === 0) {
                    if (typeof root.__dsResolveMobileAction === 'function') {
                      try { return !!root.__dsResolveMobileAction(a); } catch (e) {}
                    }
                    if (a.indexOf('emoji-') === 0) {
                      var emSafe = {
                        'emoji-smile':'\uD83D\uDE42','emoji-heart':'\u2764\uFE0F','emoji-fire':'\uD83D\uDD25','emoji-star':'\u2B50',
                        'emoji-cool':'\uD83D\uDE0E','emoji-party':'\uD83C\uDF89','emoji-check':'\u2705','emoji-thumbs-up':'\uD83D\uDC4D',
                        'emoji-lightbulb':'\uD83D\uDCA1','emoji-rocket':'\uD83D\uDE80','emoji-gift':'\uD83C\uDF81','emoji-camera':'\uD83D\uDCF7',
                        'emoji-sparkles':'\u2728','emoji-rainbow':'\uD83C\uDF08','emoji-sun':'\u2600\uFE0F','emoji-moon':'\uD83C\uDF19',
                        'emoji-flower':'\uD83C\uDF38','emoji-leaf':'\uD83C\uDF43','emoji-music':'\uD83C\uDFB5','emoji-headphones':'\uD83C\uDFA7',
                        'emoji-coffee':'\u2615','emoji-lightning':'\u26A1','emoji-target':'\uD83C\uDFAF','emoji-trophy':'\uD83C\uDFC6'
                      };
                      if (typeof root.__dsInsertEmoji === 'function' && emSafe[a]) {
                        try { return !!root.__dsInsertEmoji(emSafe[a]); } catch (eSafe1) {}
                      }
                      if (emSafe[a]) return insertGlyphViaText(emSafe[a]);
                    }
                    if (a.indexOf('icon-') === 0) {
                      var iconSafeName = String(a).replace(/^icon-/, '');
                      if (typeof root.__dsInsertIcon === 'function') {
                        try { return !!root.__dsInsertIcon(iconSafeName); } catch (eSafe2) {}
                      }
                      var iconSafeGlyph = {
                        'heart':'\u2665','star':'\u2605','bolt':'\u26A1','check':'\u2713','arrow':'\u279C','pin':'\u2316',
                        'plus':'\uFF0B','minus':'\u2212','close':'\u2715','cart':'\uD83D\uDED2','user':'\uD83D\uDC64','mail':'\u2709',
                        'home':'\u2302','search':'\u2315','clock':'\uD83D\uDD52','camera':'\uD83D\uDCF7','gift':'\uD83C\uDF81',
                        'phone':'\u260E','globe':'\uD83C\uDF10','tag':'\uD83C\uDFF7','lock':'\uD83D\uDD12','unlock':'\uD83D\uDD13',
                        'flag':'\u2691','location':'\uD83D\uDCCD'
                      };
                      if (iconSafeGlyph[iconSafeName]) return insertGlyphViaText(iconSafeGlyph[iconSafeName]);
                    }
                    if (a.indexOf('emoji-') === 0 && typeof root.__dsInsertEmoji === 'function') {
                      var em = {
                        'emoji-smile':'Ã°Å¸â„¢â€š','emoji-heart':'Ã¢ÂÂ¤Ã¯Â¸Â','emoji-fire':'Ã°Å¸â€Â¥','emoji-star':'Ã¢Â­Â','emoji-cool':'Ã°Å¸ËœÅ½','emoji-party':'Ã°Å¸Å½â€°',
                        'emoji-check':'Ã¢Å“â€¦','emoji-thumbs-up':'Ã°Å¸â€˜Â','emoji-lightbulb':'Ã°Å¸â€™Â¡','emoji-rocket':'Ã°Å¸Å¡â‚¬','emoji-gift':'Ã°Å¸Å½Â','emoji-camera':'Ã°Å¸â€œÂ·',
                        'emoji-sparkles':'Ã¢Å“Â¨','emoji-rainbow':'Ã°Å¸Å’Ë†','emoji-sun':'Ã¢Ëœâ‚¬Ã¯Â¸Â','emoji-moon':'Ã°Å¸Å’â„¢','emoji-flower':'Ã°Å¸Å’Â¸','emoji-leaf':'Ã°Å¸ÂÆ’',
                        'emoji-music':'Ã°Å¸Å½Âµ','emoji-headphones':'Ã°Å¸Å½Â§','emoji-coffee':'Ã¢Ëœâ€¢','emoji-lightning':'Ã¢Å¡Â¡','emoji-target':'Ã°Å¸Å½Â¯','emoji-trophy':'Ã°Å¸Ââ€ '
                      };
                      if (em[a]) { try { return !!root.__dsInsertEmoji(em[a]); } catch (e2) {} }
                    }
                    if (a.indexOf('icon-') === 0 && typeof root.__dsInsertIcon === 'function') {
                      try { return !!root.__dsInsertIcon(String(a).replace(/^icon-/, '')); } catch (e3) {}
                    }
                    return false;
                  }
                  if (a === 'hard-refresh') {
                    try { var u = new URL(window.location.href); u.searchParams.set('_dsr', String(Date.now())); window.location.href = u.toString(); return true; } catch (e) { window.location.reload(); return true; }
                  }
                  if (a === 'upload-image') return click('input[data-action="upload-image"]');
                  if (a === 'upload-svg') return click('input[data-action="upload-svg"]');
                  if (a === 'save') return click('[data-action="save"]') || click('[data-action="final-save"]');
                  if (a === 'final-save') return click('[data-action="final-save"]');
                  if (a === 'apply') return click('[data-action="apply-product"]');
                  if (a === 'undo') return click('[data-action="undo"]');
                  if (a === 'redo') return click('[data-action="redo"]');
                  if (a === 'add-text') return click('[data-action="add-text"]');
                  if (a === 'open-textures') return click('[data-action="open-textures"]');
                  if (a === 'open-frames') return click('[data-action="open-frames"]');
                  if (a === 'open-emoticons') { normalizeSymbolSheetText(); openSheet('emoticons'); return true; }
                  if (a === 'open-icons') { normalizeSymbolSheetText(); openSheet('icons'); return true; }
                  if (a === 'my-designs') return click('[data-action="my-designs"]');
                  if (a === 'load-design') {
                    var mobileDid = find('[data-role="mobile-design-id"]');
                    var desktopDid = find('[data-input="design-id"]');
                    if (desktopDid && mobileDid) desktopDid.value = String(mobileDid.value || '').trim();
                    return click('[data-action="load-design"]');
                  }
                  if (a === 'export') return click('[data-action="export"]');
                  if (a === 'template-apply') {
                    var mobileTpl = find('[data-role="mobile-template-select"]');
                    var desktopTpl = find('[data-input="template-select"]');
                    var selectedId = mobileTpl ? String(mobileTpl.value || '') : '';
                    if (desktopTpl && selectedId !== '') {
                      desktopTpl.value = selectedId;
                      try { desktopTpl.dispatchEvent(new Event('change', { bubbles: true })); } catch (eTpl1) {}
                    }
                    if (selectedId !== '') {
                      try {
                        var uTpl = new URL(window.location.href);
                        uTpl.searchParams.set('ds_template', selectedId);
                        uTpl.searchParams.set('ds_pick', '1');
                        window.location.href = uTpl.toString();
                        return true;
                      } catch (eTpl2) {}
                    }
                    return click('[data-action="template-apply"]');
                  }
                  if (a === 'fit-canvas') return click('[data-action="fit-canvas"]');
                  if (a === 'zoom-reset') return click('[data-action="zoom-reset"]');
                  if (a === 'toggle-fullscreen') return click('[data-action="toggle-fullscreen"]');
                  if (a === 'toggle-grid') return click('input[data-opt="grid"]');
                  if (a === 'toggle-snap') return click('input[data-opt="snap"]');
                  if (a === 'toggle-bleed-marks') return click('input[data-opt="bleed-marks"]');
                  if (a === 'mug-open') {
                    return click('[data-action="mug-open"]')
                      || click('[data-role="mug-canvas-mini"]');
                  }
                  if (a === 'open-zones') {
                    return click('.ds-zone-preview')
                      || click('[data-action="toggle-zones"]')
                      || click('[data-action="zone-picker-open"]')
                      || click('[data-action="object-props"]');
                  }
                  if (a === 'object-props') return click('[data-action="object-props"]');
                  if (a === 'edit-text') {
                    if (typeof root.__dsEnterTextEdit === 'function') {
                      try { if (root.__dsEnterTextEdit()) return true; } catch (e4) {}
                    }
                    return click('[data-action="object-props"]');
                  }
                  if (a === 'group') return click('[data-action="group"]');
                  if (a === 'ungroup') return click('[data-action="ungroup"]');
                  if (a === 'mask') return click('[data-action="mask"]');
                  if (a === 'crop-image') return click('[data-action="crop-image"]');
                  if (a === 'ai-remove-bg') return click('[data-action="ai-remove-bg"]');
                  if (a === 'duplicate-object') return click('[data-action="duplicate-object"]');
                  if (a === 'delete-object') return click('[data-action="delete-object"]');
                  if (a === 'layer-up') return click('[data-action="layer-up"]');
                  if (a === 'layer-down') return click('[data-action="layer-down"]');
                  if (a === 'layer-front') return click('[data-action="layer-front"]');
                  if (a === 'layer-back') return click('[data-action="layer-back"]');
                  if (a === 'align-left') return click('[data-align="left"]');
                  if (a === 'align-center') return click('[data-align="center"]');
                  if (a === 'align-right') return click('[data-align="right"]');
                  if (a === 'align-top') return click('[data-align="top"]');
                  if (a === 'align-middle') return click('[data-align="middle"]');
                  if (a === 'align-bottom') return click('[data-align="bottom"]');
                  if (a === 'shape-rect') return click('[data-shape="rect"]');
                  if (a === 'shape-circle') return click('[data-shape="circle"]');
                  if (a === 'shape-triangle') return click('[data-shape="triangle"]');
                  if (a === 'shape-star') return click('[data-shape="star"]');
                  if (a === 'shape-heart') return click('[data-shape="heart"]');
                  if (a === 'shape-polygon') return click('[data-shape="polygon"]');
                  return false;
                }
                function closeSheets() {
                  var w = find('[data-role="mobile-sheets"]');
                  var b = find('[data-role="mobile-sheet-backdrop"]');
                  var c = find('[data-role="mobile-apply-cta"]');
                  root.classList.remove('ds-mobile-sheet-open');
                  root.removeAttribute('data-mobile-sheet-open');
                  if (w) { w.setAttribute('aria-hidden', 'true'); w.querySelectorAll('.ds-mobile-sheet').forEach(function(s){ s.classList.remove('is-active'); }); }
                  if (b) b.setAttribute('aria-hidden', 'true');
                  if (c) c.setAttribute('aria-hidden', 'false');
                }
                function resolveSheetKey(navAction, navBtn) {
                  var action = String(navAction || '');
                  var mode = String(root.getAttribute('data-mobile-mode') || 'canvas').toLowerCase();
                  var label = '';
                  if (navBtn) {
                    label = String(navBtn.getAttribute('data-label') || navBtn.textContent || '').trim().toLowerCase();
                  }
                  var objectByLabel = (label === 'edit' || label === 'style' || label === 'arrange' || label === 'more');
                  if (objectByLabel) mode = 'object';
                  if (mode === 'object') {
                    try {
                      if (typeof root.__dsApplyObjectSheets === 'function') root.__dsApplyObjectSheets();
                    } catch (e) {}
                    if (action === 'insert') return 'edit';
                    if (action === 'canvas') return 'style';
                    if (action === 'library') return 'arrange';
                    if (action === 'preview') return 'more';
                  }
                  return action;
                }
                function isDirect3DPreview() {
                  var b = window.DS_BOOT || {};
                  var mode = String(b.preview_mode || '').toLowerCase();
                  var type = String(b.preview_type || '').toLowerCase();
                  if (!mode) mode = (type === 'mug' || type === 'bottle' || type === 'tshirt') ? '3d' : '2d';
                  return mode === '3d' && (type === 'mug' || type === 'bottle' || type === 'tshirt');
                }
                function applyPropProxy(prop, value) {
                  var el = find('.ds-props [data-prop="' + String(prop || '') + '"]');
                  if (!el) return false;
                  el.value = value;
                  try { el.dispatchEvent(new Event('input', { bubbles: true })); } catch (e1) {}
                  try { el.dispatchEvent(new Event('change', { bubbles: true })); } catch (e2) {}
                  return true;
                }
                function syncMobileSheetValuesFromProps(sheetKey) {
                  var sheet = find('[data-mobile-sheet="' + String(sheetKey || '') + '"]');
                  if (!sheet) return;
                  var grid = sheet.querySelector('.ds-mobile-sheet-grid');
                  if (!grid) return;
                  function propEl(name) { return find('.ds-props [data-prop="' + String(name || '') + '"]'); }
                  function read(name) {
                    var el = propEl(name);
                    if (!el) return '';
                    return String(el.value == null ? '' : el.value);
                  }
                  function setVal(sel, val) {
                    var input = grid.querySelector(sel);
                    if (!input) return;
                    if (input.tagName && String(input.tagName).toLowerCase() === 'select') {
                      var has = false;
                      Array.prototype.forEach.call(input.options || [], function(opt) {
                        if (String(opt.value || '') === String(val || '')) has = true;
                      });
                      if (!has && String(val || '') !== '') {
                        var extra = document.createElement('option');
                        extra.value = String(val || '');
                        extra.textContent = String(val || '');
                        input.appendChild(extra);
                      }
                    }
                    input.value = String(val == null ? '' : val);
                  }
                  if (sheetKey === 'style') {
                    setVal('[data-mobile-style-prop="fontFamily"]', read('fontFamily'));
                    setVal('[data-mobile-style-prop="fontSize"]', read('fontSize'));
                    setVal('[data-mobile-style-prop="fill"]', read('fill'));
                    setVal('[data-mobile-style-prop="stroke"]', read('stroke'));
                    setVal('[data-mobile-style-prop="strokeWidth"]', read('strokeWidth'));
                    setVal('[data-mobile-style-prop="strokeSide"]', read('strokeSide'));
                    setVal('[data-mobile-style-prop="opacity"]', read('opacity'));
                    setVal('[data-mobile-style-prop="cornerRadius"]', read('cornerRadius'));
                    return;
                  }
                  if (sheetKey === 'edit') {
                    setVal('[data-mobile-arrange-prop="angle"]', read('angle'));
                    return;
                  }
                  if (sheetKey === 'arrange') {
                    setVal('[data-mobile-arrange-prop="angle"]', read('angle'));
                  }
                }
                function syncCanvasHelperToggleButtons() {
                  var map = [
                    { action: 'toggle-grid', opt: 'grid' },
                    { action: 'toggle-snap', opt: 'snap' },
                    { action: 'toggle-bleed-marks', opt: 'bleed-marks' }
                  ];
                  map.forEach(function(item) {
                    var cb = find('.ds-toolbar input[data-opt="' + item.opt + '"]') || find('input[data-opt="' + item.opt + '"]');
                    var on = !!(cb && cb.checked);
                    var buttons = root.querySelectorAll('[data-mobile-sheet-action="' + item.action + '"]');
                    if (!buttons || !buttons.length) return;
                    Array.prototype.forEach.call(buttons, function(btn) {
                      btn.classList.toggle('is-active', on);
                      btn.setAttribute('aria-pressed', on ? 'true' : 'false');
                    });
                  });
                }
                function fontOptionsHtml() {
                  var src = find('.ds-props [data-prop="fontFamily"]');
                  if (!src || !src.options) return '';
                  var html = '';
                  Array.prototype.forEach.call(src.options, function(opt) {
                    var v = String(opt.value || '');
                    var t = String(opt.textContent || v);
                    html += '<option value="' + v.replace(/"/g, '&quot;') + '">' + t.replace(/</g, '&lt;').replace(/>/g, '&gt;') + '</option>';
                  });
                  return html;
                }
                function ensureObjectSheetContent(sheetKey) {
                  if (typeof root.__dsApplyObjectSheets === 'function') {
                    try { root.__dsApplyObjectSheets(); return; } catch (e0) {}
                  }
                  var sheet = find('[data-mobile-sheet="' + String(sheetKey || '') + '"]');
                  if (!sheet) return;
                  var grid = sheet.querySelector('.ds-mobile-sheet-grid');
                  if (!grid) return;
                  var html = '';
                  var commonEditControls = '<label class="ds-mobile-style-field">Rotate<input type="number" step="1" data-mobile-arrange-prop="angle"></label>'
                    + '<button type="button" data-mobile-sheet-action="duplicate-object">Duplicate</button>'
                    + '<button type="button" data-mobile-sheet-action="delete-object">Delete</button>';
                  if (sheetKey === 'edit') {
                    html += '<button type="button" data-mobile-sheet-action="crop-image">Crop</button>';
                    html += '<button type="button" data-mobile-sheet-action="ai-remove-bg">AI Remove BG</button>';
                    html += '<button type="button" data-mobile-sheet-action="group">Group</button>';
                    html += '<button type="button" data-mobile-sheet-action="ungroup">Ungroup</button>';
                    html += '<button type="button" data-mobile-sheet-action="mask">Mask</button>';
                    html += commonEditControls;
                  } else if (sheetKey === 'style') {
                    html += '<label class="ds-mobile-style-field">Font<select data-mobile-style-prop="fontFamily">' + fontOptionsHtml() + '</select></label>';
                    html += '<label class="ds-mobile-style-field">Size<input type="number" min="1" step="1" data-mobile-style-prop="fontSize"></label>';
                    html += '<label class="ds-mobile-style-field">Fill<input type="color" data-mobile-style-prop="fill"></label>';
                    html += '<label class="ds-mobile-style-field">Stroke<input type="color" data-mobile-style-prop="stroke"></label>';
                    html += '<label class="ds-mobile-style-field">Stroke W<input type="number" min="0" step="1" data-mobile-style-prop="strokeWidth"></label>';
                    html += '<label class="ds-mobile-style-field">Stroke Side<select data-mobile-style-prop="strokeSide"><option value="inside">Inside</option><option value="outside">Outside</option></select></label>';
                    html += '<label class="ds-mobile-style-field">Corners<input type="number" min="0" step="1" data-mobile-style-prop="cornerRadius"></label>';
                    html += '<label class="ds-mobile-style-field">Opacity<input type="range" min="0" max="100" step="1" data-mobile-style-prop="opacity"></label>';
                  } else if (sheetKey === 'arrange') {
                    html += '<label class="ds-mobile-style-field">Rotation<input type="number" step="1" data-mobile-arrange-prop="angle"></label>';
                    html += '<button type="button" data-mobile-sheet-action="align-left">Align Left</button>';
                    html += '<button type="button" data-mobile-sheet-action="align-center">Align Center</button>';
                    html += '<button type="button" data-mobile-sheet-action="align-right">Align Right</button>';
                    html += '<button type="button" data-mobile-sheet-action="align-top">Align Top</button>';
                    html += '<button type="button" data-mobile-sheet-action="align-middle">Align Middle</button>';
                    html += '<button type="button" data-mobile-sheet-action="align-bottom">Align Bottom</button>';
                    html += '<button type="button" data-mobile-sheet-action="layer-up">Layer Up</button>';
                    html += '<button type="button" data-mobile-sheet-action="layer-down">Layer Down</button>';
                    html += '<button type="button" data-mobile-sheet-action="layer-front">Bring Front</button>';
                    html += '<button type="button" data-mobile-sheet-action="layer-back">Send Back</button>';
                    html += '<div class="ds-mobile-sheet-section ds-mobile-layer-section"><h5>Layers</h5><ul class="ds-layer-list ds-layer-list-inline" data-role="arrange-layer-list"></ul></div>';
                  } else if (sheetKey === 'more') {
                    html += '<button type="button" data-mobile-sheet-action="object-props">Object Properties</button>';
                  }
                  var selectedType = '';
                  try {
                    if (root.__dsEditorUiState && root.__dsEditorUiState.selectedObjectType) {
                      selectedType = String(root.__dsEditorUiState.selectedObjectType || '').toLowerCase();
                    }
                    if (!selectedType && typeof root.__dsDetectSelectedType === 'function') {
                      selectedType = String(root.__dsDetectSelectedType() || '').toLowerCase();
                    }
                  } catch (eType) {}
                  if (sheetKey === 'edit' && selectedType) {
                    if (selectedType === 'text') {
                      html = '<button type="button" data-mobile-sheet-action="edit-text">Edit Text</button>' + commonEditControls;
                    } else if (selectedType === 'image') {
                      html = '<button type="button" data-mobile-sheet-action="crop-image">Crop</button>' +
                        '<button type="button" data-mobile-sheet-action="ai-remove-bg">AI Remove BG</button>' +
                        commonEditControls;
                    } else if (selectedType === 'multi') {
                      html = '<button type="button" data-mobile-sheet-action="group">Group</button>' +
                        '<button type="button" data-mobile-sheet-action="mask">Mask</button>';
                    } else if (selectedType === 'group') {
                      html = '<button type="button" data-mobile-sheet-action="ungroup">Ungroup</button>' + commonEditControls;
                    } else {
                      html = commonEditControls;
                    }
                  }
                  if (sheetKey === 'style' && selectedType === 'image') {
                    html = '<label class="ds-mobile-style-field">Opacity<input type="range" min="0" max="100" step="1" data-mobile-style-prop="opacity"></label>' +
                      '<label class="ds-mobile-style-field">Stroke<input type="color" data-mobile-style-prop="stroke"></label>' +
                      '<label class="ds-mobile-style-field">Stroke W<input type="number" min="0" step="1" data-mobile-style-prop="strokeWidth"></label>' +
                      '<label class="ds-mobile-style-field">Corners<input type="number" min="0" step="1" data-mobile-style-prop="cornerRadius"></label>';
                  }
                  if (html !== '') grid.innerHTML = html;
                }
                function openSheet(key) {
                  if (root.__dsMobileBridgeReady) return;
                  var w = find('[data-role="mobile-sheets"]');
                  var b = find('[data-role="mobile-sheet-backdrop"]');
                  var c = find('[data-role="mobile-apply-cta"]');
                  if (!w || !b) return;
                  ensureObjectSheetContent(key);
                  var sheet = w.querySelector('[data-mobile-sheet="' + key + '"]');
                  if (!sheet) return;
                  root.classList.add('ds-mobile-sheet-open');
                  root.setAttribute('data-mobile-sheet-open', key);
                  w.setAttribute('aria-hidden', 'false');
                  b.setAttribute('aria-hidden', 'false');
                  w.querySelectorAll('.ds-mobile-sheet').forEach(function(s){ s.classList.toggle('is-active', s === sheet); });
                  syncMobileSheetValuesFromProps(key);
                  if (key === 'canvas') syncCanvasHelperToggleButtons();
                  if (c) c.setAttribute('aria-hidden', 'true');
                }

                root.addEventListener('click', function(e) {
                  if (root.__dsMobileBridgeReady) return;
                  if (!isMobilePortrait()) return;
                  var top = e.target && e.target.closest ? e.target.closest('[data-role="mobile-topbar"] [data-mobile-action]') : null;
                  if (top) { e.preventDefault(); trigger(top.getAttribute('data-mobile-action')); return; }
                  var cta = e.target && e.target.closest ? e.target.closest('[data-role="mobile-apply-cta"]') : null;
                  if (cta) { e.preventDefault(); trigger('apply'); return; }
                  var nav = e.target && e.target.closest ? e.target.closest('[data-role="mobile-bottom-nav"] [data-mobile-action]') : null;
                  if (nav) {
                    e.preventDefault();
                    var navAction = String(nav.getAttribute('data-mobile-action') || '');
                    if (navAction === 'preview' && isDirect3DPreview()) {
                      trigger('mug-open');
                      return;
                    }
                    openSheet(resolveSheetKey(navAction, nav));
                    return;
                  }
                  var close = e.target && e.target.closest ? e.target.closest('[data-mobile-sheet-close]') : null;
                  if (close) { e.preventDefault(); closeSheets(); return; }
                  var act = e.target && e.target.closest ? e.target.closest('[data-mobile-sheet-action]') : null;
                  if (act) {
                    e.preventDefault();
                    var a = String(act.getAttribute('data-mobile-sheet-action') || '');
                    if (a === 'toggle-grid' || a === 'toggle-snap' || a === 'toggle-bleed-marks') {
                      trigger(a);
                      window.setTimeout(syncCanvasHelperToggleButtons, 0);
                      return;
                    }
                    if (a === 'open-shapes' || a === 'open-emoticons' || a === 'open-icons') {
                      trigger(a);
                      return;
                    }
                    var handled = trigger(a);
                    if (handled) closeSheets();
                    return;
                  }
                  var bd = e.target && e.target.closest ? e.target.closest('[data-role="mobile-sheet-backdrop"]') : null;
                  if (bd) { e.preventDefault(); closeSheets(); }
                }, true);
                root.addEventListener('input', function(e) {
                  if (root.__dsMobileBridgeReady) return;
                  if (!isMobilePortrait()) return;
                  var zoomInput = e.target && e.target.closest ? e.target.closest('[data-role="mobile-zoom-slider"]') : null;
                  if (zoomInput) {
                    var baseZoom = find('[data-input="zoom"]');
                    if (baseZoom) {
                      baseZoom.value = String(zoomInput.value || '100');
                      try { baseZoom.dispatchEvent(new Event('input', { bubbles: true })); } catch (eZi1) {}
                      try { baseZoom.dispatchEvent(new Event('change', { bubbles: true })); } catch (eZi2) {}
                    }
                    var zoomLabel = find('[data-role="mobile-zoom-label"]');
                    if (zoomLabel) zoomLabel.textContent = String(Math.round(Number(zoomInput.value || 100))) + '%';
                    return;
                  }
                  var styleInput = e.target && e.target.closest ? e.target.closest('[data-mobile-style-prop], [data-mobile-arrange-prop]') : null;
                  if (!styleInput) return;
                  var p1 = String(styleInput.getAttribute('data-mobile-style-prop') || '');
                  var p2 = String(styleInput.getAttribute('data-mobile-arrange-prop') || '');
                  if (p1) applyPropProxy(p1, styleInput.value);
                  if (p2) applyPropProxy(p2, styleInput.value);
                }, true);
                root.addEventListener('change', function(e) {
                  if (root.__dsMobileBridgeReady) return;
                  if (!isMobilePortrait()) return;
                  var zoomInput = e.target && e.target.closest ? e.target.closest('[data-role="mobile-zoom-slider"]') : null;
                  if (!zoomInput) return;
                  var baseZoom = find('[data-input="zoom"]');
                  if (baseZoom) {
                    baseZoom.value = String(zoomInput.value || '100');
                    try { baseZoom.dispatchEvent(new Event('input', { bubbles: true })); } catch (eZc1) {}
                    try { baseZoom.dispatchEvent(new Event('change', { bubbles: true })); } catch (eZc2) {}
                  }
                  var zoomLabel = find('[data-role="mobile-zoom-label"]');
                  if (zoomLabel) zoomLabel.textContent = String(Math.round(Number(zoomInput.value || 100))) + '%';
                }, true);
                normalizeSymbolSheetText();
              }, 250);
            })();
            </script>
            <button type="button" class="ds-mobile-apply-cta ds-mobile-btn-with-icon" data-role="mobile-apply-cta" data-mobile-action="apply">
                <span class="ds-mobile-btn-ic" aria-hidden="true">
                    <svg viewBox="0 0 24 24" focusable="false" aria-hidden="true">
                        <path d="M4 8h16l-1 11a1.5 1.5 0 0 1-1.5 1.4h-11A1.5 1.5 0 0 1 5 19L4 8z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/>
                        <path d="M3 8l4.5-4h9L21 8" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
                        <path d="M9.2 12.6l1.8 1.8 3.9-3.9" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                    </svg>
                </span>
                <span><?php esc_html_e('Apply to Product', 'ds'); ?></span>
            </button>
        </div>
        <?php if ($embed_mode): ?>
            </div>
        <?php endif; ?>
        <?php
        return ob_get_clean();
    }

    private static function read_template_json_meta($template_id, $meta_key, $fallback) {
        $raw = (string) get_post_meta($template_id, $meta_key, true);
        if ($raw === '') {
            return $fallback;
        }
        $decoded = json_decode($raw, true);
        if (json_last_error() !== JSON_ERROR_NONE || !is_array($decoded)) {
            return $fallback;
        }
        return $decoded;
    }

    private static function get_editor_templates() {
        $statuses = ['publish', 'private'];
        $include_unpublished = (bool) apply_filters('ds_editor_include_unpublished_templates', false);
        if ($include_unpublished && current_user_can('edit_posts')) {
            $statuses = ['publish', 'private', 'draft', 'pending'];
        }
        $posts = get_posts([
            'post_type' => 'ds_template',
            'post_status' => $statuses,
            'numberposts' => -1,
            'orderby' => 'title',
            'order' => 'ASC',
        ]);
        $items = [];
        foreach ($posts as $p) {
            $thumb_url = get_the_post_thumbnail_url($p->ID, 'medium');
            $items[] = [
                'id' => (int) $p->ID,
                'title' => get_the_title($p),
                'width_mm' => (float) get_post_meta($p->ID, 'width_mm', true),
                'height_mm' => (float) get_post_meta($p->ID, 'height_mm', true),
                'preview_type' => ((string) get_post_meta($p->ID, 'preview_type', true)) ?: 'mug',
                'thumb_url' => $thumb_url ? (string) $thumb_url : '',
            ];
        }
        return $items;
    }
}

