/*-------------------------------------------------------------------
 VMO AI Website Builder – Main Admin Script (REV—autosave+undo 2025‑06‑27)
-------------------------------------------------------------------*/


































































































































































































// Corrupted PHP content removed
console.log('🚀 VMO AI Builder UNIFIED v8.0.0 - Main Script Loading!');
(function ($) {
    // WordPress admin loads jQuery in noConflict mode; ensure `$` is jQuery inside this scope.
    $ = window.jQuery || $;
    if (typeof $ !== 'function') {
        console.error('[VMO] jQuery not available; aborting vmo-main.js init');
        return;
    }
    // 🔒 GATEKEEPER: FORCE PLAN SELECTION ON FIRST LOAD
    // If the user has no plan, redirect them to the "My Plan" page immediately.
    if (typeof vmo_ajax !== 'undefined') {
        const urlParams = new URLSearchParams(window.location.search);
        const page = urlParams.get('page');
        const isVmoPage = page && page.startsWith('vmo-');
        const isPlanPage = page === 'vmo-my-plan';
        const isPaymentSuccess = urlParams.get('payment') === 'success';
        const hasPlan = vmo_ajax.current_plan && vmo_ajax.current_plan !== '';

        if (isVmoPage && !isPlanPage && !hasPlan) {
            
            // Allow temporary access if returning from successful payment (to process activation)
            if (isPaymentSuccess) {
                console.log('💳 Payment success detected. Verifying activation...');
            } 
            // VMO: Bypass Check for Admin/Management Plan
            else if (vmo_ajax.user_roles && (vmo_ajax.user_roles.includes('administrator') || vmo_ajax.user_roles.includes('vmo_manager'))) {
                console.log('🛡️ Admin/Manager Bypass Active for Plan Check');
            }
            // VMO: Standard User Redirect
            else {
                console.warn('🔒 No active plan detected. Redirecting to Plan Selection...');

                // Show Toast Notification (if toast utility available, or fallback to alert/console)
                if (typeof vmo_toast_notification === 'function') {
                    vmo_toast_notification('Subscription Required', 'You need an active plan to access these features.', 'info');
                }
                
                // Create professional overlay
                const $overlay = $('<div>').css({
                    'position': 'fixed', 'top': 0, 'left': 0, 'width': '100vw', 'height': '100vh',
                    'background': '#0b0f1a', 'z-index': 999999, 'display': 'flex',
                    'flex-direction': 'column', 'align-items': 'center', 'justify-content': 'center',
                    'color': 'white', 'text-align': 'center', 'font-family': 'Inter, system-ui, sans-serif'
                }).appendTo('body');
                
                $('<h1>').text('Welcome to VMO Business Suite').css({'font-size':'32px', 'margin-bottom':'15px', 'font-weight':'700'}).appendTo($overlay);
                $('<p>').text('Please select a plan to activate your AI Consultant.').css({'font-size':'18px', 'color':'#a0aec0', 'margin-bottom':'30px'}).appendTo($overlay);
                
                // Spinner
                $('<div class="vmo-loader-spinner">').css({
                    'width':'40px', 'height':'40px', 'border':'3px solid rgba(255,255,255,0.1)',
                    'border-top-color':'#0fe1ff', 'border-radius':'50%', 'animation':'spin 1s linear infinite'
                }).appendTo($overlay);

                // Add keyframes if needed
                if (!$('style#vmo-spinner-style').length) {
                    $('<style id="vmo-spinner-style">@keyframes spin {100%{transform:rotate(360deg)}}</style>').appendTo('head');
                }

                // Redirect
                const adminBase = vmo_ajax.ajaxurl.replace('admin-ajax.php', 'admin.php');
                setTimeout(() => {
                     window.location.href = `${adminBase}?page=vmo-my-plan`;
                }, 1000);
                return; // Stop execution of the rest of the script
            }
        }
    }

    /* ========== GLOBALS & HELPERS ========== */
    const $doc  = $(document);
    const $wrap = $('#vmo-ai-builder-main');

    /* ========== GUARDRAIL 5B: HIGH-COST ACTION CONFIRMATION ========== */
    /**
     * Shows a confirmation dialog before expensive token operations.
     * Returns a Promise that resolves to true (proceed) or false (cancel).
     *
     * @param {string} actionName  Human-readable action name
     * @param {string} costKey     Key from vmo_ajax.token_costs
     * @returns {Promise<boolean>}
     */
    window.vmoConfirmTokenSpend = function(actionName, costKey) {
        const ajax = window.vmo_ajax || window.vmoAjax || {};
        const costs = ajax.token_costs || {};
        const balance = parseInt(ajax.token_balance, 10) || 0;
        const threshold = parseInt(ajax.high_cost_threshold, 10) || 1000;
        const cost = parseInt(costs[costKey], 10) || 0;

        // Skip confirmation for low-cost actions
        if (cost < threshold) return Promise.resolve(true);

        // Check if balance is insufficient
        if (balance < cost) {
            const upgradeUrl = ajax.upgradeUrl || ajax.topupUrl || '#';
            if (typeof window.showToast === 'function') {
                window.showToast(
                    `Insufficient balance for ${actionName}. Need ${cost.toLocaleString()} tokens, have ${balance.toLocaleString()}. <a href="${upgradeUrl}" style="color:#0fe1ff;text-decoration:underline">Upgrade or top up</a>.`,
                    'error'
                );
            }
            return Promise.resolve(false);
        }

        return new Promise(resolve => {
            const remaining = balance - cost;
            const pctRemaining = Math.round((remaining / balance) * 100);

            const $overlay = $('<div class="vmo-confirm-spend-overlay">').css({
                position: 'fixed', top: 0, left: 0, width: '100vw', height: '100vh',
                background: 'rgba(0,0,0,0.7)', zIndex: 999999, display: 'flex',
                alignItems: 'center', justifyContent: 'center', fontFamily: 'Inter, system-ui, sans-serif'
            });

            const $dialog = $('<div>').css({
                background: '#1a1f2e', borderRadius: '16px', padding: '32px', maxWidth: '420px',
                width: '90%', color: '#e2e8f0', textAlign: 'center', boxShadow: '0 20px 60px rgba(0,0,0,0.5)',
                border: '1px solid rgba(15,225,255,0.2)'
            }).appendTo($overlay);

            $('<h3>').text('Confirm Token Spend').css({ margin: '0 0 16px', fontSize: '20px', fontWeight: 700, color: '#0fe1ff' }).appendTo($dialog);
            $('<p>').html(
                `<strong>${actionName}</strong> will use <strong style="color:#f6ad55">${cost.toLocaleString()}</strong> tokens.<br>` +
                `Current balance: <strong>${balance.toLocaleString()}</strong> → Remaining: <strong>${remaining.toLocaleString()}</strong> (${pctRemaining}%)`
            ).css({ fontSize: '15px', lineHeight: '1.6', margin: '0 0 24px', color: '#a0aec0' }).appendTo($dialog);

            if (pctRemaining < 20) {
                $('<p>').text('⚠️ This will leave you with very low balance.').css({ color: '#fc8181', fontSize: '13px', margin: '0 0 20px' }).appendTo($dialog);
            }

            const $btnRow = $('<div>').css({ display: 'flex', gap: '12px', justifyContent: 'center' }).appendTo($dialog);

            $('<button>').text('Cancel').css({
                padding: '10px 24px', borderRadius: '8px', border: '1px solid #4a5568',
                background: 'transparent', color: '#a0aec0', cursor: 'pointer', fontSize: '14px'
            }).on('click', () => { $overlay.remove(); resolve(false); }).appendTo($btnRow);

            $('<button>').text('Proceed').css({
                padding: '10px 24px', borderRadius: '8px', border: 'none',
                background: 'linear-gradient(135deg, #0fe1ff, #0891b2)', color: '#0b0f1a',
                cursor: 'pointer', fontSize: '14px', fontWeight: 600
            }).on('click', () => { $overlay.remove(); resolve(true); }).appendTo($btnRow);

            $overlay.appendTo('body');
        });
    };

    /* ========== GUARDRAIL 5C: LOW-BALANCE PROACTIVE NUDGE ========== */
    (function initLowBalanceNudge() {
        const ajax = window.vmo_ajax || window.vmoAjax || {};
        const balance = parseInt(ajax.token_balance, 10) || 0;
        const remainingPercent = Number(ajax.credit_remaining_percent);
        const thresholdPercent = Number(ajax.low_credit_remaining_percent) || 40;
        const upgradeUrl = ajax.upgradeUrl || '#';
        const topupUrl = ajax.topupUrl || '#';
        const plan = ajax.current_plan || '';

        // Don't nudge users with no plan/no balance; they get the hard credit gatekeeper.
        if (!plan || plan === '' || balance <= 0 || !Number.isFinite(remainingPercent) || remainingPercent > thresholdPercent) return;

        // Only show once per session
        if (window._vmoLowBalanceNudgeShown) return;
        window._vmoLowBalanceNudgeShown = true;

        // Show a non-blocking banner after a short delay
        setTimeout(function() {
            const $banner = $('<div class="vmo-low-balance-nudge">').css({
                position: 'fixed', bottom: '20px', right: '20px', zIndex: 99999,
                background: 'linear-gradient(135deg, #1a1f2e, #2d3748)', borderRadius: '12px',
                padding: '16px 20px', maxWidth: '380px', color: '#e2e8f0',
                boxShadow: '0 8px 30px rgba(0,0,0,0.4)', border: '1px solid rgba(246,173,85,0.3)',
                fontFamily: 'Inter, system-ui, sans-serif', fontSize: '14px', lineHeight: '1.5',
                animation: 'vmoSlideIn 0.3s ease-out'
            });

            $('<div>').html(
                `<strong style="color:#f6ad55">Low VMO Credit</strong><br>` +
                `You have <strong>${remainingPercent.toFixed(remainingPercent % 1 === 0 ? 0 : 1)}%</strong> of your plan credit remaining. ` +
                `Consider <a href="${topupUrl}" style="color:#0fe1ff;text-decoration:underline">topping up</a> or ` +
                `<a href="${upgradeUrl}" style="color:#0fe1ff;text-decoration:underline">upgrading your plan</a> to avoid interruptions.`
            ).appendTo($banner);

            if (ajax.model_downgraded) {
                $('<div>').text('🔄 AI is using economy model to conserve your credits.').css({
                    marginTop: '8px', fontSize: '12px', color: '#a0aec0', fontStyle: 'italic'
                }).appendTo($banner);
            }

            const $close = $('<button>').text('✕').css({
                position: 'absolute', top: '8px', right: '10px', background: 'none',
                border: 'none', color: '#718096', cursor: 'pointer', fontSize: '16px'
            }).on('click', function() { $banner.remove(); });
            $banner.prepend($close);

            // Auto-dismiss after 15 seconds
            setTimeout(function() { $banner.fadeOut(300, function() { $(this).remove(); }); }, 15000);

            // Animation keyframes
            if (!$('style#vmo-nudge-style').length) {
                $('<style id="vmo-nudge-style">@keyframes vmoSlideIn{from{transform:translateX(100%);opacity:0}to{transform:translateX(0);opacity:1}}</style>').appendTo('head');
            }

            $banner.appendTo('body');
        }, 3000);
    })();

    window.lastContent             = window.lastContent || '';
    window.vmoMediaModalContext    = {};       // { mode, selector, mediaType, mediaId }

    function vmoGenerateMediaId () {
        return 'media-' + Math.random().toString(36).substr(2, 9) + '-' + Date.now();
    }
/* Build one HTML string from the current blocks – Mirrors original schema */
function vmoSyncLastContent () {
    var html = (window.vmoState?.blocks || [])
        .map(b => b.html)
        .join('\n');

    if (typeof window.vmoNormalizeBuilderHtml === 'function') {
        html = window.vmoNormalizeBuilderHtml(html);
    }

    if (typeof window.vmoShouldTreatAsRealContent === 'function' && !window.vmoShouldTreatAsRealContent(html, window.vmoState)) {
        window.lastContent = '';
        return;
    }

    window.lastContent = html;
}

    window.vmoSyncLastContent = vmoSyncLastContent;

    /* ensure each media/placeholder carries a stable id */
    function vmoEnsureMediaIdsInBlocks () {
        if (!Array.isArray(window.vmoState?.blocks)) return;
        window.vmoState.blocks.forEach(block => {
            if (!block.html) return;
            const $tmp = $('<div>'+block.html+'</div>');
            $tmp.find('.vmo-media-placeholder, img, video, audio').each(function () {
                const $el = $(this);
                if (!$el.attr('data-media-id')) $el.attr('data-media-id', vmoGenerateMediaId());
                if (!$el.attr('data-type'))    $el.attr('data-type', $el.is('video') ? 'video' : ($el.is('audio') ? 'audio' : 'image'));
                if (!$el.hasClass('vmo-media-placeholder')) $el.addClass('vmo-media-placeholder');
            });
            block.html = $tmp.html();
        });
    }
    $(vmoEnsureMediaIdsInBlocks);
    window.renderBlocks = (fn => function(){ if (typeof fn==='function') fn.apply(this,arguments); vmoEnsureMediaIdsInBlocks(); })(window.renderBlocks);
    vmoSyncLastContent();   // keep lastContent in step for save / publish

    /* ========== IFRAME OVERLAY BRIDGE ========== */
    function normalizeReplaceMediaType(mediaType) {
        return mediaType === 'video' ? 'video' : (mediaType === 'audio' ? 'audio' : 'image');
    }

    function readReplaceMediaTypeFromNode(node) {
        if (!node) return 'image';

        const tagName = String(node.tagName || '').toLowerCase();
        const rawType = String(node.getAttribute('data-type') || node.getAttribute('data-media-type') || '').toLowerCase();
        const source = String(node.getAttribute('src') || node.getAttribute('data-src') || node.getAttribute('data') || '').toLowerCase();

        if (rawType === 'audio' || tagName === 'audio' || /\.(mp3|wav|m4a|aac|oga)(\?|$)/i.test(source)) {
            return 'audio';
        }

        if (rawType === 'video' || node.hasAttribute('data-vmo-sora') || node.classList.contains('vmo-media-placeholder-video')) {
            return 'video';
        }

        if (tagName === 'video') {
            return 'video';
        }

        if (tagName === 'iframe' && /(youtube|vimeo|dailymotion|twitch|facebook|instagram)/i.test(source)) {
            return 'video';
        }

        if (/\.(mp4|webm|ogg|mov|m4v)(\?|$)/i.test(source)) {
            return 'video';
        }

        return 'image';
    }

    function resolveMediaPickerContext(payload) {
        const iframe = document.getElementById('vmo-preview-iframe');
        const doc = iframe && iframe.contentDocument ? iframe.contentDocument : null;
        const context = payload && typeof payload === 'object' && !Array.isArray(payload) ? Object.assign({}, payload) : {};
        let selector = '';
        let mediaType = '';
        let mediaId = '';

        if (payload && typeof payload === 'object' && !Array.isArray(payload)) {
            selector = String(payload.selector || payload.targetSelector || payload.target_selector || '');
            mediaType = String(payload.type || payload.mediaType || payload.media_type || '');
            mediaId = String(payload.mediaId || payload.media_id || payload.id || '');
        } else if (payload != null) {
            mediaId = String(payload);
        }

        if (!selector && mediaId) {
            selector = `[data-media-id="${mediaId}"]`;
        }

        if (!mediaId && selector && doc) {
            const node = doc.querySelector(selector);
            mediaId = node && node.dataset ? String(node.dataset.mediaId || '') : '';
        }

        if (!mediaType && selector && doc) {
            const node = doc.querySelector(selector);
            if (node) {
                mediaType = readReplaceMediaTypeFromNode(node);
            }
        }

        return {
            selector,
            mediaType: normalizeReplaceMediaType(mediaType),
            mediaId,
            slot_id: String(context.slot_id || context.slotId || context.media_id || context.mediaId || mediaId || ''),
            page_id: String(context.page_id || context.pageId || ''),
            section_id: String(context.section_id || context.sectionId || ''),
            product_id: String(context.product_id || context.productId || ''),
            placement_role: String(context.placement_role || context.placementRole || ''),
            current_attachment_id: String(context.current_attachment_id || context.currentAttachmentId || ''),
            current_src: String(context.current_src || context.currentSrc || ''),
            block_id: String(context.block_id || context.blockId || ''),
            target_tag: String(context.target_tag || context.targetTag || ''),
            allowed_replacement_types: Array.isArray(context.allowed_replacement_types)
                ? context.allowed_replacement_types
                : (Array.isArray(context.allowedReplacementTypes) ? context.allowedReplacementTypes : []),
            source: String(context.source || 'builder_preview')
        };
    }

    window.vmoOpenMediaPicker = function(payload) {
        const context = resolveMediaPickerContext(payload);

        if (!context.selector) {
            if (typeof showToast === 'function') {
                showToast('No media target found.', 'error');
            }
            return;
        }

        window.vmoMediaModalContext = {
            ...context,
            mode: 'replace',
            selector: context.selector,
            mediaType: context.mediaType,
            mediaId: context.mediaId
        };
        openReplaceMediaModal(context.selector, context.mediaType);
    };
    
    /* Handle postMessage fallback for iframe communication */
    window.addEventListener('message', function(event) {
        console.log('[VMO PostMessage] Received message:', event.data);
        
        if (event.data && event.data.action === 'vmoOpenMediaPicker') {
            console.log('[VMO PostMessage] Handling vmoOpenMediaPicker');
            window.vmoOpenMediaPicker(event.data.data || event.data);
        } else if (event.data && event.data.action === 'openReplaceModal') {
            console.log('[VMO PostMessage] Handling openReplaceModal');
            // Handle the openReplaceModal action (supports both flat and nested payloads)
            const payload = event.data.data || event.data;
            console.log('[VMO PostMessage] Extracted payload:', payload);
            window.vmoOpenMediaPicker(payload);
        }
    });
    
    /* ========== DIRECT REPLACE BUTTON HANDLER ========== */
    // Handle replace button clicks directly in main window by monitoring iframe
    function setupReplaceButtonHandler() {
        const iframe = document.getElementById('vmo-preview-iframe');
        if (!iframe) return;
        
        // Remove any existing handler
        if (iframe._vmoReplaceHandler) {
            iframe.removeEventListener('load', iframe._vmoReplaceHandler);
        }
        
        // Set up handler for iframe load/reload
        iframe._vmoReplaceHandler = function() {
            const iframeDoc = iframe.contentDocument;
            if (!iframeDoc) return;
            
            console.log('🔧 Setting up replace button click handler in iframe');
            
            // Remove existing handler if any
            if (iframeDoc._vmoMainReplaceHandler) {
                ['pointerdown', 'mousedown', 'touchstart', 'click'].forEach(eventName => {
                    iframeDoc.removeEventListener(eventName, iframeDoc._vmoMainReplaceHandler, true);
                });
            }
            
            // Add direct click handler
            iframeDoc._vmoMainReplaceHandler = function(e) {
                const trigger = e.target && typeof e.target.closest === 'function'
                    ? e.target.closest('.vmo-replace-overlay, .vmo-replace-btn, .vmo-replace-media-btn, .replace-image-btn, .replace-video-btn, [data-replace-media], [data-vmo-editor-only="media-replace-overlay"]')
                    : null;

                if (!trigger) return;
                
                console.log('🚀 Replace button clicked - direct handler!', trigger);
                e.preventDefault();
                e.stopPropagation();
                if (typeof e.stopImmediatePropagation === 'function') e.stopImmediatePropagation();

                const now = Date.now();
                const lastOpen = Number(trigger.dataset.vmoReplaceOpenedAt || '0');
                if (now - lastOpen < 350) return;
                trigger.dataset.vmoReplaceOpenedAt = String(now);
                
                const mediaId = trigger.dataset.mediaId || trigger.dataset.replaceMedia || trigger.getAttribute('data-replace-media') || '';
                if (!mediaId) return;
                const selector = `[data-media-id="${mediaId}"]`;
                const element = iframeDoc.querySelector(selector);
                const mediaType = normalizeReplaceMediaType(trigger.dataset.mediaType || readReplaceMediaTypeFromNode(element));
                const allowedReplacementTypes = String(trigger.dataset.allowedReplacementTypes || '')
                    .split(',')
                    .map(type => type.trim().toLowerCase())
                    .filter(Boolean);
                const context = resolveMediaPickerContext({
                    selector,
                    mediaType,
                    mediaId,
                    slot_id: trigger.dataset.slotId || mediaId,
                    page_id: trigger.dataset.pageId || '',
                    section_id: trigger.dataset.sectionId || '',
                    product_id: trigger.dataset.productId || '',
                    placement_role: trigger.dataset.placementRole || '',
                    current_attachment_id: trigger.dataset.currentAttachmentId || '',
                    current_src: trigger.dataset.currentSrc || '',
                    block_id: trigger.dataset.blockId || '',
                    target_tag: trigger.dataset.targetTag || '',
                    allowed_replacement_types: allowedReplacementTypes
                });
                
                console.log('🎯 Opening replace modal:', context);
                
                // Set context and open modal
                window.vmoMediaModalContext = Object.assign({ mode: 'replace' }, context);
                openReplaceMediaModal(context.selector, context.mediaType);
            };
            
            ['pointerdown', 'mousedown', 'touchstart', 'click'].forEach(eventName => {
                iframeDoc.addEventListener(eventName, iframeDoc._vmoMainReplaceHandler, true);
            });
            console.log('✅ Replace button handler attached to iframe document');
        };
        
        iframe.addEventListener('load', iframe._vmoReplaceHandler);
        
        // Also run immediately if iframe is already loaded
        if (iframe.contentDocument) {
            iframe._vmoReplaceHandler();
        }
    }
    
    // Initialize the handler
    $(document).ready(setupReplaceButtonHandler);
    
    /* kill legacy onclick calls that still reference the old helper */
window.vmoReplaceIframeMedia = (...args) => window.vmoOpenMediaPicker({
    iframeId : 'vmo-preview-iframe',
    selector : args[1],            // same order as before
    type     : args[2] || 'image'
});

    function vmoReplacementAjax(action, payload = {}) {
        return new Promise((resolve, reject) => {
            $.post(vmo_ajax.ajaxurl, Object.assign({
                action,
                security: vmo_ajax.nonce
            }, payload)).done((response) => {
                if (response && response.success) {
                    resolve(response.data || {});
                    return;
                }
                reject(response && response.data ? response.data : { message: 'Media job failed.' });
            }).fail((xhr) => {
                const data = xhr && xhr.responseJSON && xhr.responseJSON.data ? xhr.responseJSON.data : null;
                reject(data || { message: 'Media job request failed.' });
            });
        });
    }

    function vmoBuildReplacementSlotContext(extra = {}) {
        const ctx = Object.assign({}, window.vmoMediaModalContext || {}, extra || {});
        return {
            schema: 'vmo_media_replacement_slot_v1',
            slot_id: String(ctx.slot_id || ctx.slotId || ctx.mediaId || ctx.media_id || ''),
            media_id: String(ctx.mediaId || ctx.media_id || ''),
            page_id: String(ctx.page_id || ctx.pageId || ''),
            section_id: String(ctx.section_id || ctx.sectionId || ''),
            product_id: String(ctx.product_id || ctx.productId || ''),
            placement_role: String(ctx.placement_role || ctx.placementRole || ''),
            media_type: normalizeReplaceMediaType(ctx.mediaType || ctx.media_type || 'image'),
            current_attachment_id: String(ctx.current_attachment_id || ctx.currentAttachmentId || ''),
            selector: String(ctx.selector || ''),
            current_src: String(ctx.current_src || ctx.currentSrc || ''),
            block_id: String(ctx.block_id || ctx.blockId || ''),
            target_tag: String(ctx.target_tag || ctx.targetTag || ''),
            project_id: String(window.currentProjectId || window.vmoCurrentProjectId || ctx.project_id || ctx.post_id || ''),
            allowed_replacement_types: Array.isArray(ctx.allowed_replacement_types) ? ctx.allowed_replacement_types : [],
            source: String(ctx.source || 'builder_replace_modal')
        };
    }

    function vmoRenderReplacementEstimate($modal, estimate) {
        const $box = $modal.find('#vmo-replace-token-estimate');
        if (!$box.length || !estimate) return;
        const required = parseInt(estimate.required_tokens || 0, 10) || 0;
        const balance = parseInt(estimate.current_balance || 0, 10) || 0;
        const eta = estimate.estimated_remaining_text || estimate.estimated_seconds ? (estimate.estimated_remaining_text || (estimate.estimated_seconds + ' seconds')) : 'Ready now';
        const ok = !!estimate.can_start;
        $box.show().html(`
            <div style="display:flex;justify-content:space-between;gap:12px;align-items:center;flex-wrap:wrap;">
                <span style="color:#9fc7d6;">Estimated cost: <strong style="color:#fff;">${required.toLocaleString()} credits</strong></span>
                <span style="color:#9fc7d6;">Balance: <strong style="color:${ok ? '#9bffdc' : '#ff9a73'};">${balance.toLocaleString()}</strong></span>
                <span style="color:#9fc7d6;">ETA: <strong style="color:#fff;">${eta}</strong></span>
            </div>
        `);
    }

    function vmoSetReplacementProgress($modal, job, message) {
        const $panel = $modal.find('#vmo-replace-job-progress');
        if (!$panel.length) return;
        const progress = Math.max(0, Math.min(100, parseInt(job?.progress || 0, 10) || 0));
        const status = message || job?.stage_label || job?.status || 'Preparing media job';
        const eta = job?.estimated_remaining_text || '';
        $panel.show();
        $panel.find('#vmo-replace-job-status').text(status);
        $panel.find('#vmo-replace-job-eta').text(eta ? 'Estimated remaining: ' + eta : '');
        $panel.find('#vmo-replace-job-bar').css('width', progress + '%');
        $panel.find('#vmo-replace-job-percent').text(progress + '%');
    }

    function vmoShowReplacementJobFailure($modal, error, options = {}) {
        const message = error?.plain_language_error || error?.message || error?.error || 'Media generation failed.';
        const recoveries = error?.recoveries || error?.recovery_options || error?.lower_cost_options || [];
        const $panel = $modal.find('#vmo-replace-job-progress');
        $panel.show();
        $panel.find('#vmo-replace-job-status').text(message);
        $panel.find('#vmo-replace-job-eta').text('Your project state was preserved. Try again, choose a lower-cost option, or upload media.');
        $panel.find('#vmo-replace-job-bar').css('width', '100%');
        $panel.find('#vmo-replace-job-percent').text('Needs attention');
        const $actions = $panel.find('#vmo-replace-job-actions').empty().show();
        $('<button type="button" class="vmo-btn">Retry</button>').css({ background:'#003054', color:'#00e2ff', border:'1px solid #00e2ff', padding:'8px 12px', borderRadius:'8px', cursor:'pointer' }).on('click', () => {
            if (options && typeof options.retry === 'function') options.retry();
        }).appendTo($actions);
        $('<button type="button" class="vmo-btn">Use Media Library</button>').css({ background:'#00ff9a', color:'#001a2e', border:'0', padding:'8px 12px', borderRadius:'8px', cursor:'pointer', fontWeight:'700' }).on('click', () => {
            $modal.find('#vmo-replace-library').trigger('click');
        }).appendTo($actions);
        if (Array.isArray(recoveries) && recoveries.length) {
            $('<span/>').css({ color:'#9fc7d6', fontSize:'12px' }).text(recoveries[0].label || '').appendTo($actions);
        }
        if (typeof showToast === 'function') showToast(message, 'error');
    }

    function vmoShowReplacementStillWorking($modal, job, message) {
        const progress = Math.max(parseInt(job?.progress || 0, 10) || 0, 60);
        vmoSetReplacementProgress($modal, Object.assign({}, job || {}, {
            progress,
            stage_label: message || 'This generation is taking longer than usual, but the job is still active. VMO will continue processing it safely.',
            estimated_remaining_text: 'Still generating safely'
        }));
        const $actions = $modal.find('#vmo-replace-job-actions');
        if ($actions.length) {
            $actions.empty().hide();
        }
    }

    async function vmoRunQueuedMediaReplacementJob(options = {}) {
        const $modal = $('#vmo-replace-modal');
        const slotContext = vmoBuildReplacementSlotContext(options.slotContext || {});
        const mediaType = normalizeReplaceMediaType(options.mediaType || slotContext.media_type || 'image');
        const replacementType = options.replacementType || (mediaType === 'video' ? 'ai_video' : (mediaType === 'audio' ? 'ai_audio' : 'ai_image'));
        const request = {
            project_id: window.currentProjectId || window.vmoCurrentProjectId || 0,
            media_type: mediaType,
            replacement_type: replacementType,
            prompt: options.prompt || '',
            model: options.model || '',
            seconds: options.seconds || '4',
            size: options.size || (mediaType === 'video' ? '1280x720' : '1024x1024'),
            voice: options.voice || 'alloy',
            url: options.url || '',
            attachment_id: options.attachmentId || 0,
            slot_context: JSON.stringify(slotContext)
        };

        const retry = () => vmoRunQueuedMediaReplacementJob(options);

        try {
            vmoSetReplacementProgress($modal, { progress: 3, stage_label: 'Checking replacement cost...' });
            const estimateData = await vmoReplacementAjax('vmo_estimate_media_replacement_job', request);
            if (estimateData.estimate || estimateData.required_tokens !== undefined) {
                vmoRenderReplacementEstimate($modal, estimateData.estimate || estimateData);
            }

            const createData = await vmoReplacementAjax('vmo_create_media_replacement_job', request);
            let job = createData.job || {};
            vmoSetReplacementProgress($modal, job);

            const finish = (finishedJob) => {
                if (!finishedJob || !finishedJob.url) {
                    vmoShowReplacementJobFailure($modal, { message: 'The job completed without a media URL.' }, { retry });
                    return;
                }
                if (window.vmoMediaModalContext) {
                    window.vmoMediaModalContext.mediaType = mediaType;
                    window.vmoMediaModalContext.attachmentId = finishedJob.attachment_id || 0;
                    window.vmoMediaModalContext.mediaJobId = finishedJob.id || '';
                }
                vmoSetReplacementProgress($modal, Object.assign({}, finishedJob, { progress: 100, stage_label: 'Inserting media into the selected slot...' }));
                vmoOnMediaSelected(finishedJob.url, { openAdjuster: true, source: options.source || replacementType, attachmentId: finishedJob.attachment_id || 0, job: finishedJob });
                setTimeout(() => $modal.remove(), 350);
            };

            if (job.status === 'complete') {
                finish(job);
                return job;
            }

            let attempts = 0;
            let softNoticeShown = false;
            const softNoticeAttempts = mediaType === 'video' ? 180 : 80;
            const normalPollDelay = mediaType === 'video' ? 5000 : 3000;
            const slowPollDelay = mediaType === 'video' ? 15000 : 10000;
            const poll = async () => {
                attempts += 1;
                try {
                    const statusData = await vmoReplacementAjax('vmo_get_media_replacement_job', { job_id: job.id });
                    job = statusData.job || job;
                    vmoSetReplacementProgress($modal, job);
                    if (job.status === 'complete') {
                        finish(job);
                        return;
                    }
                    if (job.status === 'failed') {
                        vmoShowReplacementJobFailure($modal, job, { retry });
                        return;
                    }
                    if (attempts >= softNoticeAttempts) {
                        softNoticeShown = true;
                        vmoShowReplacementStillWorking($modal, job);
                    }
                    setTimeout(poll, softNoticeShown ? slowPollDelay : normalPollDelay);
                } catch (pollError) {
                    const message = pollError?.plain_language_error || pollError?.message || pollError?.error || '';
                    if (/not found|unauthorized|not logged in/i.test(String(message))) {
                        vmoShowReplacementJobFailure($modal, pollError, { retry });
                        return;
                    }
                    softNoticeShown = softNoticeShown || attempts >= softNoticeAttempts;
                    if (softNoticeShown) {
                        vmoShowReplacementStillWorking($modal, job, 'VMO is still checking this media job. Generation continues safely in the background.');
                    }
                    setTimeout(poll, softNoticeShown ? slowPollDelay : 5000);
                }
            };
            setTimeout(poll, 1500);
            return job;
        } catch (error) {
            vmoShowReplacementJobFailure($modal, error, { retry });
            throw error;
        }
    }

    window.vmoCreateMediaReplacementJob = vmoRunQueuedMediaReplacementJob;


    /* ========== MEDIA MODAL (library + DALL·E) ========== */
    function openReplaceMediaModal (selector, mediaType) {
        console.log('[VMO Replace] Modal opening with:', { selector, mediaType });
        const resolvedContext = resolveMediaPickerContext(Object.assign({}, window.vmoMediaModalContext || {}, { selector, mediaType }));
        const normalizedType = normalizeReplaceMediaType(resolvedContext.mediaType || mediaType);
        const resolvedSelector = resolvedContext.selector || selector;
        const targetLabel = normalizedType === 'video' ? 'Video' : (normalizedType === 'audio' ? 'Audio' : 'Image');
        
        // Ensure context is set even if called directly
        try {
            window.vmoMediaModalContext = Object.assign({}, window.vmoMediaModalContext || {}, resolvedContext, {
                mode: 'replace',
                selector: resolvedSelector,
                mediaType: normalizedType,
                mediaId: resolvedContext.mediaId || ''
            });
            console.log('[VMO Replace] Context set:', window.vmoMediaModalContext);
        } catch(err) {
            console.error('[VMO Replace] Error setting context:', err);
        }
        
        console.log('[VMO Replace] Removing existing modal and creating new one');
        $('#vmo-replace-modal').remove();
        
        // Append to body so the modal is not trapped inside iframe/builder stacking contexts.
        let $container = $('body');
        console.log('[VMO Replace] Using container:', $container[0]);
        
        $container.append(`<div id="vmo-replace-modal" class="vmo-modal vmo-modal-visible vmo-replace-media-modal" data-vmo-replace-modal-version="source-picker-v1" role="dialog" aria-modal="true" aria-labelledby="vmo-replace-modal-title" style="display:flex;position:fixed;top:0;left:0;width:100vw;height:100vh;background:rgba(0,7,18,0.72);z-index:2147483647;align-items:center;justify-content:center;backdrop-filter:blur(14px) saturate(140%);-webkit-backdrop-filter:blur(14px) saturate(140%);opacity:0;transition:opacity 0.3s ease;pointer-events:auto;isolation:isolate;padding:22px;box-sizing:border-box;"><div class="vmo-modal-content vmo-media-replace-panel" style="background:linear-gradient(135deg,rgba(2,22,38,0.9),rgba(0,48,84,0.72));border-radius:18px;padding:30px;width:90%;max-width:720px;color:#fff;box-shadow:0 30px 90px rgba(0,226,255,0.28), inset 0 1px 0 rgba(255,255,255,0.12);border:1px solid rgba(0,226,255,0.38);backdrop-filter:blur(28px) saturate(170%) brightness(1.08);-webkit-backdrop-filter:blur(28px) saturate(170%) brightness(1.08);transform:scale(0.8);opacity:0;transition:all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);max-height:90vh;overflow-y:auto;">
            <h3 id="vmo-replace-modal-title" style="margin-top:0;color:#00e2ff;text-align:center;margin-bottom:10px;font-size:24px;">Replace ${targetLabel}</h3>
            <p style="margin:0 0 18px;color:#9fc7d6;text-align:center;font-size:14px;line-height:1.5;">Choose a replacement source. After it is inserted, you can fine-tune crop, fit, and positioning without moving the slot itself.</p>
            <div class="vmo-media-replace-options" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:14px;margin-bottom:14px;">
                <button id="vmo-replace-library" class="vmo-btn vmo-media-replace-source" data-source="wordpress-library" style="background:rgba(0,255,154,0.14);color:#d8fff2;border:1px solid rgba(0,255,154,0.42);padding:18px 20px;border-radius:12px;font-weight:700;cursor:pointer;transition:all 0.2s ease;text-align:left;display:flex;flex-direction:column;gap:6px;backdrop-filter:blur(10px);">
                    <span style="font-size:17px;">WordPress Media Library</span>
                    <span style="font-size:13px;font-weight:500;opacity:0.82;">Use media already stored in WordPress.</span>
                </button>
                <button id="vmo-generate-image-choice" class="vmo-btn vmo-media-replace-source" data-source="openai-image" style="background:rgba(0,226,255,0.12);color:#d9fbff;border:1px solid rgba(0,226,255,0.45);padding:18px 20px;border-radius:12px;font-weight:700;cursor:pointer;transition:all 0.2s ease;text-align:left;display:flex;flex-direction:column;gap:6px;backdrop-filter:blur(10px);">
                    <span style="font-size:17px;">OpenAI Image Generator</span>
                    <span style="font-size:13px;font-weight:500;opacity:0.82;">Generate a replacement image for this slot.</span>
                </button>
                <button id="vmo-generate-sora-standard-choice" class="vmo-btn vmo-media-replace-source" data-source="sora-2" style="background:rgba(255,138,69,0.12);color:#ffd6bd;border:1px solid rgba(255,138,69,0.48);padding:18px 20px;border-radius:12px;font-weight:700;cursor:pointer;transition:all 0.2s ease;text-align:left;display:flex;flex-direction:column;gap:6px;backdrop-filter:blur(10px);">
                    <span style="font-size:17px;">Sora 2 Video</span>
                    <span style="font-size:13px;font-weight:500;opacity:0.82;">Generate a standard replacement video.</span>
                </button>
                <button id="vmo-generate-sora-pro-choice" class="vmo-btn vmo-media-replace-source" data-source="sora-2-pro" style="background:rgba(255,107,53,0.16);color:#ffe0cc;border:1px solid rgba(255,107,53,0.55);padding:18px 20px;border-radius:12px;font-weight:700;cursor:pointer;transition:all 0.2s ease;text-align:left;display:flex;flex-direction:column;gap:6px;backdrop-filter:blur(10px);">
                    <span style="font-size:17px;">Sora 2 Pro Video</span>
                    <span style="font-size:13px;font-weight:500;opacity:0.82;">Generate a premium replacement video.</span>
                </button>
            </div>
            <div style="display:flex;justify-content:center;gap:12px;align-items:center;flex-wrap:wrap;margin-bottom:20px;">
                <button id="vmo-open-fit-controls" class="vmo-btn" style="background:transparent;color:#9fc7d6;border:1px dashed rgba(159,199,214,0.55);padding:10px 16px;border-radius:999px;font-weight:600;cursor:pointer;transition:all 0.2s ease;">Adjust Current Fit</button>
                <button id="vmo-close-replace-modal" class="vmo-btn" style="background:#333;color:#fff;border:none;padding:12px 24px;border-radius:8px;font-weight:600;cursor:pointer;transition:all 0.2s ease;">Cancel</button>
            </div>
            <div id="vmo-gen-replace-form" style="display:none;margin-top:15px;">
                <textarea id="vmo-gen-replace-desc" placeholder="Describe the replacement image to create with GPT Image latest..." style="width:100%;min-height:60px;background:#003054;color:#fff;border:1px solid #00e2ff;border-radius:8px;padding:12px;font-family:inherit;font-size:14px;box-sizing:border-box;margin-bottom:15px;"></textarea>
                <button id="vmo-submit-replace-gen" class="vmo-btn" style="background:#00ff9a;color:#001a2e;border:none;padding:12px 30px;border-radius:8px;font-weight:600;cursor:pointer;width:100%;font-size:16px;transition:all 0.2s ease;">Generate Image</button>
            </div>
            <div id="vmo-gen-video-form" style="display:none;margin-top:15px;">
                <p style="font-size:13px;color:#8899aa;margin-bottom:10px;">Generate a professional AI video. Videos are billed per second.</p>
                <textarea id="vmo-gen-video-desc" placeholder="Describe the video you want (e.g., 'Cinematic aerial shot of a modern office building at sunset with smooth camera motion')..." style="width:100%;min-height:70px;background:#003054;color:#fff;border:1px solid #ff6b35;border-radius:8px;padding:12px;font-family:inherit;font-size:14px;box-sizing:border-box;margin-bottom:12px;"></textarea>
                <div style="display:flex;gap:12px;align-items:center;margin-bottom:12px;flex-wrap:wrap;">
                    <label for="vmo-video-duration" style="font-size:13px;color:#8899aa;">Duration:</label>
                    <select id="vmo-video-duration" style="background:#003054;color:#fff;border:1px solid #ff6b35;border-radius:6px;padding:8px 12px;font-size:13px;">
                        <option value="4">4 seconds (~25,200 tokens)</option>
                        <option value="8">8 seconds (~50,400 tokens)</option>
                        <option value="12">12 seconds (~75,600 tokens)</option>
                    </select>
                    <label for="vmo-video-quality" style="font-size:13px;color:#8899aa;">Quality:</label>
                    <select id="vmo-video-quality" style="background:#003054;color:#fff;border:1px solid #ff6b35;border-radius:6px;padding:8px 12px;font-size:13px;">
                        <option value="pro">Pro (HD)</option>
                        <option value="standard">Standard</option>
                    </select>
                </div>
                <button id="vmo-submit-video-gen" class="vmo-btn" style="background:#ff6b35;color:#fff;border:none;padding:12px 30px;border-radius:8px;font-weight:600;cursor:pointer;width:100%;font-size:16px;transition:all 0.2s ease;">🎬 Generate Video</button>
                <div id="vmo-video-gen-progress" style="display:none;margin-top:12px;padding:12px;background:#002040;border-radius:8px;text-align:center;">
                    <div style="color:#ff6b35;font-weight:600;">Generating video...</div>
                    <div id="vmo-video-gen-status" style="color:#8899aa;font-size:13px;margin-top:6px;">Submitting to AI...</div>
                    <div style="background:#003054;border-radius:4px;height:6px;margin-top:10px;overflow:hidden;">
                        <div id="vmo-video-gen-bar" style="background:linear-gradient(90deg,#ff6b35,#ffaa00);height:100%;width:0%;transition:width 0.5s ease;border-radius:4px;"></div>
                    </div>
                </div>
            </div>
            <div id="vmo-gen-audio-form" style="display:none;margin-top:15px;">
                <p style="font-size:13px;color:#8899aa;margin-bottom:10px;">Generate audio for this exact slot.</p>
                <textarea id="vmo-gen-audio-desc" placeholder="Write the narration, intro, or audio script..." style="width:100%;min-height:70px;background:#003054;color:#fff;border:1px solid #00ff9a;border-radius:8px;padding:12px;font-family:inherit;font-size:14px;box-sizing:border-box;margin-bottom:12px;"></textarea>
                <div style="display:flex;gap:12px;align-items:center;margin-bottom:12px;flex-wrap:wrap;">
                    <label for="vmo-audio-voice" style="font-size:13px;color:#8899aa;">Voice:</label>
                    <select id="vmo-audio-voice" style="background:#003054;color:#fff;border:1px solid #00ff9a;border-radius:6px;padding:8px 12px;font-size:13px;">
                        <option value="alloy">Alloy</option>
                        <option value="ash">Ash</option>
                        <option value="echo">Echo</option>
                        <option value="fable">Fable</option>
                        <option value="nova">Nova</option>
                        <option value="shimmer">Shimmer</option>
                    </select>
                </div>
                <button id="vmo-submit-audio-gen" class="vmo-btn" style="background:#00ff9a;color:#001a2e;border:none;padding:12px 30px;border-radius:8px;font-weight:600;cursor:pointer;width:100%;font-size:16px;transition:all 0.2s ease;">Generate Audio</button>
            </div>
            <div id="vmo-replace-token-estimate" style="display:none;margin-top:14px;padding:12px;background:#001f38;border:1px solid rgba(0,226,255,0.25);border-radius:8px;font-size:13px;"></div>
            <div id="vmo-replace-job-progress" style="display:none;margin-top:14px;padding:14px;background:#002040;border-radius:8px;text-align:left;">
                <div style="display:flex;justify-content:space-between;gap:12px;align-items:center;">
                    <div id="vmo-replace-job-status" style="color:#fff;font-weight:700;">Preparing media job...</div>
                    <div id="vmo-replace-job-percent" style="color:#00e2ff;font-size:12px;font-weight:700;">0%</div>
                </div>
                <div id="vmo-replace-job-eta" style="color:#9fc7d6;font-size:12px;margin-top:6px;"></div>
                <div style="background:#003054;border-radius:4px;height:7px;margin-top:10px;overflow:hidden;">
                    <div id="vmo-replace-job-bar" style="background:linear-gradient(90deg,#00e2ff,#00ff9a);height:100%;width:0%;transition:width 0.5s ease;border-radius:4px;"></div>
                </div>
                <div id="vmo-replace-job-actions" style="display:none;margin-top:12px;gap:8px;align-items:center;flex-wrap:wrap;"></div>
            </div>
            <div id="vmo-overlay-form" style="display:none;margin-top:15px;">
                <p style="font-size:13px;color:#8899aa;margin-bottom:10px;">Add a visual overlay effect or your own media as an overlay layer on this section.</p>
                <div style="display:flex;gap:10px;flex-wrap:wrap;margin-bottom:15px;">
                    <button class="vmo-overlay-preset" data-preset="heroSky" style="background:#003054;color:#c084fc;border:1px solid #c084fc;padding:8px 14px;border-radius:6px;cursor:pointer;font-size:13px;transition:all 0.2s ease;">☁️ Clouds</button>
                    <button class="vmo-overlay-preset" data-preset="heroNight" style="background:#003054;color:#c084fc;border:1px solid #c084fc;padding:8px 14px;border-radius:6px;cursor:pointer;font-size:13px;transition:all 0.2s ease;">✨ Fireflies</button>
                    <button class="vmo-overlay-preset" data-preset="heroWinter" style="background:#003054;color:#c084fc;border:1px solid #c084fc;padding:8px 14px;border-radius:6px;cursor:pointer;font-size:13px;transition:all 0.2s ease;">❄️ Snow</button>
                    <button class="vmo-overlay-preset" data-preset="heroRain" style="background:#003054;color:#c084fc;border:1px solid #c084fc;padding:8px 14px;border-radius:6px;cursor:pointer;font-size:13px;transition:all 0.2s ease;">🌧️ Rain</button>
                    <button class="vmo-overlay-preset" data-preset="luxurySparkle" style="background:#003054;color:#c084fc;border:1px solid #c084fc;padding:8px 14px;border-radius:6px;cursor:pointer;font-size:13px;transition:all 0.2s ease;">💎 Sparkles</button>
                    <button class="vmo-overlay-preset" data-preset="atmospheric" style="background:#003054;color:#c084fc;border:1px solid #c084fc;padding:8px 14px;border-radius:6px;cursor:pointer;font-size:13px;transition:all 0.2s ease;">🌫️ Fog</button>
                    <button class="vmo-overlay-preset" data-preset="dramatic" style="background:#003054;color:#c084fc;border:1px solid #c084fc;padding:8px 14px;border-radius:6px;cursor:pointer;font-size:13px;transition:all 0.2s ease;">💨 Smoke</button>
                    <button class="vmo-overlay-preset" data-preset="romantic" style="background:#003054;color:#c084fc;border:1px solid #c084fc;padding:8px 14px;border-radius:6px;cursor:pointer;font-size:13px;transition:all 0.2s ease;">🌸 Petals</button>
                    <button class="vmo-overlay-preset" data-preset="autumn" style="background:#003054;color:#c084fc;border:1px solid #c084fc;padding:8px 14px;border-radius:6px;cursor:pointer;font-size:13px;transition:all 0.2s ease;">🍂 Leaves</button>
                    <button class="vmo-overlay-preset" data-preset="campfire" style="background:#003054;color:#c084fc;border:1px solid #c084fc;padding:8px 14px;border-radius:6px;cursor:pointer;font-size:13px;transition:all 0.2s ease;">🔥 Embers</button>
                    <button class="vmo-overlay-preset" data-preset="underwater" style="background:#003054;color:#c084fc;border:1px solid #c084fc;padding:8px 14px;border-radius:6px;cursor:pointer;font-size:13px;transition:all 0.2s ease;">🫧 Bubbles</button>
                    <button class="vmo-overlay-preset" data-preset="interactive" style="background:#003054;color:#c084fc;border:1px solid #c084fc;padding:8px 14px;border-radius:6px;cursor:pointer;font-size:13px;transition:all 0.2s ease;">💧 Ripples</button>
                </div>
                <div style="margin-bottom:12px;">
                    <div style="font-size:13px;color:#8899aa;display:block;margin-bottom:6px;">Or upload your own overlay media:</div>
                    <button id="vmo-overlay-upload" class="vmo-btn" style="background:#c084fc;color:#001a2e;border:none;padding:10px 20px;border-radius:8px;font-weight:600;cursor:pointer;transition:all 0.2s ease;">📁 Upload Custom Image/Video Overlay</button>
                </div>
                <div style="display:flex;gap:12px;align-items:center;margin-bottom:12px;flex-wrap:wrap;">
                    <label for="vmo-overlay-opacity" style="font-size:13px;color:#8899aa;">Opacity:</label>
                    <input id="vmo-overlay-opacity" type="range" min="10" max="100" value="70" style="flex:1;accent-color:#c084fc;">
                    <span id="vmo-overlay-opacity-val" style="color:#c084fc;font-size:13px;">70%</span>
                    <label for="vmo-overlay-speed" style="font-size:13px;color:#8899aa;">Speed:</label>
                    <input id="vmo-overlay-speed" type="range" min="10" max="150" value="50" style="flex:1;accent-color:#c084fc;">
                    <span id="vmo-overlay-speed-val" style="color:#c084fc;font-size:13px;">0.5x</span>
                </div>
            </div></div></div>`);

        const $modal = $('#vmo-replace-modal').data('type', normalizedType);
        console.log('[VMO Replace] Modal created:', $modal.length ? 'SUCCESS' : 'FAILED');

        const allowedTypes = Array.isArray(window.vmoMediaModalContext?.allowed_replacement_types) && window.vmoMediaModalContext.allowed_replacement_types.length
            ? window.vmoMediaModalContext.allowed_replacement_types.map(type => String(type).toLowerCase())
            : (normalizedType === 'audio' ? ['audio'] : ['image', 'video']);
        const allowImageGeneration = allowedTypes.includes('image') || allowedTypes.includes('logo');
        const allowVideoGeneration = allowedTypes.includes('video');
        const allowAudioGeneration = allowedTypes.includes('audio');
        $('#vmo-generate-image-choice').toggle(allowImageGeneration);
        $('#vmo-generate-sora-standard-choice, #vmo-generate-sora-pro-choice').toggle(allowVideoGeneration);
        $('#vmo-generate-audio-choice').toggle(allowAudioGeneration);
        if (!allowImageGeneration && !allowVideoGeneration && !allowAudioGeneration) {
            $('#vmo-replace-generate').hide();
        }
        
        // Add entrance animation
        setTimeout(() => {
            console.log('[VMO Replace] Starting entrance animation');
            $modal.css('opacity', '1');
            $modal.find('.vmo-modal-content').css({
                'transform': 'scale(1)',
                'opacity': '1'
            });
            console.log('[VMO Replace] Modal should now be visible');
        }, 50);
        
        // Close modal with animation
        $('#vmo-close-replace-modal').on('click', ()=> {
            $modal.css('opacity', '0');
            setTimeout(() => $modal.remove(), 300);
        });
        
        // Close on background click
        $modal.on('click', function(e) {
            if (e.target === this) {
                $(this).css('opacity', '0');
                setTimeout(() => $(this).remove(), 300);
            }
        });

        /* WP Media */
        let frame=null;
        let uploadFrame=null;
        const libraryType = Array.from(new Set(allowedTypes.map(type => type === 'logo' ? 'image' : type).filter(type => ['image', 'video', 'audio'].includes(type))));
        const inferAttachmentMediaType = (attachment) => {
            const rawType = String(attachment?.type || attachment?.mime || attachment?.mime_type || '').toLowerCase();
            const url = String(attachment?.url || '').toLowerCase().split('?')[0];
            if (rawType.includes('audio') || /\.(mp3|wav|m4a|aac|oga)$/i.test(url)) return 'audio';
            if (rawType.includes('video') || /\.(mp4|webm|ogg|mov|m4v)$/i.test(url)) return 'video';
            return 'image';
        };
        const handlePickedReplacement = (attachment, source) => {
            const url = attachment && attachment.url ? attachment.url : '';
            if (!url) return showToast('Selected media has no usable URL.', 'error');
            const pickedMediaType = inferAttachmentMediaType(attachment);
            if (window.vmoMediaModalContext) window.vmoMediaModalContext.mediaType = pickedMediaType;
            vmoRunQueuedMediaReplacementJob({
                replacementType: source === 'upload' ? 'upload' : 'library',
                mediaType: pickedMediaType,
                url,
                attachmentId: attachment.id || attachment.ID || 0,
                source
            }).catch(() => {});
        };

        $('#vmo-replace-library').on('click', ()=>{
            console.log('[VMO Replace] Media library button clicked');
            if (typeof wp === 'undefined' || !wp.media) {
                return showToast('WordPress Media Library is not available on this screen.', 'error');
            }
            if (frame) {
                console.log('[VMO Replace] Reopening existing media frame');
                return frame.open();
            }
            
            console.log('[VMO Replace] Creating new media frame for type:', normalizedType);
            frame = wp.media({ title:'Select Replacement', button:{ text:'Replace'}, multiple:false, library:{type: libraryType} });
            
            frame.on('select', ()=>{
                console.log('[VMO Replace] Media selected from library');
                const attachment = frame.state().get('selection').first().toJSON();
                console.log('[VMO Replace] Selected URL:', attachment.url);
                handlePickedReplacement(attachment, 'library');
            });
            
            frame.open();
        });

        $('#vmo-replace-upload').on('click', ()=>{
            console.log('[VMO Replace] Upload button clicked');
            if (typeof wp === 'undefined' || !wp.media) {
                return showToast('WordPress Media Library is not available on this screen.', 'error');
            }
            if (uploadFrame) return uploadFrame.open();
            uploadFrame = wp.media({ title:'Upload Replacement', button:{ text:'Use Uploaded Media'}, multiple:false, library:{type: libraryType} });
            uploadFrame.on('open', () => {
                try { uploadFrame.content.mode('upload'); } catch(e) {}
            });
            uploadFrame.on('select', ()=>{
                const attachment = uploadFrame.state().get('selection').first().toJSON();
                handlePickedReplacement(attachment, 'upload');
            });
            uploadFrame.open();
        });

        /* Generate */
        $('#vmo-replace-generate').on('click', ()=> {
            console.log('[VMO Replace] Generate button clicked');
            $('#vmo-ai-generate-choice').toggle();
            $('#vmo-gen-replace-form').hide();
            $('#vmo-gen-video-form').hide();
            $('#vmo-gen-audio-form').hide();
            $('#vmo-overlay-form').hide();
        });

        $('#vmo-generate-image-choice').on('click', () => {
            if (window.vmoMediaModalContext) window.vmoMediaModalContext.mediaType = 'image';
            $('#vmo-ai-generate-choice').hide();
            $('#vmo-gen-video-form').hide();
            $('#vmo-gen-audio-form').hide();
            $('#vmo-gen-replace-form').show();
            $('#vmo-gen-replace-desc').focus();
        });

        $('#vmo-generate-sora-standard-choice, #vmo-generate-sora-pro-choice').on('click', function() {
            const quality = this.id === 'vmo-generate-sora-pro-choice' ? 'pro' : 'standard';
            if (window.vmoMediaModalContext) window.vmoMediaModalContext.mediaType = 'video';
            $('#vmo-video-quality').val(quality);
            $('#vmo-ai-generate-choice').hide();
            $('#vmo-gen-replace-form').hide();
            $('#vmo-gen-audio-form').hide();
            $('#vmo-gen-video-form').show();
            $('#vmo-submit-video-gen').text(quality === 'pro' ? 'Generate Sora 2 Pro Video' : 'Generate Sora 2 Video');
            $('#vmo-gen-video-desc').focus();
        });

        $('#vmo-generate-audio-choice').on('click', function() {
            if (window.vmoMediaModalContext) window.vmoMediaModalContext.mediaType = 'audio';
            $('#vmo-ai-generate-choice').hide();
            $('#vmo-gen-replace-form').hide();
            $('#vmo-gen-video-form').hide();
            $('#vmo-gen-audio-form').show();
            $('#vmo-gen-audio-desc').focus();
        });

        $('#vmo-open-fit-controls').on('click', () => {
            $modal.remove();
            if (typeof window.openMediaPlacementAdjuster === 'function') {
                window.openMediaPlacementAdjuster(window.vmoMediaModalContext || {});
            }
        });
        
        $('#vmo-submit-replace-gen').on('click', ()=>{
            console.log('[VMO Replace] Generate submit clicked');
            const desc = $('#vmo-gen-replace-desc').val().trim();
            if (!desc) {
                console.log('[VMO Replace] No description provided');
                return showToast('Describe the image first.','error');
            }
            
            console.log('[VMO Replace] Queueing image generation for:', desc);
            $('#vmo-submit-replace-gen').prop('disabled', true).text('Queued...');
            vmoRunQueuedMediaReplacementJob({
                replacementType: 'ai_image',
                mediaType: 'image',
                prompt: desc,
                model: 'openai-image-generation-2',
                source: 'ai-image'
            }).catch(() => {
                $('#vmo-submit-replace-gen').prop('disabled', false).text('Generate Image');
            });
        });

        /* ========== Sora Video Generation ========== */

        // Opacity/speed slider display
        $('#vmo-overlay-opacity').on('input', function() { $('#vmo-overlay-opacity-val').text(this.value + '%'); });
        $('#vmo-overlay-speed').on('input', function() { $('#vmo-overlay-speed-val').text((this.value / 100).toFixed(1) + 'x'); });

        $('#vmo-submit-video-gen').on('click', async () => {
            const desc = $('#vmo-gen-video-desc').val().trim();
            if (!desc) return showToast('Describe the video first.', 'error');

            const seconds = parseInt($('#vmo-video-duration').val()) || 4;
            const quality = $('#vmo-video-quality').val() || 'pro';
            const model = quality === 'pro' ? 'sora-2-pro' : 'sora-2';

            $('#vmo-submit-video-gen').prop('disabled', true).text('Queued...');
            $('#vmo-video-gen-progress').hide();
            vmoRunQueuedMediaReplacementJob({
                replacementType: 'ai_video',
                mediaType: 'video',
                prompt: desc,
                seconds: String(seconds),
                model,
                size: '1920x1080',
                source: quality === 'pro' ? 'ai-video-sora-pro' : 'ai-video-sora-standard'
            }).catch(() => {
                $('#vmo-submit-video-gen').prop('disabled', false).text(quality === 'pro' ? 'Generate Sora 2 Pro Video' : 'Generate Sora 2 Video');
            });
        });

        $('#vmo-submit-audio-gen').on('click', async () => {
            const desc = $('#vmo-gen-audio-desc').val().trim();
            if (!desc) return showToast('Write the audio script first.', 'error');
            const voice = $('#vmo-audio-voice').val() || 'alloy';
            $('#vmo-submit-audio-gen').prop('disabled', true).text('Queued...');
            vmoRunQueuedMediaReplacementJob({
                replacementType: 'ai_audio',
                mediaType: 'audio',
                prompt: desc,
                voice,
                source: 'ai-audio'
            }).catch(() => {
                $('#vmo-submit-audio-gen').prop('disabled', false).text('Generate Audio');
            });
        });

        /* ========== Media Overlay Controls ========== */
        $('#vmo-replace-overlay').on('click', () => {
            console.log('[VMO Replace] Overlay panel opened');
            $('#vmo-ai-generate-choice').hide();
            $('#vmo-gen-replace-form').hide();
            $('#vmo-gen-video-form').hide();
            $('#vmo-gen-audio-form').hide();
            $('#vmo-overlay-form').toggle();
        });

        // Preset overlays
        $(document).off('click.vmoOverlayPreset').on('click.vmoOverlayPreset', '.vmo-overlay-preset', function() {
            const preset = $(this).data('preset');
            const presets = window.vmoOverlayPresets || {};
            const config = presets[preset];
            if (!config) return showToast('Overlay preset not found.', 'error');

            // Apply user-configured opacity and speed
            const opacity = parseInt($('#vmo-overlay-opacity').val()) / 100;
            const speed = parseInt($('#vmo-overlay-speed').val()) / 100;
            const finalConfig = Object.assign({}, config, { opacity, speed });

            // Find the target section in iframe
            const ctx = window.vmoMediaModalContext || {};
            const iframe = document.getElementById('vmo-preview-iframe');
            if (iframe && iframe.contentDocument) {
                let targetEl = null;
                if (ctx.selector) targetEl = iframe.contentDocument.querySelector(ctx.selector);
                if (!targetEl && ctx.mediaId) targetEl = iframe.contentDocument.querySelector('[data-media-id="' + ctx.mediaId + '"]');
                const section = targetEl ? (targetEl.closest('section') || targetEl.closest('[data-block-id]') || targetEl.parentElement) : null;

                if (section) {
                    // Store overlay config as data attribute for persistence
                    section.setAttribute('data-vmo-overlay', JSON.stringify(finalConfig));
                    section.style.position = 'relative';
                    section.style.overflow = 'hidden';

                    // Initialize the overlay
                    if (window.VMOAnimatedOverlay) {
                        // Remove existing overlay on this section if any
                        const existingCanvas = section.querySelector('.vmo-overlay-canvas');
                        if (existingCanvas) existingCanvas.remove();
                        new window.VMOAnimatedOverlay(section, finalConfig);
                    }

                    // Sync back to vmoState
                    const blockEl = section.closest('[data-block-id]');
                    if (typeof window.vmoSyncPreviewStateFromIframe === 'function') {
                        window.vmoSyncPreviewStateFromIframe({
                            blockId: blockEl ? String(blockEl.dataset.blockId || '') : '',
                            markUnsaved: true,
                            autoSave: true
                        });
                    } else if (blockEl && window.vmoState?.blocks) {
                        const block = window.vmoState.blocks.find(b => String(b.id) === String(blockEl.dataset.blockId));
                        if (block) {
                            block.html = blockEl.innerHTML;
                            window.isUnsaved = true;
                            if (typeof window.autoSaveCheckpoint === 'function') window.autoSaveCheckpoint();
                        }
                    }

                    showToast('Overlay applied! ✨', 'success');
                    if (typeof window.pushUndo === 'function') window.pushUndo('Add overlay: ' + preset);
                    $modal.remove();
                } else {
                    showToast('Could not find target section for overlay.', 'error');
                }
            }
        });

        // Custom overlay upload
        let overlayFrame = null;
        $('#vmo-overlay-upload').on('click', () => {
            if (overlayFrame) return overlayFrame.open();
            overlayFrame = wp.media({ title: 'Select Overlay Media', button: { text: 'Use as Overlay' }, multiple: false });
            overlayFrame.on('select', () => {
                const attachment = overlayFrame.state().get('selection').first().toJSON();
                const url = attachment.url;
                const opacity = parseInt($('#vmo-overlay-opacity').val()) / 100;
                const speed = parseInt($('#vmo-overlay-speed').val()) / 100;
                const config = { type: 'clouds', imageUrl: url, opacity, speed, layers: 3 };

                const ctx = window.vmoMediaModalContext || {};
                const iframe = document.getElementById('vmo-preview-iframe');
                if (iframe && iframe.contentDocument) {
                    let targetEl = null;
                    if (ctx.selector) targetEl = iframe.contentDocument.querySelector(ctx.selector);
                    if (!targetEl && ctx.mediaId) targetEl = iframe.contentDocument.querySelector('[data-media-id="' + ctx.mediaId + '"]');
                    const section = targetEl ? (targetEl.closest('section') || targetEl.closest('[data-block-id]') || targetEl.parentElement) : null;

                    if (section) {
                        section.setAttribute('data-vmo-overlay', JSON.stringify(config));
                        section.setAttribute('data-overlay-image', url);
                        section.style.position = 'relative';
                        section.style.overflow = 'hidden';
                        if (window.VMOAnimatedOverlay) {
                            const existingCanvas = section.querySelector('.vmo-overlay-canvas');
                            if (existingCanvas) existingCanvas.remove();
                            new window.VMOAnimatedOverlay(section, config);
                        }
                        const blockEl = section.closest('[data-block-id]');
                        if (typeof window.vmoSyncPreviewStateFromIframe === 'function') {
                            window.vmoSyncPreviewStateFromIframe({
                                blockId: blockEl ? String(blockEl.dataset.blockId || '') : '',
                                markUnsaved: true,
                                autoSave: true
                            });
                        } else if (blockEl && window.vmoState?.blocks) {
                            const block = window.vmoState.blocks.find(b => String(b.id) === String(blockEl.dataset.blockId));
                            if (block) {
                                block.html = blockEl.innerHTML;
                                window.isUnsaved = true;
                                if (typeof window.autoSaveCheckpoint === 'function') window.autoSaveCheckpoint();
                            }
                        }
                        showToast('Custom overlay applied! ✨', 'success');
                        if (typeof window.pushUndo === 'function') window.pushUndo('Add custom overlay');
                        $modal.remove();
                    }
                }
            });
            overlayFrame.open();
        });
    }
    
    // Expose the function globally for fallback access
    window.openReplaceMediaModal = openReplaceMediaModal;
    
    // Additional exposure for debugging
    console.log('[VMO Replace] Function exposed globally:', typeof window.openReplaceMediaModal);
    
    // Force exposure to ensure iframe can access it
    if (typeof window !== 'undefined') {
        window.openReplaceMediaModal = openReplaceMediaModal;
        // Also add to global scope for iframe access
        if (typeof global !== 'undefined') {
            global.openReplaceMediaModal = openReplaceMediaModal;
        }
    }

    window.vmoPendingMediaSelection = Array.isArray(window.vmoPendingMediaSelection) ? window.vmoPendingMediaSelection : [];

    function vmoEscapeAttr(value) {
        return String(value || '')
            .replace(/&/g, '&amp;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#39;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;');
    }

    function vmoNormalizeBuilderMediaItem(item) {
        if (!item) return null;

        const rawUrl = String(item.url || item.src || item.source_url || '').trim();
        if (!rawUrl) return null;

        const rawType = String(item.type || item.media_type || item.mime || '').toLowerCase();
        const title = String(item.title || item.filename || item.name || 'Selected media').trim();
        const urlWithoutQuery = rawUrl.split('?')[0].toLowerCase();
        let type = rawType;

        if (!type || type.indexOf('/') !== -1) {
            if (/video\//i.test(rawType) || /\.(mp4|webm|ogg|mov|m4v)$/i.test(urlWithoutQuery)) {
                type = 'video';
            } else if (/audio\//i.test(rawType) || /\.(mp3|wav|m4a|aac|oga)$/i.test(urlWithoutQuery)) {
                type = 'audio';
            } else if (/application\//i.test(rawType) || /\.(pdf|doc|docx|xls|xlsx|ppt|pptx|zip)$/i.test(urlWithoutQuery)) {
                type = 'file';
            } else {
                type = 'image';
            }
        }

        if (type !== 'video' && type !== 'audio' && type !== 'file') type = 'image';

        return {
            url: rawUrl,
            type,
            title: title || 'Selected media'
        };
    }

    function vmoRenderPendingMediaSelection() {
        const inputRow = document.querySelector('.vmo-chat-input-row') || document.querySelector('#vmo-prompt-input')?.parentElement;
        if (!inputRow) return;

        let bar = document.getElementById('vmo-pending-media-selection');
        const mediaItems = Array.isArray(window.vmoPendingMediaSelection) ? window.vmoPendingMediaSelection : [];

        if (!mediaItems.length) {
            if (bar) bar.remove();
            return;
        }

        if (!bar) {
            bar = document.createElement('div');
            bar.id = 'vmo-pending-media-selection';
            bar.style.cssText = 'display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:8px 0;padding:8px 10px;background:rgba(0,48,84,0.75);border:1px solid rgba(0,226,255,0.35);border-radius:8px;color:#fff;font-size:12px;';
            inputRow.parentNode.insertBefore(bar, inputRow);
        }

        bar.innerHTML = '';

        const label = document.createElement('span');
        label.textContent = mediaItems.length === 1 ? '1 media attached' : mediaItems.length + ' media attached';
        label.style.cssText = 'color:#00e2ff;font-weight:600;margin-right:2px;';
        bar.appendChild(label);

        mediaItems.forEach((item, index) => {
            const chip = document.createElement('span');
            chip.style.cssText = 'display:inline-flex;align-items:center;gap:6px;max-width:170px;padding:4px 8px;background:rgba(255,255,255,0.08);border:1px solid rgba(255,255,255,0.16);border-radius:999px;';

            if (item.type === 'image') {
                const img = document.createElement('img');
                img.src = item.url;
                img.alt = '';
                img.style.cssText = 'width:24px;height:24px;border-radius:50%;object-fit:cover;flex:0 0 auto;';
                chip.appendChild(img);
            } else {
                const icon = document.createElement('span');
                icon.textContent = item.type === 'video' ? 'Video' : 'Audio';
                icon.style.cssText = 'font-size:10px;color:#00ff9a;font-weight:700;';
                chip.appendChild(icon);
            }

            const title = document.createElement('span');
            title.textContent = item.title || item.type;
            title.style.cssText = 'overflow:hidden;text-overflow:ellipsis;white-space:nowrap;';
            chip.appendChild(title);

            const remove = document.createElement('button');
            remove.type = 'button';
            remove.textContent = 'x';
            remove.setAttribute('aria-label', 'Remove media attachment');
            remove.style.cssText = 'background:transparent;border:0;color:#fff;cursor:pointer;font-size:14px;line-height:1;padding:0 2px;';
            remove.addEventListener('click', () => {
                window.vmoPendingMediaSelection.splice(index, 1);
                vmoRenderPendingMediaSelection();
            });
            chip.appendChild(remove);

            bar.appendChild(chip);
        });

        const clear = document.createElement('button');
        clear.type = 'button';
        clear.textContent = 'Clear';
        clear.style.cssText = 'margin-left:auto;background:transparent;border:1px solid rgba(255,255,255,0.24);color:#fff;border-radius:999px;padding:4px 10px;cursor:pointer;font-size:12px;';
        clear.addEventListener('click', () => window.vmoClearPendingMediaSelection());
        bar.appendChild(clear);
    }

    window.vmoStageMediaForNextPrompt = function(mediaItems, options = {}) {
        const items = (Array.isArray(mediaItems) ? mediaItems : [mediaItems])
            .map(vmoNormalizeBuilderMediaItem)
            .filter(Boolean);

        if (!items.length) return [];

        window.vmoPendingMediaSelection = items;
        vmoRenderPendingMediaSelection();

        if (options.focus !== false) {
            const promptInput = document.getElementById('vmo-prompt-input');
            if (promptInput) {
                setTimeout(() => promptInput.focus(), 50);
            }
        }

        if (options.toast !== false && typeof window.showToast === 'function') {
            window.showToast(items.length === 1 ? 'Media attached to next Jarvis instruction.' : items.length + ' media files attached to next Jarvis instruction.', 'success');
        }

        return items;
    };

    window.vmoClearPendingMediaSelection = function() {
        window.vmoPendingMediaSelection = [];
        vmoRenderPendingMediaSelection();
    };

    function vmoCreateMediaElementHtml(item, options = {}) {
        const media = vmoNormalizeBuilderMediaItem(item);
        if (!media) return '';

        const mediaId = typeof vmoGenerateMediaId === 'function' ? vmoGenerateMediaId() : ('vmo-media-' + Date.now());
        const escapedUrl = vmoEscapeAttr(media.url);
        const escapedTitle = vmoEscapeAttr(media.title || '');
        const style = options.style || 'width:100%;height:auto;display:block;object-fit:cover;';

        if (media.type === 'video') {
            // Default: NO autoplay, NO loop, controls visible so user explicitly starts playback.
            // The previous default (autoplay+muted+loop) caused hero videos to play on every
            // VMO admin page where the saved markup was rendered, drowning out phone calls.
            // Callers that genuinely want a looping background can pass {autoplay:true, loop:true}.
            const wantAutoplay = options.autoplay === true;
            const wantLoop = options.loop === true;
            const wantControls = options.controls !== false;
            const autoplayAttr = wantAutoplay ? ' autoplay muted playsinline' : '';
            const loopAttr = wantLoop ? ' loop' : '';
            const controlsAttr = wantControls ? ' controls' : '';
            return `<video src="${escapedUrl}" data-media-id="${mediaId}" data-vmo-media-source="jarvis"${autoplayAttr}${loopAttr}${controlsAttr} preload="metadata" style="${vmoEscapeAttr(style)}"></video>`;
        }

        if (media.type === 'audio') {
            return `<audio src="${escapedUrl}" data-media-id="${mediaId}" data-vmo-media-source="jarvis" controls style="${vmoEscapeAttr(style)}"></audio>`;
        }

        if (media.type === 'file') {
            return `<a href="${escapedUrl}" data-media-id="${mediaId}" data-vmo-media-source="jarvis" target="_blank" rel="noopener" style="display:flex;align-items:center;justify-content:center;min-height:180px;padding:24px;border-radius:12px;background:rgba(255,255,255,0.08);color:inherit;text-decoration:none;border:1px solid rgba(255,255,255,0.18);font-weight:700;">${escapedTitle || 'Open file'}</a>`;
        }

        return `<img src="${escapedUrl}" alt="${escapedTitle}" data-media-id="${mediaId}" data-vmo-media-source="jarvis" style="${vmoEscapeAttr(style)}" />`;
    }

    function vmoGetMediaInstructionIntent(prompt, mediaItems) {
        const text = String(prompt || '').toLowerCase();
        const count = Array.isArray(mediaItems) ? mediaItems.length : 0;
        const wantsSlider = /\b(carousel|slider|slideshow|slide\s*show|swipeable|swipe|slides)\b/i.test(text);
        const wantsGallery = /\b(gallery|grid|masonry|portfolio|collection|album|lookbook)\b/i.test(text);
        const wantsHeroMedia = /\b(hero|header|banner|masthead|above the fold|top section|landing header)\b/i.test(text);
        const wantsBackground = /\b(background|backdrop|behind|cover the section|cover this section|cover the page|hero video|full\s*screen|fullscreen|entire page|whole page|webpage background)\b/i.test(text);
        const wantsParallax = /\b(parallax|scroll effect|scrolling effect|depth effect)\b/i.test(text);

        if (wantsSlider) return 'slider';
        if (wantsGallery || (count > 1 && !wantsBackground)) return 'gallery';
        if (wantsBackground || wantsParallax || wantsHeroMedia) return 'background';
        return 'insert';
    }

    function vmoInstructionTargetsExistingArea(prompt) {
        return /\b(hero|header|banner|masthead|section|block|part|top|first|main|landing|footer|about|services|products|shop|pricing|contact|booking|appointment|testimonial|menu|team)\b/i.test(String(prompt || ''));
    }

    function vmoFindRequestedBlockIndex(prompt) {
        const blocks = Array.isArray(window.vmoState?.blocks) ? window.vmoState.blocks : [];
        const text = String(prompt || '').toLowerCase();
        const numbered = text.match(/(?:section|block|part)\s*(\d+)/i);

        if (!blocks.length) return -1;

        if (numbered) {
            const requestedIndex = parseInt(numbered[1], 10) - 1;
            if (requestedIndex >= 0 && requestedIndex < blocks.length) return requestedIndex;
        }

        if (/\b(hero|header|top|first|main|landing|banner)\b/i.test(text)) return 0;
        if (/\b(last|bottom|footer)\b/i.test(text)) return blocks.length - 1;

        const keywords = ['about', 'services', 'products', 'shop', 'gallery', 'portfolio', 'pricing', 'contact', 'booking', 'appointment', 'testimonial', 'menu', 'team'];
        for (const keyword of keywords) {
            if (!text.includes(keyword)) continue;

            const found = blocks.findIndex(block => String(block?.html || '').toLowerCase().includes(keyword));
            if (found >= 0) return found;
        }

        if (typeof window.vmoState?.selectedBlockIndex === 'number' && window.vmoState.selectedBlockIndex >= 0 && window.vmoState.selectedBlockIndex < blocks.length) {
            return window.vmoState.selectedBlockIndex;
        }

        return 0;
    }

    function vmoGetPrimaryEditableNode(container) {
        if (!container) return null;
        return container.querySelector('section, [data-vmo-section-type], .vmo-section, header, main, article') || container.firstElementChild || container;
    }

    function vmoGetHeroEditableNode(container) {
        if (!container || typeof container.querySelectorAll !== 'function') return vmoGetPrimaryEditableNode(container);

        const candidates = Array.from(container.querySelectorAll('section, header, [role="banner"], main, article, div'));
        let best = null;
        let bestScore = -1;

        candidates.forEach(node => {
            const tagName = String(node.tagName || '').toLowerCase();
            const identity = `${node.id || ''} ${node.className || ''} ${node.getAttribute('data-vmo-section-type') || ''} ${node.getAttribute('aria-label') || ''}`.toLowerCase();
            let score = 0;

            const hasPrimaryHeading = !!(node.querySelector && node.querySelector('h1'));
            if (/\b(hero|masthead|banner|landing|intro|showcase|above-fold)\b/i.test(identity)) score += 12;
            if (node.getAttribute('role') === 'banner') score += 8;
            if (tagName === 'header') score += hasPrimaryHeading ? 8 : 3;
            if (hasPrimaryHeading) score += 5;
            if (node.querySelector && node.querySelector('img, video, [data-media-id], [style*="background-image"]')) score += 2;
            if (/\b(nav|menu|toolbar|footer)\b/i.test(identity)) score -= 8;

            if (score > bestScore) {
                best = node;
                bestScore = score;
            }
        });

        return best && bestScore > 0 ? best : vmoGetPrimaryEditableNode(container);
    }

    function vmoCommitMediaInstructionChanges(options = {}) {
        if (typeof window.renderBlocks === 'function') window.renderBlocks();
        window.isUnsaved = true;
        if (typeof window.vmoSyncLastContent === 'function') window.vmoSyncLastContent();
        if (typeof window.autoSaveCheckpoint === 'function') window.autoSaveCheckpoint();
        setTimeout(() => {
            if (typeof window.vmoInjectReplaceButtonsInIframe === 'function') window.vmoInjectReplaceButtonsInIframe();
        }, 250);

        if (options.clearPending !== false && typeof window.vmoClearPendingMediaSelection === 'function') {
            window.vmoClearPendingMediaSelection();
        }

        if (typeof window.showToast === 'function') {
            window.showToast(options.message || 'Media applied to the live preview.', 'success');
        }
    }

    function vmoBuildGalleryBlock(mediaItems, prompt) {
        const wantsHoverZoom = /\b(zoom|magnif|hover|enlarge|scale)\b/i.test(prompt || '');
        const items = mediaItems.map(item => {
            const mediaHtml = vmoCreateMediaElementHtml(item, {
                controls: item.type === 'video',
                autoplay: item.type === 'video' ? false : undefined,
                style: 'width:100%;height:100%;display:block;object-fit:cover;border-radius:10px;transition:transform 0.35s ease, filter 0.35s ease;'
            });

            return `<figure class="vmo-ai-gallery-item" style="margin:0;position:relative;overflow:hidden;border-radius:10px;background:#0b1620;aspect-ratio:4/3;box-shadow:0 16px 36px rgba(0,0,0,0.18);">${mediaHtml}</figure>`;
        }).join('\n');

        return `<section class="vmo-ai-media-section vmo-ai-gallery-section" data-vmo-section-type="media-gallery" style="padding:clamp(48px,7vw,96px) clamp(20px,5vw,72px);background:#0f172a;color:#fff;">
    <style data-vmo-ai-media-style>
        .vmo-ai-gallery-section .vmo-ai-gallery-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:18px;max-width:1180px;margin:0 auto;}
        .vmo-ai-gallery-grid.vmo-ai-hover-zoom .vmo-ai-gallery-item:hover img,.vmo-ai-gallery-grid.vmo-ai-hover-zoom .vmo-ai-gallery-item:hover video{transform:scale(1.07);filter:saturate(1.08);}
        @media (max-width:640px){.vmo-ai-gallery-section{padding:40px 16px;}.vmo-ai-gallery-section .vmo-ai-gallery-grid{grid-template-columns:1fr 1fr;gap:10px;}}
    </style>
    <div class="vmo-ai-gallery-grid${wantsHoverZoom ? ' vmo-ai-hover-zoom' : ''}">
        ${items}
    </div>
</section>`;
    }

    function vmoBuildSliderBlock(mediaItems) {
        const slides = mediaItems.map((item, index) => {
            const mediaHtml = vmoCreateMediaElementHtml(item, {
                controls: item.type === 'video',
                autoplay: item.type === 'video' ? false : undefined,
                style: 'width:100%;height:100%;display:block;object-fit:cover;'
            });
            return `<article class="vmo-ai-slide" data-slide-index="${index}" style="min-width:100%;height:100%;scroll-snap-align:start;position:relative;">${mediaHtml}</article>`;
        }).join('\n');

        return `<section class="vmo-ai-media-section vmo-ai-slider-section" data-vmo-section-type="media-slider" style="padding:clamp(48px,7vw,96px) clamp(20px,5vw,72px);background:#101827;color:#fff;">
    <div class="vmo-ai-slider" data-vmo-ai-slider style="position:relative;max-width:1180px;margin:0 auto;overflow:hidden;border-radius:14px;box-shadow:0 24px 70px rgba(0,0,0,0.28);background:#060b12;aspect-ratio:16/9;">
        <div class="vmo-ai-slider-track" style="height:100%;display:flex;overflow-x:auto;scroll-snap-type:x mandatory;scroll-behavior:smooth;scrollbar-width:none;">
            ${slides}
        </div>
        <button type="button" data-vmo-slide="prev" aria-label="Previous slide" style="position:absolute;left:14px;top:50%;transform:translateY(-50%);width:42px;height:42px;border-radius:50%;border:1px solid rgba(255,255,255,0.35);background:rgba(0,0,0,0.45);color:#fff;font-size:24px;line-height:1;cursor:pointer;">‹</button>
        <button type="button" data-vmo-slide="next" aria-label="Next slide" style="position:absolute;right:14px;top:50%;transform:translateY(-50%);width:42px;height:42px;border-radius:50%;border:1px solid rgba(255,255,255,0.35);background:rgba(0,0,0,0.45);color:#fff;font-size:24px;line-height:1;cursor:pointer;">›</button>
    </div>
    <script data-vmo-ai-slider-runtime>(function(){document.querySelectorAll('[data-vmo-ai-slider]').forEach(function(slider){if(slider.dataset.vmoSliderReady)return;slider.dataset.vmoSliderReady='1';var track=slider.querySelector('.vmo-ai-slider-track');if(!track)return;slider.querySelectorAll('[data-vmo-slide]').forEach(function(btn){btn.addEventListener('click',function(){var dir=btn.getAttribute('data-vmo-slide')==='next'?1:-1;track.scrollBy({left:dir*track.clientWidth,behavior:'smooth'});});});});})();</script>
</section>`;
    }

    function vmoApplyMediaBackgroundToBlock(blockIndex, mediaItems, prompt) {
        const blocks = Array.isArray(window.vmoState?.blocks) ? window.vmoState.blocks : [];
        const media = mediaItems[0];
        const block = blocks[blockIndex];

        if (!block || !media) return false;

        const container = document.createElement('div');
        container.innerHTML = String(block.html || '');
        const target = /\b(hero|header|banner|masthead|landing|top section|above the fold)\b/i.test(prompt || '')
            ? vmoGetHeroEditableNode(container)
            : vmoGetPrimaryEditableNode(container);
        if (!target || !target.style) return false;

        const wantsParallax = /\b(parallax|scroll effect|scrolling effect|depth effect)\b/i.test(prompt || '');
        const isFullscreen = /\b(fullscreen|full\s*screen|hero|entire|whole page|webpage)\b/i.test(prompt || '') || media.type === 'video';
        const mediaStyle = 'position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0;pointer-events:none;' + (wantsParallax ? 'transform:scale(1.08);' : '');
        const mediaHtml = vmoCreateMediaElementHtml(media, { style: mediaStyle, autoplay: true, controls: false });

        Array.from(target.children || []).forEach(child => {
            if (child.classList && (child.classList.contains('vmo-ai-background-media') || child.classList.contains('vmo-ai-background-overlay'))) {
                child.remove();
            }
        });

        target.style.position = target.style.position && target.style.position !== 'static' ? target.style.position : 'relative';
        target.style.overflow = 'hidden';
        if (isFullscreen && !target.style.minHeight) target.style.minHeight = '100vh';
        if (!target.style.backgroundColor || /^(transparent|rgba\(0,\s*0,\s*0,\s*0\))$/i.test(target.style.backgroundColor)) {
            target.style.backgroundColor = '#070b12';
        }

        const mediaWrap = document.createElement('div');
        mediaWrap.className = 'vmo-ai-background-media';
        mediaWrap.setAttribute('data-vmo-media-role', media.type === 'video' ? 'background-video' : 'background-image');
        if (wantsParallax) mediaWrap.setAttribute('data-vmo-parallax', 'true');
        mediaWrap.style.cssText = 'position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden;';
        mediaWrap.innerHTML = mediaHtml;

        const overlay = document.createElement('div');
        overlay.className = 'vmo-ai-background-overlay';
        overlay.style.cssText = 'position:absolute;inset:0;z-index:1;pointer-events:none;background:linear-gradient(120deg,rgba(0,0,0,0.42),rgba(0,0,0,0.12));';

        target.insertBefore(overlay, target.firstChild);
        target.insertBefore(mediaWrap, target.firstChild);

        Array.from(target.children || []).forEach(child => {
            if (child === mediaWrap || child === overlay || !child.style) return;
            if (!child.style.position || child.style.position === 'static') child.style.position = 'relative';
            if (!child.style.zIndex) child.style.zIndex = '2';
        });

        if (wantsParallax && !container.querySelector('[data-vmo-ai-parallax-runtime]')) {
            const runtime = document.createElement('script');
            runtime.setAttribute('data-vmo-ai-parallax-runtime', '');
            runtime.textContent = "(function(){function update(){document.querySelectorAll('[data-vmo-parallax=\"true\"] img,[data-vmo-parallax=\"true\"] video').forEach(function(el){var rect=el.parentElement.getBoundingClientRect();var shift=Math.max(-42,Math.min(42,rect.top*-0.08));el.style.transform='scale(1.08) translateY('+shift+'px)';});}window.addEventListener('scroll',update,{passive:true});window.addEventListener('resize',update);update();})();";
            container.appendChild(runtime);
        }

        block.html = container.innerHTML;
        return true;
    }

    function vmoApplyMediaInsertToBlock(blockIndex, mediaItems) {
        const blocks = Array.isArray(window.vmoState?.blocks) ? window.vmoState.blocks : [];
        const block = blocks[blockIndex];
        if (!block) return false;

        const container = document.createElement('div');
        container.innerHTML = String(block.html || '');
        const target = vmoGetPrimaryEditableNode(container);
        if (!target) return false;

        const wrapper = document.createElement('div');
        wrapper.className = 'vmo-ai-inserted-media';
        wrapper.style.cssText = 'margin:24px auto;display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(min(220px,100%),1fr));align-items:start;max-width:min(100%,860px);';
        wrapper.innerHTML = mediaItems.map(item => vmoCreateMediaElementHtml(item, {
            controls: item.type !== 'image',
            autoplay: false,
            style: item.type === 'image'
                ? 'width:100%;height:auto;max-height:420px;display:block;object-fit:cover;border-radius:12px;box-shadow:0 16px 40px rgba(0,0,0,0.18);'
                : 'width:100%;aspect-ratio:16/9;max-height:420px;display:block;object-fit:cover;border-radius:12px;box-shadow:0 16px 40px rgba(0,0,0,0.18);'
        })).join('\n');

        target.appendChild(wrapper);
        block.html = container.innerHTML;
        return true;
    }

    window.vmoApplyMediaInstructionToPreview = function(prompt, mediaItems, options = {}) {
        const items = (Array.isArray(mediaItems) ? mediaItems : [mediaItems])
            .map(vmoNormalizeBuilderMediaItem)
            .filter(Boolean);

        if (!items.length) {
            if (typeof window.showToast === 'function') window.showToast('Select or upload media first.', 'error');
            return false;
        }

        if (!window.vmoState || !Array.isArray(window.vmoState.blocks)) {
            window.vmoState = window.vmoState || { blocks: [] };
            window.vmoState.blocks = [];
        }

        const intent = vmoGetMediaInstructionIntent(prompt, items);
        const targetIndex = vmoFindRequestedBlockIndex(prompt);
        let changed = false;

        if (typeof window.pushUndo === 'function') window.pushUndo();

        if (intent === 'gallery') {
            window.vmoState.blocks.push({
                id: window.getUniqueBlockId ? window.getUniqueBlockId() : String(Date.now()),
                html: vmoBuildGalleryBlock(items, prompt),
                type: 'media-gallery'
            });
            changed = true;
        } else if (intent === 'slider') {
            window.vmoState.blocks.push({
                id: window.getUniqueBlockId ? window.getUniqueBlockId() : String(Date.now()),
                html: vmoBuildSliderBlock(items),
                type: 'media-slider'
            });
            changed = true;
        } else if (intent === 'background') {
            changed = targetIndex >= 0 && vmoApplyMediaBackgroundToBlock(targetIndex, items, prompt);
        } else {
            changed = targetIndex >= 0 && vmoApplyMediaInsertToBlock(targetIndex, items);
        }

        if (!changed && !vmoInstructionTargetsExistingArea(prompt)) {
            window.vmoState.blocks.push({
                id: window.getUniqueBlockId ? window.getUniqueBlockId() : String(Date.now()),
                html: vmoBuildGalleryBlock(items, prompt),
                type: 'media-gallery'
            });
            changed = true;
        } else if (!changed && typeof window.showToast === 'function') {
            window.showToast('I could not find the requested website area, so I did not insert the media in the wrong place.', 'error');
        }

        if (changed) {
            vmoCommitMediaInstructionChanges({
                clearPending: options.clearPending !== false,
                message: intent === 'background' ? 'Media background applied to the live preview.' : 'Media inserted into the live preview.'
            });
        }

        return changed;
    };

    /* ========== INSERT MEDIA MODAL (restored from OLDER plugin with AI agent) ========== */
    function openInsertMediaModal(mode = "insert", selector = null, mediaType = "image") {
        const isReplace = (mode === "replace");
        
        // Remove any existing modal
        $('#vmo-insert-modal').remove();
        
        const modal = `
        <div id="vmo-insert-modal" class="vmo-modal" style="display:flex !important;position:fixed !important;top:0 !important;left:0 !important;width:100vw !important;height:100vh !important;background:rgba(0,0,0,0.5) !important;z-index:999999;align-items:center !important;justify-content:center !important;backdrop-filter:blur(2px);margin:0 !important;padding:0 !important;border:none !important;outline:none !important;box-sizing:border-box !important;">
            <div class="vmo-modal-content" style="background:#001a2e;border-radius:15px;padding:30px;width:90%;max-width:800px;max-height:90vh;overflow-y:auto;color:#fff;box-shadow:0 30px 80px rgba(0,226,255,0.4);border:2px solid #00e2ff;position:relative;transform:scale(1);transition:all 0.3s ease;margin:0;box-sizing:border-box;">
                <h3 style="margin-top:0;color:#00e2ff;text-align:center;margin-bottom:25px;font-size:24px;">${isReplace ? "🔄 Replace Media" : "📸 Add Media"}</h3>
                <div style="margin-bottom:15px;display:flex;gap:10px;flex-wrap:wrap;justify-content:center;">
                    <button id="vmo-insert-library" class="vmo-btn" style="background:#00ff9a;color:#001a2e;border:none;padding:10px 20px;border-radius:8px;font-weight:600;cursor:pointer;transition:all 0.2s ease;">🖼️ From Media Library</button>
                    <button id="vmo-insert-generate" class="vmo-btn" style="background:#003054;color:#00e2ff;border:1px solid #00e2ff;padding:10px 20px;border-radius:8px;font-weight:600;cursor:pointer;transition:all 0.2s ease;">🎨 Generate Image</button>
                    <button id="vmo-close-modal" class="vmo-btn" style="background:#333;color:#fff;border:none;padding:10px 20px;border-radius:8px;font-weight:600;cursor:pointer;transition:all 0.2s ease;">Cancel</button>
                </div>
                <div id="vmo-preview-images" style="display:none;gap:10px;flex-wrap:wrap;align-items:center;justify-content:center;margin:20px 0;"></div>
                <div id="vmo-what-to-do" style="display:none;margin-top:20px;">
                    <div style="font-weight:600;color:#00e2ff;display:block;margin-bottom:12px;text-align:center;">
                        ${isReplace ? "✨ Ready to replace media directly" : "🚀 How should this media be integrated?"}
                    </div>
                    ${!isReplace ? `
                    <div id="vmo-preset-prompts" style="margin-bottom:12px;display:flex;gap:8px;flex-wrap:wrap;justify-content:center;">
                        <button class="vmo-preset-btn" data-prompt="Create a visually stunning fullscreen hero section with this image, overlay text, a bold call-to-action button, and advanced parallax effect. Use modern CSS transitions and blend modes for a cinematic feel." style="background:#003054;color:#00e2ff;border:1px solid #00e2ff;padding:6px 12px;border-radius:6px;font-size:12px;cursor:pointer;transition:all 0.2s ease;">Hero Image</button>
                        <button class="vmo-preset-btn" data-prompt="Insert this video as a striking fullscreen background hero with smooth fade-in and dynamic playback controls. Ensure no overlays obscure the video. Prefer subtle gradient overlays for legibility if needed." style="background:#003054;color:#00e2ff;border:1px solid #00e2ff;padding:6px 12px;border-radius:6px;font-size:12px;cursor:pointer;transition:all 0.2s ease;">Hero Video</button>
                        <button class="vmo-preset-btn" data-prompt="Create a high-quality gallery or portfolio grid with this media. Use advanced hover transitions, fade-in and slide-up animations, and responsive masonry layout." style="background:#003054;color:#00e2ff;border:1px solid #00e2ff;padding:6px 12px;border-radius:6px;font-size:12px;cursor:pointer;transition:all 0.2s ease;">Gallery</button>
                        <button class="vmo-preset-btn" data-prompt="Insert this logo in the top left or center of the section, with high z-index and subtle drop shadow or glass effect for maximum clarity." style="background:#003054;color:#00e2ff;border:1px solid #00e2ff;padding:6px 12px;border-radius:6px;font-size:12px;cursor:pointer;transition:all 0.2s ease;">Logo</button>
                        <button class="vmo-preset-btn" data-prompt="Showcase this image in a beautiful, swipeable carousel with smooth transitions, navigation arrows, and slide indicators." style="background:#003054;color:#00e2ff;border:1px solid #00e2ff;padding:6px 12px;border-radius:6px;font-size:12px;cursor:pointer;transition:all 0.2s ease;">Carousel</button>
                        <button class="vmo-preset-btn" data-prompt="Apply this image as CSS backdrop to the section element with z-index: 1. Use inline style: background-image: url(IMAGE_URL), background-size: cover, background-position: center. Keep all existing content intact with z-index: 2+. The image must be applied directly to the section's style attribute." style="background:#4a1a7a;color:#dda0ff;border:1px solid:#9966ff;padding:6px 12px;border-radius:6px;font-size:12px;cursor:pointer;transition:all 0.2s ease;">Section Backdrop</button>
                        <button class="vmo-preset-btn" data-prompt="Layer this media on top of existing elements with high z-index (999+) for stacking and overlay effects. Position it prominently above all other content while maintaining proper viewport visibility within the iframe." style="background:#2d5a1f;color:#90ff70;border:1px solid #60cc40;padding:6px 12px;border-radius:6px;font-size:12px;cursor:pointer;transition:all 0.2s ease;">Stack On Top</button>
                    </div>
                    <textarea id="vmo-img-action-prompt" style="width:100%;min-height:60px;background:#003054;color:#fff;border:1px solid #00e2ff;border-radius:8px;padding:12px;font-family:inherit;font-size:14px;box-sizing:border-box;" placeholder="Describe how to use the media..."></textarea>
                    <button id="vmo-stage-media-chat" class="vmo-btn" style="margin-top:10px;background:#003054;color:#00e2ff;border:1px solid #00e2ff;padding:10px 20px;border-radius:8px;font-weight:600;cursor:pointer;width:100%;font-size:14px;transition:all 0.2s ease;">Use With Jarvis Chat</button>
                    ` : ''}
                    <button id="vmo-img-action-send" class="vmo-btn vmo-btn-primary" style="margin-top:15px;background:#00ff9a;color:#001a2e;border:none;padding:12px 30px;border-radius:8px;font-weight:600;cursor:pointer;width:100%;font-size:16px;transition:all 0.2s ease;">${isReplace ? "Replace Now" : "Insert"}</button>
                </div>
                <div id="vmo-gen-form" style="display:none;margin-top:15px;">
                    <textarea id="vmo-gen-desc" placeholder="Describe your image..." style="width:100%;min-height:60px;background:#003054;color:#fff;border:1px solid #00e2ff;border-radius:8px;padding:12px;font-family:inherit;font-size:14px;box-sizing:border-box;margin-bottom:15px;"></textarea>
                    <button id="vmo-submit-gen" class="vmo-btn" style="background:#00ff9a;color:#001a2e;border:none;padding:12px 30px;border-radius:8px;font-weight:600;cursor:pointer;width:100%;font-size:16px;transition:all 0.2s ease;">Generate Image</button>
                    <div id="vmo-insert-job-progress" style="display:none;margin-top:14px;background:rgba(0,48,84,0.72);border:1px solid rgba(0,226,255,0.45);border-radius:8px;padding:12px;">
                        <div id="vmo-insert-job-status" style="color:#fff;font-size:13px;margin-bottom:8px;">Preparing media job...</div>
                        <div style="height:8px;background:rgba(255,255,255,0.12);border-radius:999px;overflow:hidden;"><div id="vmo-insert-job-bar" style="height:100%;width:4%;background:#00ff9a;border-radius:999px;transition:width 0.25s ease;"></div></div>
                        <div id="vmo-insert-job-eta" style="margin-top:7px;color:#9fc7d6;font-size:12px;"></div>
                    </div>
                </div>
            </div>
        </div>`;

        // Always append modal directly to body for true fullscreen coverage
        $('body').append(modal);
        
        // Add critical CSS to ensure true fullscreen behavior
        const modalCSS = `
        <style id="vmo-modal-fullscreen-css">
        #vmo-insert-modal.vmo-modal {
            display: flex !important;
            position: fixed !important;
            top: 0 !important;
            left: 0 !important;
            right: 0 !important;
            bottom: 0 !important;
            width: 100vw !important;
            height: 100vh !important;
            max-width: none !important;
            max-height: none !important;
            margin: 0 !important;
            padding: 0 !important;
            border: none !important;
            transform: none !important;
        }
        body.vmo-modal-open {
            overflow: hidden !important;
        }
        </style>
        `;
        
        if ($('#vmo-modal-fullscreen-css').length === 0) {
            $('head').append(modalCSS);
        }
        
        // Prevent body scroll when modal is open
        $('body').addClass('vmo-modal-open');
        
        // Set initial state for animation
        $('#vmo-insert-modal').css({
            'opacity': '0',
            'pointer-events': 'auto'
        }).find('.vmo-modal-content').css({
            'transform': 'scale(0.8)',
            'opacity': '0'
        });
        
        $('#vmo-insert-modal').data({ media: [], mode, selector, mediaType });        // Close modal on background click or cancel button
        $('#vmo-insert-modal').on('click', function(e) {
            if (e.target === this) {
                // Add fade-out animation before removing
                $(this).css('opacity', '0');
                $('body').removeClass('vmo-modal-open');
                setTimeout(() => $(this).remove(), 300);
            }
        });

        // Close modal button with animation
        $('#vmo-close-modal').on('click', () => {
            $('#vmo-insert-modal').css('opacity', '0');
            $('body').removeClass('vmo-modal-open');
            setTimeout(() => $('#vmo-insert-modal').remove(), 300);
        });

        // Add entrance animation
        setTimeout(() => {
            $('#vmo-insert-modal').css({
                'opacity': '1',
                'transition': 'opacity 0.3s ease'
            });
            $('#vmo-insert-modal .vmo-modal-content').css({
                'transform': 'scale(1)',
                'opacity': '1',
                'transition': 'all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1)'
            });
        }, 50);

        // Preset prompt buttons
        $('.vmo-preset-btn').on('click', function() {
            const prompt = $(this).data('prompt');
            $('#vmo-img-action-prompt').val(prompt);
        });

        $('#vmo-stage-media-chat').on('click', function() {
            const mediaArr = $('#vmo-insert-modal').data('media') || [];
            if (!mediaArr.length) return showToast('Select or generate media first.', 'error');

            window.vmoStageMediaForNextPrompt(mediaArr, { focus: true, toast: true });
            $('body').removeClass('vmo-modal-open');
            $('#vmo-insert-modal').remove();
        });

        // Ensure insert button is properly initialized
        function ensureInsertButtonReady() {
            const $insertBtn = $('#vmo-img-action-send');
            if ($insertBtn.length) {
                $insertBtn.prop('disabled', false).removeClass('vmo-btn-loading');
                console.log('[VMO Modal] Insert button initialized and ready');
            }
        }
        
        // Initialize button state
        setTimeout(ensureInsertButtonReady, 100);

        // Media library selection
        let frame = null;
        $('#vmo-insert-library').on('click', () => {
            // Temporarily lower our modal's z-index so WordPress media library can appear on top
            $('#vmo-insert-modal').css('z-index', '150000');
            
            // Also temporarily hide our modal to prevent any interference
            $('#vmo-insert-modal').css('visibility', 'hidden');
            
            if (frame) {
                frame.open();
                // Show our modal again once the media library is fully loaded
                setTimeout(() => $('#vmo-insert-modal').css('visibility', 'visible'), 100);
                return;
            }
            
            const frameConfig = {
                title: 'Select Media',
                button: { text: 'Insert' },
                multiple: true
            };

            if (isReplace) {
                frameConfig.library = { type: mediaType === 'video' ? 'video' : 'image' };
            }

            frame = wp.media(frameConfig);
            
            frame.on('select', function() {
                console.log('[VMO Media] Media selection started...');
                const selections = frame.state().get('selection');
                const mediaArr = selections.map(attachment => {
                    const json = attachment.toJSON();
                    return {
                        url: json.url,
                        type: json.type,
                        title: json.title || json.filename
                    };
                });
                
                console.log('[VMO Media] Selected media:', mediaArr);
                
                // Store media data with redundancy
                $('#vmo-insert-modal').data('media', mediaArr);
                if (!isReplace) {
                    window.vmoStageMediaForNextPrompt(mediaArr, { focus: false, toast: true });
                }
                
                // Verify the data was stored correctly
                const storedMedia = $('#vmo-insert-modal').data('media');
                console.log('[VMO Media] Stored media verification:', storedMedia);
                
                displaySelectedMedia(mediaArr);
                $('#vmo-what-to-do').show();
                
                // Update button text since we're using existing media, not generating
                if (!isReplace) {
                    $('#vmo-img-action-send').text('Insert');
                }
                
                // Ensure button is enabled and ready
                const $insertBtn = $('#vmo-img-action-send');
                $insertBtn.prop('disabled', false).removeClass('vmo-btn-loading');
                console.log('[VMO Media] Insert button enabled and ready');
                
                // Restore our modal's z-index and visibility after selection
                $('#vmo-insert-modal').css({
                    'z-index': '999999',
                    'visibility': 'visible'
                });
            });
            
            // Restore z-index and visibility when media library is closed without selection
            frame.on('close', function() {
                $('#vmo-insert-modal').css({
                    'z-index': '999999',
                    'visibility': 'visible'
                });
            });
            
            // Show our modal again once the media library opens
            frame.on('open', function() {
                setTimeout(() => $('#vmo-insert-modal').css('visibility', 'visible'), 200);
            });
            
            frame.open();
        });

        // AI Image generation
        $('#vmo-insert-generate').on('click', () => {
            $('#vmo-gen-form').show();
            $('#vmo-what-to-do').hide();
        });

        const updateInsertJobProgress = (job, message) => {
            const progress = Math.max(0, Math.min(100, parseInt(job?.progress || 0, 10) || 0));
            $('#vmo-insert-job-progress').show();
            $('#vmo-insert-job-status').text(message || job?.stage_label || job?.status || 'Preparing media job...');
            $('#vmo-insert-job-bar').css('width', Math.max(4, progress) + '%');
            $('#vmo-insert-job-eta').text(job?.estimated_remaining_text ? 'Estimated remaining: ' + job.estimated_remaining_text : '');
        };

        const runInsertImageJob = async (desc) => {
            const slotContext = vmoBuildReplacementSlotContext({
                selector,
                mediaType: 'image',
                placement_role: isReplace ? 'manual_replace_image' : 'manual_insert_image',
                source: isReplace ? 'insert_modal_replace_generate' : 'insert_modal_generate'
            });
            const request = {
                project_id: window.currentProjectId || window.vmoCurrentProjectId || 0,
                media_type: 'image',
                replacement_type: 'ai_image',
                prompt: desc,
                model: 'openai-image-generation-2',
                size: '1024x1024',
                slot_context: JSON.stringify(slotContext)
            };

            updateInsertJobProgress({ progress: 3, stage_label: 'Checking generation cost...' });
            await vmoReplacementAjax('vmo_estimate_media_replacement_job', request);
            const createData = await vmoReplacementAjax('vmo_create_media_replacement_job', request);
            let job = createData.job || {};
            updateInsertJobProgress(job);
            if (job.status === 'complete' && job.url) return job;

            let attempts = 0;
            let softNoticeShown = false;
            const softNoticeAttempts = 80;
            return new Promise((resolve, reject) => {
                const poll = () => {
                    attempts += 1;
                    vmoReplacementAjax('vmo_get_media_replacement_job', { job_id: job.id }).then((statusData) => {
                        job = statusData.job || job;
                        updateInsertJobProgress(job);
                        if (job.status === 'complete' && job.url) {
                            resolve(job);
                            return;
                        }
                        if (job.status === 'failed') {
                            reject(job);
                            return;
                        }
                        if (attempts >= softNoticeAttempts) {
                            softNoticeShown = true;
                            updateInsertJobProgress(Object.assign({}, job, {
                                progress: Math.max(parseInt(job.progress || 0, 10) || 0, 60),
                                stage_label: 'This generation is taking longer than usual, but the job is still active. VMO will continue processing it safely.',
                                estimated_remaining_text: 'Still generating safely'
                            }));
                        }
                        setTimeout(poll, softNoticeShown ? 10000 : 3000);
                    }).catch((error) => {
                        const message = error?.plain_language_error || error?.message || error?.error || '';
                        if (/not found|unauthorized|not logged in/i.test(String(message))) {
                            reject(error);
                            return;
                        }
                        softNoticeShown = softNoticeShown || attempts >= softNoticeAttempts;
                        if (softNoticeShown) {
                            updateInsertJobProgress(Object.assign({}, job || {}, {
                                progress: Math.max(parseInt(job?.progress || 0, 10) || 0, 60),
                                stage_label: 'VMO is still checking this media job. Generation continues safely in the background.',
                                estimated_remaining_text: 'Still generating safely'
                            }));
                        }
                        setTimeout(poll, softNoticeShown ? 10000 : 5000);
                    });
                };
                setTimeout(poll, 1200);
            });
        };

        $('#vmo-submit-gen').on('click', async () => {
            const desc = $('#vmo-gen-desc').val().trim();
            if (!desc) return showToast('Describe the image first.', 'error');

            const $generateBtn = $('#vmo-submit-gen');
            $generateBtn.prop('disabled', true).text('Generating...');
            showToast('Generating image...', 'info', true, true);
            try {
                const job = await runInsertImageJob(desc);
                hideToast(true);
                const mediaArr = [{
                    url: job.url,
                    type: 'image',
                    title: 'Generated Image',
                    attachment_id: job.attachment_id || 0,
                    job_id: job.id || ''
                }];
                console.log('[VMO Generate] Generated media:', mediaArr);

                $('#vmo-insert-modal').data('media', mediaArr);
                if (!isReplace) {
                    window.vmoStageMediaForNextPrompt(mediaArr, { focus: false, toast: true });
                }

                // Verify the data was stored correctly
                const storedMedia = $('#vmo-insert-modal').data('media');
                console.log('[VMO Generate] Stored media verification:', storedMedia);

                displaySelectedMedia(mediaArr);
                $('#vmo-gen-form').hide();
                $('#vmo-what-to-do').show();

                // Update button text since we generated media with AI
                $('#vmo-img-action-send').text('Generate & Insert');

                // Ensure button is enabled and ready
                const $insertBtn = $('#vmo-img-action-send');
                $insertBtn.prop('disabled', false).removeClass('vmo-btn-loading');
                console.log('[VMO Generate] Insert button enabled and ready');
            } catch (error) {
                hideToast(true);
                const message = error?.plain_language_error || error?.message || 'Generation failed. Please try again.';
                updateInsertJobProgress({ progress: 100, stage_label: message }, message);
                showToast(message, 'error');
            } finally {
                $generateBtn.prop('disabled', false).text('Generate Image');
            }
        });

        function displaySelectedMedia(mediaArr) {
            const previewHtml = mediaArr.map(m => {
                const normalized = vmoNormalizeBuilderMediaItem(m);
                if (!normalized) return '';

                if (normalized.type === 'video') {
                    return `<video src="${vmoEscapeAttr(normalized.url)}" style="max-width:150px;max-height:100px;border-radius:6px;" muted></video>`;
                } else if (normalized.type === 'audio') {
                    return `<audio src="${vmoEscapeAttr(normalized.url)}" style="width:150px;" controls></audio>`;
                } else if (normalized.type === 'file') {
                    return `<a href="${vmoEscapeAttr(normalized.url)}" target="_blank" rel="noopener" style="display:inline-flex;align-items:center;justify-content:center;max-width:150px;min-height:72px;padding:10px;border-radius:6px;background:#003054;color:#fff;text-decoration:none;text-align:center;font-size:12px;">${vmoEscapeAttr(normalized.title || 'File')}</a>`;
                }

                return `<img src="${vmoEscapeAttr(normalized.url)}" style="max-width:150px;max-height:100px;border-radius:6px;object-fit:cover;" alt="${vmoEscapeAttr(normalized.title || 'Media')}">`;
            }).join('');
            
            $('#vmo-preview-images').html(previewHtml).show();
        }
    }

    // Enhanced AI-powered insert action with dedicated agent
    $(document).off('click.vmoSendMedia').on('click.vmoSendMedia', '#vmo-img-action-send', function () {
        console.log('[VMO Insert] Button clicked - checking button state and data...');
        
        // Check if button is already disabled/loading
        const $button = $(this);
        if ($button.prop('disabled') || $button.hasClass('vmo-btn-loading')) {
            console.log('[VMO Insert] Button is disabled or loading, ignoring click');
            return false;
        }
        
        const mediaArr = $('#vmo-insert-modal').data('media') || [];
        const modalData = $('#vmo-insert-modal').data();
        const { mode, selector, mediaType } = modalData;
        
        console.log('[VMO Insert] Media data:', mediaArr, 'Modal data:', modalData);
        
        if (!mediaArr.length) {
            console.log('[VMO Insert] No media found in modal data');
            return showToast('Select or generate at least one image / video first.', 'error');
        }
        
        // **REPLACE MODE: Direct replacement without AI**
        if (mode === 'replace') {
            if (mediaArr.length > 0) {
                window.vmoOnMediaSelected(mediaArr[0].url, { openAdjuster: true, source: 'replace-modal' });
                $('body').removeClass('vmo-modal-open');
                $('#vmo-insert-modal').remove();
            }
            return;
        }
        
        // **INSERT MODE: AI-integrated with enhanced dedicated agent**
        const prompt = $('#vmo-img-action-prompt').val().trim();
        if (!prompt) return showToast('Describe how to use the media.', 'error');

        // Check if user has selected a specific element
        if (window.vmoElementRefiner && window.vmoElementRefiner.selectedElement) {
            // ELEMENT-SPECIFIC REFINEMENT MODE
            const mediaUrl = mediaArr[0]?.url;
            if (!mediaUrl) return showToast('Media URL not found', 'error');
            
            if (window.showToast) {
                window.showToast('Applying media to selected element...', 'info', true, true);
            }
            
            // Use element-specific refinement
            if (window.refineSelectedElement) {
                window.refineSelectedElement(prompt, mediaUrl);
                
                // Close modal and clear selection
                $('#vmo-insert-modal').remove();
                $('body').removeClass('vmo-modal-open');
                
                if (window.clearElementSelection) {
                    window.clearElementSelection();
                }
            } else {
                showToast('Element refinement system not loaded', 'error');
            }
            return;
        }

        if (typeof window.vmoApplyMediaInstructionToPreview === 'function' && window.vmoApplyMediaInstructionToPreview(prompt, mediaArr, { clearPending: true })) {
            $('body').removeClass('vmo-modal-open');
            $('#vmo-insert-modal').remove();
            if (typeof window.appendChatMessage === 'function') {
                window.appendChatMessage('ai', 'Done. I applied the selected media to the live preview.');
            }
            return;
        }

        // Always pass full structure for smart context
        let sectionMap = "";
        let fullPageHtml = "";
        if (window.vmoState && Array.isArray(window.vmoState.blocks)) {
            sectionMap = window.vmoState.blocks.map((b, i) => `<!-- SECTION ${i+1} -->: ${b.type || 'Section'}`).join('\n');
            fullPageHtml = window.vmoState.blocks.map(b => b.html).join('\n\n');
        }

        // Compose media HTML block
        const mediaHtmlArr = mediaArr.map(m =>
            (m.type === 'video' || /\.(mp4|webm|ogg)$/i.test(m.url))
              ? `<video src="${m.url}" autoplay muted loop playsinline style="max-width:100%"></video>`
              : `<img src="${m.url}" alt="" style="max-width:100%" />`
        ).join('\n');

        // Enhanced user prompt with full context awareness
        const enhancedPrompt = `
CURRENT PAGE STRUCTURE:
${sectionMap}

CURRENT FULL PAGE HTML:
${fullPageHtml}

USER INSTRUCTION:
${prompt}

SELECTED MEDIA TO INTEGRATE:
${mediaHtmlArr}

REQUIREMENTS:
- Analyze my instruction and enhance it with professional design insights
- If I mention specific sections, target only those sections
- If I mention effects like overlays, animations, or scroll interactions, implement them with modern techniques
- If I mention carousels, implement with appropriate libraries (Splide.js recommended)
- If I mention background media, implement with proper layering and responsive design
- Return the complete updated HTML for the entire page
- Maintain all section numbering and structure
- Use semantic HTML and modern CSS/JS techniques
        `.trim();

        // Prevent double submissions
        const $submitBtn = $('#vmo-img-action-send');
        if ($submitBtn.prop('disabled') || $submitBtn.hasClass('vmo-btn-loading')) {
            console.log('[VMO Insert] Button already processing, preventing double submission');
            return false;
        }
        
        console.log('[VMO Insert] Disabling button and starting AJAX request');
        $submitBtn.prop('disabled', true).addClass('vmo-btn-loading');
        
        // Add a fail-safe timeout to re-enable button if something goes wrong
        const failsafeTimeout = setTimeout(() => {
            console.log('[VMO Insert] Fail-safe timeout triggered - re-enabling button');
            $submitBtn.prop('disabled', false).removeClass('vmo-btn-loading');
            if (window.hideToast) hideToast(true);
            showToast('Request timed out. Please try again.', 'error');
        }, 60000); // 60 second timeout

        // Intelligent section detection - check for selected section or use hero detection
        let targetSectionIndex = 0;
        let targetSectionHtml = window.vmoState?.blocks?.[0]?.html || '';
        
        // Check if user has selected a specific section
        if (window.vmoState && window.vmoState.selectedBlockIndex !== undefined && window.vmoState.selectedBlockIndex >= 0) {
            targetSectionIndex = window.vmoState.selectedBlockIndex;
            targetSectionHtml = window.vmoState.blocks?.[targetSectionIndex]?.html || '';
            console.log('[VMO Insert] Using selected section index:', targetSectionIndex);
        }
        // If prompt mentions hero/header/main, always target section 0
        else if (/hero|header|main|banner|landing|fullscreen|top\s*section/i.test(enhancedPrompt)) {
            targetSectionIndex = 0;
            targetSectionHtml = window.vmoState?.blocks?.[0]?.html || '';
            console.log('[VMO Insert] Hero detected - targeting section 0');
        }

        // Send to enhanced backend AI agent
        showToast('🤖 AI Agent processing media integration...', 'info', true, true);
        $.ajax({
            url: vmo_ajax.ajaxurl,
            method: 'POST',
            dataType: 'json',
            data: {
                action: 'vmo_insert_media_section',
                security: vmo_ajax.nonce,
                media_url: mediaArr[0].url,
                media_type: mediaArr[0].type || 'image',
                media_items: JSON.stringify(mediaArr),
                prompt: enhancedPrompt,
                section_index: targetSectionIndex,
                full_page_html: fullPageHtml,
                section_html: targetSectionHtml
            }
        }).done(res => {
            hideToast(true);

            if (res.success && res.data?.html) {
                // Split the HTML output back into sections
                let newBlocks = [];
                let html = res.data.html.replace(/<\/?(html|head|body|prompt)[^>]*>/gi, '')
                                       .replace(/<!DOCTYPE[^>]*>/gi, '')
                                       .trim();

                // Simple splitter: looks for <!-- SECTION N -->
                let split = html.split(/(<!--\s*SECTION\s*\d+.*?-->)/gi);
                let curSection = "";
                split.forEach((chunk, i) => {
                    if (/<!--\s*SECTION\s*\d+/i.test(chunk)) {
                        if (curSection.trim()) newBlocks.push({ 
                            html: curSection.trim(), 
                            type: 'section', 
                            id: window.getUniqueBlockId ? window.getUniqueBlockId() : Date.now().toString()
                        });
                        curSection = chunk;
                    } else {
                        curSection += chunk;
                    }
                });
                if (curSection.trim()) newBlocks.push({ 
                    html: curSection.trim(), 
                    type: 'section', 
                    id: window.getUniqueBlockId ? window.getUniqueBlockId() : Date.now().toString()
                });
                
                // Overwrite blocks with new structure
                if (typeof window.pushUndo === 'function') window.pushUndo();
                window.vmoState.blocks = newBlocks;
                if (typeof window.renderBlocks === 'function') window.renderBlocks();
                window.isUnsaved = true;
                if (typeof window.autoSaveCheckpoint === 'function') window.autoSaveCheckpoint();
                if (typeof window.vmoInjectReplaceButtonsInIframe === 'function') window.vmoInjectReplaceButtonsInIframe();
                
                showToast('🎉 Media integrated successfully by AI Agent!', 'success');
                $('body').removeClass('vmo-modal-open');
                $('#vmo-insert-modal').remove();
            } else {
                const msg = res?.data?.message || 'Failed to integrate media.';
                showToast('AI Agent: ' + msg, 'error');
            }
        }).fail((xhr) => {
            hideToast(true);
            // Try to surface server-provided error message
            let msg = 'AI Agent encountered a server error.';
            try {
                if (xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) {
                    msg = 'AI Agent: ' + xhr.responseJSON.data.message;
                } else if (xhr.responseText) {
                    const parsed = JSON.parse(xhr.responseText);
                    if (parsed?.data?.message) msg = 'AI Agent: ' + parsed.data.message;
                    else if (parsed?.message) msg = 'AI Agent: ' + parsed.message;
                }
            } catch(_) {}
            showToast(msg, 'error');
        }).always(() => {
            console.log('[VMO Insert] AJAX request completed - re-enabling button');
            clearTimeout(failsafeTimeout);
            $submitBtn.prop('disabled', false).removeClass('vmo-btn-loading');
            
            // Additional safety check - ensure button is clickable
            setTimeout(() => {
                if ($submitBtn.prop('disabled') || $submitBtn.hasClass('vmo-btn-loading')) {
                    console.log('[VMO Insert] Button still disabled after AJAX - force enabling');
                    $submitBtn.prop('disabled', false).removeClass('vmo-btn-loading');
                }
            }, 100);
        });
    });

    window.openInsertMediaModal = openInsertMediaModal;

    function vmoBuildPlacementTransform(scale, offsetX, offsetY) {
        return `translate(${offsetX}px, ${offsetY}px) scale(${scale})`;
    }

    function vmoRemoveMediaGenerationMarkersFromNode(node) {
        if (!node || !node.removeAttribute) return;

        ['data-vmo-generate', 'data-vmo-prompt', 'data-vmo-sora', 'data-vmo-sora-prompt', 'data-vmo-generated'].forEach(attr => {
            node.removeAttribute(attr);
        });
    }

    function vmoReadPlacementState(holder) {
        if (!holder) {
            return {
                fit: 'cover',
                positionX: 50,
                positionY: 50,
                scale: 1,
                offsetX: 0,
                offsetY: 0
            };
        }

        return {
            fit: String(holder.getAttribute('data-vmo-fit') || 'cover'),
            positionX: parseFloat(holder.getAttribute('data-vmo-position-x') || '50') || 50,
            positionY: parseFloat(holder.getAttribute('data-vmo-position-y') || '50') || 50,
            scale: parseFloat(holder.getAttribute('data-vmo-scale') || '1') || 1,
            offsetX: parseFloat(holder.getAttribute('data-vmo-offset-x') || '0') || 0,
            offsetY: parseFloat(holder.getAttribute('data-vmo-offset-y') || '0') || 0
        };
    }

    function vmoPersistPlacementState(holder, state) {
        if (!holder || !holder.setAttribute) return;

        holder.setAttribute('data-vmo-fit', state.fit);
        holder.setAttribute('data-vmo-position-x', String(state.positionX));
        holder.setAttribute('data-vmo-position-y', String(state.positionY));
        holder.setAttribute('data-vmo-scale', String(state.scale));
        holder.setAttribute('data-vmo-offset-x', String(state.offsetX));
        holder.setAttribute('data-vmo-offset-y', String(state.offsetY));
    }

    function vmoResolvePlacementHandle(node) {
        if (!node) return null;

        const tagName = String(node.tagName || '').toLowerCase();

        if (node.dataset && node.dataset.bg === '1') {
            return { mode: 'background', target: node, holder: node, mediaEl: null, container: node };
        }

        if (tagName === 'img' || tagName === 'video') {
            return { mode: 'element', target: node, holder: node, mediaEl: node, container: node.parentElement || node };
        }

        const innerMedia = typeof node.querySelector === 'function'
            ? node.querySelector('[data-vmo-replaced-media="1"], img, video')
            : null;

        if (innerMedia) {
            return { mode: 'element', target: node, holder: node, mediaEl: innerMedia, container: node };
        }

        const backgroundImage = String(node.style && node.style.backgroundImage ? node.style.backgroundImage : '');
        if (backgroundImage && backgroundImage !== 'none') {
            return { mode: 'background', target: node, holder: node, mediaEl: null, container: node };
        }

        return null;
    }

    function vmoApplyPlacementState(handle, state) {
        if (!handle || !state) return;

        const normalizedState = {
            fit: ['cover', 'contain', 'fill'].includes(state.fit) ? state.fit : 'cover',
            positionX: Math.max(0, Math.min(100, parseFloat(state.positionX) || 50)),
            positionY: Math.max(0, Math.min(100, parseFloat(state.positionY) || 50)),
            scale: Math.max(0.5, Math.min(2.5, parseFloat(state.scale) || 1)),
            offsetX: Math.max(-240, Math.min(240, parseFloat(state.offsetX) || 0)),
            offsetY: Math.max(-240, Math.min(240, parseFloat(state.offsetY) || 0))
        };

        vmoPersistPlacementState(handle.holder, normalizedState);

        if (handle.mode === 'background') {
            let backgroundSize = normalizedState.fit === 'contain'
                ? 'contain'
                : (normalizedState.fit === 'fill' ? '100% 100%' : 'cover');

            if (normalizedState.scale !== 1) {
                const scaledPercent = Math.round(normalizedState.scale * 100);
                backgroundSize = normalizedState.fit === 'fill'
                    ? `${scaledPercent}% ${scaledPercent}%`
                    : `${scaledPercent}%`;
            }

            handle.target.style.backgroundSize = backgroundSize;
            handle.target.style.backgroundPosition = `calc(${normalizedState.positionX}% + ${normalizedState.offsetX}px) calc(${normalizedState.positionY}% + ${normalizedState.offsetY}px)`;
            handle.target.style.backgroundRepeat = 'no-repeat';
            handle.target.style.overflow = handle.target.style.overflow || 'hidden';
            return;
        }

        if (!handle.mediaEl) return;

        handle.mediaEl.style.objectFit = normalizedState.fit;
        handle.mediaEl.style.objectPosition = `${normalizedState.positionX}% ${normalizedState.positionY}%`;
        handle.mediaEl.style.transform = vmoBuildPlacementTransform(normalizedState.scale, normalizedState.offsetX, normalizedState.offsetY);
        handle.mediaEl.style.transformOrigin = 'center center';
        handle.mediaEl.style.width = handle.mediaEl.style.width || '100%';
        handle.mediaEl.style.height = handle.mediaEl.style.height || '100%';
        handle.mediaEl.style.display = handle.mediaEl.style.display || 'block';

        if (handle.container && handle.container !== handle.mediaEl) {
            handle.container.style.overflow = handle.container.style.overflow || 'hidden';
            if (!handle.container.style.position || handle.container.style.position === 'static') {
                handle.container.style.position = 'relative';
            }
        }
    }

    function vmoSyncMediaSlotFromPreview(node) {
        if (!node) return;

        const blockWrapper = typeof node.closest === 'function' ? node.closest('[data-block-id]') : null;
        const blockId = blockWrapper ? String(blockWrapper.dataset.blockId || '') : '';

        if (typeof window.vmoSyncPreviewStateFromIframe === 'function') {
            window.vmoSyncPreviewStateFromIframe({
                blockId,
                markUnsaved: true,
                autoSave: true
            });
        }
    }

    function vmoCreateManualReplacementElement(doc, type, url, mediaId, sourceNode) {
        const normalizedType = normalizeReplaceMediaType(type);
        const element = doc.createElement(normalizedType === 'video' ? 'video' : (normalizedType === 'audio' ? 'audio' : 'img'));

        element.setAttribute('data-vmo-replaced-media', '1');
        element.setAttribute('data-media-id', mediaId || '');
        element.setAttribute('data-type', normalizedType);
        element.style.cssText = normalizedType === 'audio'
            ? 'width:100%;display:block;'
            : 'position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 50%;display:block;transform:translate(0px, 0px) scale(1);transform-origin:center center;';

        if (normalizedType === 'video') {
            element.setAttribute('src', url);
            element.setAttribute('autoplay', 'autoplay');
            element.setAttribute('muted', 'muted');
            element.setAttribute('loop', 'loop');
            element.setAttribute('playsinline', 'playsinline');
            element.removeAttribute('controls');
        } else if (normalizedType === 'audio') {
            element.setAttribute('src', url);
            element.setAttribute('controls', 'controls');
        } else {
            element.setAttribute('src', url);
            element.setAttribute('loading', 'lazy');
            element.setAttribute('alt', sourceNode && sourceNode.getAttribute ? (sourceNode.getAttribute('alt') || 'Inserted Image') : 'Inserted Image');
        }

        return element;
    }

    function vmoApplyManualReplacementToDom(doc, node, url, type, mediaId) {
        if (!doc || !node) return node;

        const normalizedType = normalizeReplaceMediaType(type);
        const tagName = String(node.tagName || '').toLowerCase();

        vmoRemoveMediaGenerationMarkersFromNode(node);
        node.setAttribute('data-media-id', mediaId || '');
        node.setAttribute('data-type', normalizedType);
        node.classList.add('vmo-media-placeholder');

        if (node.dataset && node.dataset.bg === '1') {
            node.style.backgroundImage = `url('${url}')`;
            node.style.backgroundSize = node.style.backgroundSize || 'cover';
            node.style.backgroundPosition = node.style.backgroundPosition || 'center';
            node.style.backgroundRepeat = 'no-repeat';
            return node;
        }

        if (normalizedType === 'image' && tagName === 'img') {
            node.setAttribute('src', url);
            node.setAttribute('loading', 'lazy');
            node.setAttribute('data-vmo-replaced-media', '1');
            return node;
        }

        if (normalizedType === 'video' && tagName === 'video') {
            node.setAttribute('src', url);
            node.setAttribute('autoplay', 'autoplay');
            node.setAttribute('muted', 'muted');
            node.setAttribute('loop', 'loop');
            node.setAttribute('playsinline', 'playsinline');
            node.removeAttribute('controls');
            node.setAttribute('data-vmo-replaced-media', '1');
            if (typeof node.load === 'function') {
                node.load();
            }
            return node;
        }

        if (normalizedType === 'audio' && tagName === 'audio') {
            node.setAttribute('src', url);
            node.setAttribute('controls', 'controls');
            node.setAttribute('data-vmo-replaced-media', '1');
            if (typeof node.load === 'function') {
                node.load();
            }
            return node;
        }

        const replacement = vmoCreateManualReplacementElement(doc, normalizedType, url, mediaId, node);

        node.setAttribute('data-vmo-media-container', '1');
        if (!node.style.position || node.style.position === 'static') {
            node.style.position = 'relative';
        }
        node.style.overflow = node.style.overflow || 'hidden';
        node.style.backgroundImage = 'none';

        node.innerHTML = '';
        node.appendChild(replacement);

        return node;
    }

    function vmoApplyManualReplacementToJquery($node, url, type, mediaId) {
        if (!$node || !$node.length) return $node;

        const normalizedType = normalizeReplaceMediaType(type);
        const tagName = String($node.prop('tagName') || '').toLowerCase();

        $node.removeAttr('data-vmo-generate data-vmo-prompt data-vmo-sora data-vmo-sora-prompt data-vmo-generated');
        $node.attr({
            'data-media-id': mediaId || '',
            'data-type': normalizedType
        }).addClass('vmo-media-placeholder');

        if ($node.attr('data-bg') === '1') {
            $node.css({
                backgroundImage: `url('${url}')`,
                backgroundSize: $node.css('background-size') || 'cover',
                backgroundPosition: $node.css('background-position') || 'center',
                backgroundRepeat: 'no-repeat'
            });
            return $node;
        }

        if (normalizedType === 'image' && tagName === 'img') {
            $node.attr({ src: url, loading: 'lazy', 'data-vmo-replaced-media': '1' });
            return $node;
        }

        if (normalizedType === 'video' && tagName === 'video') {
            $node.attr({
                src: url,
                autoplay: 'autoplay',
                muted: 'muted',
                loop: 'loop',
                playsinline: 'playsinline',
                'data-vmo-replaced-media': '1'
            }).removeAttr('controls');
            return $node;
        }

        if (normalizedType === 'audio' && tagName === 'audio') {
            $node.attr({
                src: url,
                controls: 'controls',
                'data-vmo-replaced-media': '1'
            });
            return $node;
        }

        const replacementTag = normalizedType === 'video' ? 'video' : (normalizedType === 'audio' ? 'audio' : 'img');
        const $replacement = $(`<${replacementTag}>`);

        $replacement.attr({
            'data-vmo-replaced-media': '1',
            'data-media-id': mediaId || '',
            'data-type': normalizedType
        }).css({
            position: normalizedType === 'audio' ? 'static' : 'absolute',
            inset: normalizedType === 'audio' ? '' : '0',
            width: '100%',
            height: normalizedType === 'audio' ? '' : '100%',
            objectFit: normalizedType === 'audio' ? '' : 'cover',
            objectPosition: normalizedType === 'audio' ? '' : '50% 50%',
            display: 'block',
            transform: normalizedType === 'audio' ? '' : 'translate(0px, 0px) scale(1)',
            transformOrigin: normalizedType === 'audio' ? '' : 'center center'
        });

        if (normalizedType === 'video') {
            $replacement.attr({
                src: url,
                autoplay: 'autoplay',
                muted: 'muted',
                loop: 'loop',
                playsinline: 'playsinline'
            });
        } else if (normalizedType === 'audio') {
            $replacement.attr({
                src: url,
                controls: 'controls'
            });
        } else {
            $replacement.attr({
                src: url,
                loading: 'lazy',
                alt: $node.attr('alt') || 'Inserted Image'
            });
        }

        $node.attr('data-vmo-media-container', '1').css({
            position: $node.css('position') === 'static' ? 'relative' : $node.css('position'),
            overflow: 'hidden',
            backgroundImage: 'none'
        }).empty().append($replacement);

        return $node;
    }

    window.openMediaPlacementAdjuster = function(context) {
        const ctx = context && typeof context === 'object' ? context : (window.vmoMediaModalContext || {});
        const iframe = document.getElementById('vmo-preview-iframe');
        const doc = iframe && iframe.contentDocument ? iframe.contentDocument : null;

        if (!doc) {
            return showToast('Preview is not ready for media adjustments.', 'error');
        }

        let node = null;
        if (ctx.selector) node = doc.querySelector(ctx.selector);
        if (!node && ctx.mediaId) node = doc.querySelector(`[data-media-id="${ctx.mediaId}"]`);
        if (!node) {
            return showToast('Could not find the selected media slot.', 'error');
        }

        const handle = vmoResolvePlacementHandle(node);
        if (!handle) {
            return showToast('This media slot cannot be adjusted yet.', 'error');
        }

        $('#vmo-placement-modal').remove();

        const state = vmoReadPlacementState(handle.holder);
        $('body').append(`<div id="vmo-placement-modal" class="vmo-modal" style="display:flex;position:fixed;top:0;left:0;width:100vw;height:100vh;background:rgba(0,0,0,0.55);z-index:999999;align-items:center;justify-content:center;backdrop-filter:blur(3px);"><div class="vmo-modal-content" style="background:#001a2e;border-radius:16px;padding:28px;width:92%;max-width:560px;color:#fff;box-shadow:0 30px 80px rgba(0,226,255,0.3);border:1px solid rgba(0,226,255,0.45);max-height:88vh;overflow-y:auto;"><h3 style="margin:0 0 8px;color:#00e2ff;text-align:center;font-size:24px;">Fine-Tune Media Fit</h3><p style="margin:0 0 20px;color:#9fc7d6;text-align:center;font-size:14px;line-height:1.5;">Adjust crop, focus point, zoom, and nudges while keeping the replacement in the exact same slot.</p><div style="display:grid;gap:16px;"><label style="display:grid;gap:8px;font-size:13px;color:#9fc7d6;">Fit<select id="vmo-fit-mode" style="background:#003054;color:#fff;border:1px solid #00e2ff;border-radius:8px;padding:10px 12px;"><option value="cover">Cover</option><option value="contain">Contain</option><option value="fill">Fill</option></select></label><label style="display:grid;gap:8px;font-size:13px;color:#9fc7d6;">Zoom <span id="vmo-fit-scale-label" style="color:#fff;font-weight:600;">100%</span><input id="vmo-fit-scale" type="range" min="50" max="250" step="1" value="100" style="accent-color:#00e2ff;"></label><label style="display:grid;gap:8px;font-size:13px;color:#9fc7d6;">Horizontal Focus <span id="vmo-fit-posx-label" style="color:#fff;font-weight:600;">50%</span><input id="vmo-fit-posx" type="range" min="0" max="100" step="1" value="50" style="accent-color:#00e2ff;"></label><label style="display:grid;gap:8px;font-size:13px;color:#9fc7d6;">Vertical Focus <span id="vmo-fit-posy-label" style="color:#fff;font-weight:600;">50%</span><input id="vmo-fit-posy" type="range" min="0" max="100" step="1" value="50" style="accent-color:#00e2ff;"></label><label style="display:grid;gap:8px;font-size:13px;color:#9fc7d6;">Nudge X <span id="vmo-fit-offsetx-label" style="color:#fff;font-weight:600;">0px</span><input id="vmo-fit-offsetx" type="range" min="-240" max="240" step="1" value="0" style="accent-color:#00e2ff;"></label><label style="display:grid;gap:8px;font-size:13px;color:#9fc7d6;">Nudge Y <span id="vmo-fit-offsety-label" style="color:#fff;font-weight:600;">0px</span><input id="vmo-fit-offsety" type="range" min="-240" max="240" step="1" value="0" style="accent-color:#00e2ff;"></label></div><div style="display:flex;gap:12px;justify-content:flex-end;flex-wrap:wrap;margin-top:22px;"><button id="vmo-fit-reset" class="vmo-btn" style="background:#163447;color:#9fc7d6;border:1px solid rgba(159,199,214,0.35);padding:11px 18px;border-radius:10px;font-weight:600;cursor:pointer;">Reset</button><button id="vmo-fit-done" class="vmo-btn" style="background:#00ff9a;color:#001a2e;border:none;padding:11px 20px;border-radius:10px;font-weight:700;cursor:pointer;">Done</button></div></div></div>`);

        const $modal = $('#vmo-placement-modal');
        const $fitMode = $('#vmo-fit-mode');
        const $scale = $('#vmo-fit-scale');
        const $posX = $('#vmo-fit-posx');
        const $posY = $('#vmo-fit-posy');
        const $offsetX = $('#vmo-fit-offsetx');
        const $offsetY = $('#vmo-fit-offsety');
        let syncTimer = null;

        function closeModal() {
            $modal.remove();
        }

        function scheduleSync() {
            clearTimeout(syncTimer);
            syncTimer = setTimeout(() => vmoSyncMediaSlotFromPreview(handle.target), 120);
        }

        function readStateFromInputs() {
            return {
                fit: $fitMode.val() || 'cover',
                scale: (parseFloat($scale.val()) || 100) / 100,
                positionX: parseFloat($posX.val()) || 50,
                positionY: parseFloat($posY.val()) || 50,
                offsetX: parseFloat($offsetX.val()) || 0,
                offsetY: parseFloat($offsetY.val()) || 0
            };
        }

        function updateLabels(nextState) {
            $('#vmo-fit-scale-label').text(Math.round(nextState.scale * 100) + '%');
            $('#vmo-fit-posx-label').text(Math.round(nextState.positionX) + '%');
            $('#vmo-fit-posy-label').text(Math.round(nextState.positionY) + '%');
            $('#vmo-fit-offsetx-label').text(Math.round(nextState.offsetX) + 'px');
            $('#vmo-fit-offsety-label').text(Math.round(nextState.offsetY) + 'px');
        }

        function applyFromInputs() {
            const nextState = readStateFromInputs();
            vmoApplyPlacementState(handle, nextState);
            updateLabels(nextState);
            scheduleSync();
        }

        $fitMode.val(state.fit);
        $scale.val(Math.round(state.scale * 100));
        $posX.val(Math.round(state.positionX));
        $posY.val(Math.round(state.positionY));
        $offsetX.val(Math.round(state.offsetX));
        $offsetY.val(Math.round(state.offsetY));
        updateLabels(state);
        vmoApplyPlacementState(handle, state);

        $fitMode.on('change', applyFromInputs);
        $scale.on('input', applyFromInputs);
        $posX.on('input', applyFromInputs);
        $posY.on('input', applyFromInputs);
        $offsetX.on('input', applyFromInputs);
        $offsetY.on('input', applyFromInputs);

        $('#vmo-fit-reset').on('click', () => {
            $fitMode.val('cover');
            $scale.val('100');
            $posX.val('50');
            $posY.val('50');
            $offsetX.val('0');
            $offsetY.val('0');
            applyFromInputs();
        });

        $('#vmo-fit-done').on('click', () => {
            clearTimeout(syncTimer);
            vmoSyncMediaSlotFromPreview(handle.target);
            closeModal();
        });

        $modal.on('click', function(e) {
            if (e.target === this) {
                clearTimeout(syncTimer);
                vmoSyncMediaSlotFromPreview(handle.target);
                closeModal();
            }
        });
    };

    /* ========== CORE SWAP HANDLER (iframe + state) ========== */
    window.vmoOnMediaSelected = function (url, options = {}) {
        const ctx = window.vmoMediaModalContext || {};
        let targetBlockId = '';
        if (!ctx.selector && !ctx.mediaId) {
            console.error('[VMO] No media context set for replacement');
            return showToast('No media target found.', 'error');
        }

        const iframe=document.getElementById('vmo-preview-iframe');
        if(iframe?.contentDocument){
            const doc=iframe.contentDocument;
            let node = null;
            if (ctx.selector) node = doc.querySelector(ctx.selector);
            if (!node && ctx.mediaId) node = doc.querySelector(`[data-media-id="${ctx.mediaId}"]`);
            if (!node) {
                console.error('[VMO] Target element not found for', ctx);
                return showToast('Target element not found.', 'error');
            }
            const mediaId=node.dataset.mediaId||vmoGenerateMediaId();
            const blockWrapper = node.closest('[data-block-id]');
            targetBlockId = blockWrapper ? String(blockWrapper.dataset.blockId || '') : '';
            console.log('[VMO] Replacing media', { mediaId, mediaType: ctx.mediaType, url, node });
            node = vmoApplyManualReplacementToDom(doc, node, url, ctx.mediaType, mediaId);
            if (options && options.attachmentId) node.setAttribute('data-attachment-id', String(options.attachmentId));
            if (options && options.job) {
                node.setAttribute('data-vmo-media-job-id', String(options.job.id || ''));
                node.setAttribute('data-vmo-replacement-source', String(options.source || options.job.replacement_type || ''));
                node.setAttribute('data-vmo-slot-id', String(options.job.slot_context?.slot_id || ctx.slot_id || mediaId));
                node.setAttribute('data-vmo-placement-role', String(options.job.slot_context?.placement_role || ctx.placement_role || ''));
                node.setAttribute('data-vmo-quality-status', 'ready');
            }

            const placementHandle = vmoResolvePlacementHandle(node);
            if (placementHandle) {
                vmoApplyPlacementState(placementHandle, vmoReadPlacementState(placementHandle.holder));
            }

            if(typeof window.vmoInjectReplaceButtonsInIframe==='function') window.vmoInjectReplaceButtonsInIframe();
            ctx.mediaId=mediaId;
            ctx.selector = `[data-media-id="${mediaId}"]`;
        }

        let synced = false;
        try {
            if (typeof window.vmoSyncPreviewStateFromIframe === 'function') {
                synced = !!window.vmoSyncPreviewStateFromIframe({
                    blockId: targetBlockId,
                    markUnsaved: true,
                    autoSave: true
                });
            }
        } catch(e) {
            console.warn('[VMO] Preview sync failed', e);
        }

        if (!synced) {
            try {
                vmoReplaceMediaInBlocks(ctx.mediaId,url,ctx.mediaType);
            } catch(e) {
                console.warn('[VMO] Block sync failed', e);
            }
        }

        if (options && options.openAdjuster && typeof window.openMediaPlacementAdjuster === 'function') {
            setTimeout(() => {
                window.openMediaPlacementAdjuster(window.vmoMediaModalContext || {});
            }, 60);
        }

        showToast('Media replaced!','success');
    };

    /* update builder blocks, push undo, autosave */
    function vmoReplaceMediaInBlocks (mid,url,type){
        if(!Array.isArray(window.vmoState?.blocks)) return;
        let changed=false;
        if(typeof pushUndo==='function') pushUndo();
        window.vmoState.blocks.forEach(b=>{
            if(!b.html) return;
            const $tmp=$('<div>'+b.html+'</div>');
            let blockChanged = false;
            $tmp.find('[data-media-id="'+mid+'"]').each(function(){
                vmoApplyManualReplacementToJquery($(this), url, type, mid);
                blockChanged = true;
                changed = true;
            });
            // Always update the block HTML after processing, even if no media was found
            if(blockChanged) {
                b.html = $tmp.html();
            }
        });
        if(changed){
            if(typeof renderBlocks==='function') renderBlocks();
            window.isUnsaved=true;
            window.lastContent=window.vmoState.blocks.map(b=>b.html).join('\n');
            if(typeof autoSaveCheckpoint==='function') autoSaveCheckpoint();
        }
    }

    // (Legacy direct send handler removed; use unified binding at the bottom with handlePromptSend only)

    function isNearBottom(element, threshold = 80) {
        if (!element) return false;
        const distanceFromBottom = element.scrollHeight - element.scrollTop - element.clientHeight;
        return distanceFromBottom <= threshold;
    }

    // Append a message to chat history with role-based styling and conditional auto-scroll
    function appendChatMessage(role, text) {
        try {
            const $log = $('#vmo-chat-history');
            if ($log.length === 0) return;
            const logEl = $log[0];
            const shouldAutoScroll = isNearBottom(logEl);
            
            // ✅ CRITICAL FIX: Prevent duplicate message display
            // Use a Set to track recently added messages
            if (!window._vmoRecentMessages) {
                window._vmoRecentMessages = new Set();
            }
            
            // Create message signature to detect duplicates
            const messageSignature = `${role}:${text}:${Date.now()}`;
            const messageKey = `${role}:${text.substring(0, 50)}`; // Use first 50 chars for comparison
            
            // Check if this exact message was just added (within last 500ms)
            if (window._vmoRecentMessages.has(messageKey)) {
                console.warn('⚠️ Duplicate message prevented:', messageKey);
                return;
            }
            
            // Add to recent messages set
            window._vmoRecentMessages.add(messageKey);
            
            // Remove from set after 500ms to allow legitimate re-sends
            setTimeout(() => {
                window._vmoRecentMessages.delete(messageKey);
            }, 500);
            
            // Clean text - remove markdown symbols, format properly
            let cleanText = (text || '').toString()
                .replace(/###/g, '') // Remove markdown headers
                .replace(/\*\*/g, '') // Remove bold markers
                .replace(/\*/g, '') // Remove italic markers
                .trim();
            
            // Format text properly - preserve intentional line breaks
            cleanText = cleanText
                .split('\n')
                .filter(line => line.trim()) // Remove empty lines
                .map(line => {
                    line = line.trim();
                    // Capitalize first letter if not already (and not an emoji or bullet)
                    if (line.length > 0 && /[a-z]/.test(line[0])) {
                        line = line.charAt(0).toUpperCase() + line.slice(1);
                    }
                    return line;
                })
                .join('\n\n'); // Double line break between paragraphs
            
            // Create message element with role-specific styling
            const isUser = role === 'user';
            const $msg = $('<div/>')
                .addClass('vmo-chat-msg')
                .addClass(isUser ? 'user-msg' : 'ai-msg')
                .css({
                    'margin': '15px 0',
                    'padding': '12px 18px',
                    'border-radius': '12px',
                    'max-width': '85%',
                    'word-wrap': 'break-word',
                    'white-space': 'pre-wrap',
                    'line-height': '1.6',
                    'text-align': 'left',
                    'clear': 'both',
                    'float': isUser ? 'right' : 'left',
                    'background': isUser ? 'linear-gradient(135deg, #003054 0%, #00416A 100%)' : '#f0f0f0',
                    'color': isUser ? '#00e2ff' : '#333333',
                    'border': isUser ? '2px solid #00e2ff' : 'none',
                    'box-shadow': '0 2px 8px rgba(0,0,0,0.1)',
                    'position': 'relative',
                    'animation': 'fadeIn 0.3s ease'
                });
            
            // Add sender label with VMO logo
            const $label = $('<div/>')
                .css({
                    'font-weight': '600',
                    'font-size': '11px',
                    'margin-bottom': '6px',
                    'opacity': '0.85',
                    'text-transform': 'uppercase',
                    'letter-spacing': '0.5px',
                    'display': 'flex',
                    'align-items': 'center',
                    'gap': '6px'
                });
            
            if (!isUser) {
                // Add VMO logo for AI messages
                const pluginMeta = (window.vmoAjax || window.vmo_ajax || {});
                const pluginBase = String(pluginMeta.pluginUrl || pluginMeta.plugin_url || '').replace(/\/+$/, '');
                const logoPath = pluginBase ? `${pluginBase}/assets/vmo-icon.svg` : '';

                if (logoPath) {
                    const $logo = $('<img/>')
                        .attr('src', logoPath)
                        .attr('alt', 'VMO')
                        .css({
                            'width': '16px',
                            'height': '16px',
                            'display': 'inline-block'
                        });
                    $label.append($logo);
                }
            }
            
            $label.append($('<span/>').text(isUser ? 'You' : 'VMO AI'));
            
            // Add message content
            const $content = $('<div/>')
                .css({
                    'font-size': '14px',
                    'line-height': '1.6'
                })
                .text(cleanText);
            
            $msg.append($label).append($content);
            
            // Add clearfix after message
            const $clearfix = $('<div/>').css('clear', 'both');
            
            $log.append($msg).append($clearfix);
            
            // Only follow new messages when user is already at the bottom
            if (shouldAutoScroll) {
                $log.stop(true, false);
                $log.scrollTop(logEl.scrollHeight);
            }
            
        } catch (e) { 
            console.warn('appendChatMessage error', e); 
        }
    }
    
    // ✅ CRITICAL FIX: Expose to window so other modules can use it
    // Voice panel system disabled - no voice manipulation in no-voice version
    window.appendChatMessage = appendChatMessage;
    console.log('✅ appendChatMessage exposed globally');

    // Ensure consultation gating exists (prevents accidental generation before business consultation)
    window.vmoEnsureConsultationReady = function(cb) {
        const unlocked = !!(window.vmo_ajax && window.vmo_ajax.generation_unlocked);
        if (unlocked) {
            if (typeof cb === 'function') cb();
            return true;
        }
        // Guide the user to run the Business Consultation first
        const guidance = 'Let\'s start with your Business Consultation. Click the red Business Wizard button at the top to begin. Jarvis will coordinate the team, and Jerreme will lead the strategy so we can generate your connected website after the consultation.';
        if (window.showToast) window.showToast('Website generation is locked until after consultation.', 'warning');
        appendChatMessage('ai', guidance);
        const wizardBtn = document.getElementById('vmo-business-wizard-btn');
        if (wizardBtn) {
            try { wizardBtn.focus({ preventScroll: false }); } catch (_) { try { wizardBtn.focus(); } catch (_) {} }
        }
        // Voice feedback disabled - text only interface
        console.log('🔕 Voice feedback disabled in no-voice version');
        return false;
    };

    // Conversation-only message to Jarvis-style VMO assistant
    async function sendConversationMessage(text) {
        const userText = (text || '').trim();
        if (!userText) return;
        appendChatMessage('user', userText);
        const chat = [{ role: 'user', content: userText }];
        try {
            const res = await $.post({
                url: vmo_ajax.ajaxurl,
                method: 'POST',
                dataType: 'json',
                data: { action: 'vmo_conversation_chat', security: vmo_ajax.nonce, chat: JSON.stringify(chat), chat_type: 'jarvis' }
            });
            if (res && res.success && res.data?.response) {
                appendChatMessage('ai', res.data.response);
                // Voice response disabled - text only interface  
                console.log('🔕 Voice response disabled in no-voice version');
            } else {
                const msg = res?.data?.message || 'Jarvis couldn\'t complete that just now. Please try again in a moment.';
                if (res?.data?.detail) {
                    console.error('Jarvis request detail:', res.data.detail);
                }
                appendChatMessage('ai', msg);
            }
        } catch (err) {
            console.error('Jarvis request failed:', err);
            appendChatMessage('ai', 'Jarvis couldn\'t complete that just now. Please try again in a moment.');
        }
    }

    // Enhanced content generation function
    function generateContent(userPrompt, isRefinement = false) {
        console.log('[VMO] generateContent called:', { userPrompt, isRefinement });
        // Hard gate: do not generate until after business consultation unlock
        if (!window.vmoEnsureConsultationReady(() => {})) {
            return;
        }
        
        // Validate AJAX variables
        if (typeof vmo_ajax === 'undefined') {
            console.error('[VMO] vmo_ajax not defined');
            if (typeof window.showToast === 'function') {
                window.showToast('AJAX configuration missing. Please refresh the page.', 'error');
            }
            return;
        }
        
        console.log('[VMO] AJAX config:', vmo_ajax);
        
        window.isGenerating = true;
        $('#vmo-send-btn').prop('disabled', true).addClass('vmo-btn-loading');
        
        // Enhanced mobile-responsive prompt
        const mobileResponsivePrompt = userPrompt + `
        
CRITICAL MOBILE REQUIREMENTS:
- Must be fully responsive and mobile-first
- Include proper viewport meta tag: <meta name="viewport" content="width=device-width, initial-scale=1.0">
- Use flexible layouts (flexbox, CSS Grid) that work on all screen sizes
- Minimum 16px font size for mobile readability
- Touch-friendly buttons (minimum 44px touch targets)
- Proper spacing and margins for mobile usability
- Responsive images and media
- Test on phones, tablets, and desktops
- Ensure content fits properly in iframe container
- Use relative units (%, em, rem, vw, vh) instead of fixed pixels where appropriate`;
        
        // Show generating toast
        if (typeof window.showToast === 'function') {
            window.showToast('Designing website and preparing required media…', 'info', true, true);
        }
    // Do NOT append user chat here; handlePromptSend will append to avoid duplicates

        // Build chat messages array
        let finalPrompt = mobileResponsivePrompt;
        
        // Check responsive mode setting
        const responsiveMode = $('#vmo-theme-align').prop('checked');
        
        if (isRefinement && window.vmoState?.blocks?.length) {
            // Build refinement prompt with existing content
            const existingHtml = window.vmoState.blocks.map(b => b.html).join('\n');
            
            // Enhanced refinement instructions for surgical precision
            finalPrompt = `${userPrompt}

--- EXISTING CONTENT ---
${existingHtml}
--- END EXISTING CONTENT ---

REFINEMENT INSTRUCTIONS:
- SURGICAL PRECISION: Only modify the exact element, section, or feature mentioned in the user's request
- PRESERVE ALL OTHER CONTENT: Keep all other sections, elements, and content exactly as they are
- NO SCOPE CREEP: Do not add new sections, headers, footers, or content not explicitly requested
- MAINTAIN STRUCTURE: Preserve existing HTML structure, classes, and functionality
- PROTECT SIBLINGS: When modifying one element, ensure its siblings remain untouched
- ELEMENT ISOLATION: Changes should be confined to the specific target mentioned
- RETURN COMPLETE PAGE: Always return the full page with only the requested changes applied

Based on the instruction above, please modify ONLY the specific element/section mentioned while keeping everything else exactly the same.`;
            
            if (responsiveMode) {
                finalPrompt += '\n\nIMPORTANT: Emphasize mobile-first responsive design and ensure all changes work perfectly on mobile devices.';
            }
        } else {
            // Add smart prompt building for new content
            if (typeof window.buildSmartPrompt === 'function') {
                finalPrompt = window.buildSmartPrompt(userPrompt);
            }
            
            if (responsiveMode) {
                finalPrompt = `RESPONSIVE MODE ENABLED: Create a mobile-first, fully responsive design that looks perfect on all devices. ${finalPrompt}`;
            }
        }

        // Prepare system messages
        let profileSystem = '';
        try { 
            profileSystem = window.vmoConsultationProfileSystem || ''; 
        } catch(e) {
            console.log('[VMO] No consultation profile system available');
        }
        
        const systemMessage = `You are Jerreme, a professional web designer and expert copywriter with surgical precision capabilities. 

${profileSystem}

FOR NEW CONTENT: Create modern, responsive HTML with beautiful CSS, professional copy writing, and advanced design patterns. Include proper section structure with numbered HTML comments and section IDs. Write complete, engaging copy content for all text elements.

FOR REFINEMENTS: Make ONLY the exact changes requested. Preserve all other content, sections, and elements exactly as they are. When modifying one element (like a flip card), ensure all sibling elements remain untouched. Never add unrequested sections or content.

MEDIA HANDLING: Use proper placeholder structure for flip cards and interactive elements. Keep image containers separate from text content areas.

COPY WRITING: Provide complete, professional copy content that businesses can use directly. Match specified word counts and tone requirements exactly.`;

        const chatArr = [
            { 
                role: 'system', 
                content: systemMessage
            },
            { 
                role: 'user', 
                content: finalPrompt
            }
        ];
        
        console.log('[VMO] Prepared chat array:', chatArr);

        // Call backend generation
        const ajaxData = {
            action: 'vmo_generate_content',
            security: vmo_ajax.nonce,
            chat: JSON.stringify(chatArr),
            project_id: window.currentProjectId || 0,
            post_id: window.currentProjectId || 0
        };
        
        console.log('[VMO] Making AJAX call with data:', ajaxData);
        
        $.post({
            url: vmo_ajax.ajaxurl,
            method: 'POST',
            data: ajaxData
        }).done(async function(res) {
            console.log('[VMO] AJAX response received:', res);
            
            if (typeof window.hideToast === 'function') {
                window.hideToast(true);
            }

            if (res.success && (res.data?.grapes_plan || res.data?.html)) {
                const draftId = parseInt(res.data?.draft_id || res.data?.post_id || res.data?.website_draft?.post_id || 0, 10);
                if (draftId > 0) {
                    window.currentProjectId = draftId;
                }
                const draftTitle = res.data?.website_draft?.project_title || res.data?.project_title || '';
                if (draftTitle) {
                    window.currentProjectTitle = draftTitle;
                }

                let generatedHtml = '';
                let compositionError = '';

                if (res.data?.grapes_plan) {
                    if (window.vmoGrapesAdapter && typeof window.vmoGrapesAdapter.composeWebsite === 'function') {
                        try {
                            const extracted = window.vmoConsultation?.consultation_data?.extracted_information || {};
                            const phoneFromStatus = window.vmoConsultation?.phoneProvisioningStatus?.phone_number || '';
                            const phoneFromConsultation = extracted.business_phone || extracted.phone || extracted.supportPhone || '';
                            const emailFromConsultation = extracted.business_email || extracted.businessEmail || extracted.email || extracted.supportEmail || '';

                            const composeContext = {
                                phoneNumber: phoneFromStatus || phoneFromConsultation || '',
                                emailAddress: emailFromConsultation || ''
                            };

                            const composed = window.vmoGrapesAdapter.composeWebsite(res.data.grapes_plan, composeContext);
                            if (composed && composed.ok && composed.html) {
                                generatedHtml = composed.html;
                            } else {
                                compositionError = composed?.reason || 'GrapesJS composition failed';
                            }
                        } catch (composeErr) {
                            compositionError = composeErr?.message || 'GrapesJS composition failed';
                            console.error('[VMO] GrapesJS composition error:', composeErr);
                        }
                    } else {
                        compositionError = 'GrapesJS composer is unavailable';
                    }
                }

                if (!generatedHtml && res.data?.html) {
                    generatedHtml = res.data.html;
                }

                if (!generatedHtml) {
                    const msg = compositionError || 'Unable to compose website frontend with GrapesJS.';
                    if (typeof window.showToast === 'function') {
                        window.showToast(msg, 'error');
                    }
                    appendChatMessage('ai', msg);
                    return;
                }

                // Process the generated HTML
                let cleanHtml = generatedHtml
                    .replace(/<\/?(html|head|body|prompt)[^>]*>/gi, '')
                    .replace(/<!DOCTYPE[^>]*>/gi, '')
                    .trim();

                // Enhance content for mobile responsiveness
                cleanHtml = enhanceContentForMobile(cleanHtml);

                // Wrap with CDNs if function exists
                if (typeof window.vmoWrapWithCDNs === 'function') {
                    cleanHtml = window.vmoWrapWithCDNs(cleanHtml);
                }

                // Push undo state
                if (typeof window.pushUndo === 'function') {
                    window.pushUndo();
                }

                if (isRefinement && window.vmoState?.blocks?.length) {
                    // Replace existing content
                    window.vmoState.blocks = [{
                        id: window.getUniqueBlockId ? window.getUniqueBlockId() : Date.now().toString(),
                        html: cleanHtml,
                        type: 'content'
                    }];
                } else {
                    // Add new content
                    if (!window.vmoState) window.vmoState = { blocks: [] };
                    if (!Array.isArray(window.vmoState.blocks)) window.vmoState.blocks = [];
                    
                    window.vmoState.blocks.push({
                        id: window.getUniqueBlockId ? window.getUniqueBlockId() : Date.now().toString(),
                        html: cleanHtml,
                        type: 'content'
                    });
                }

                // Re-render and save
                if (typeof window.renderBlocks === 'function') {
                    window.renderBlocks();
                } else {
                    console.warn('[VMO] renderBlocks function not available');
                }
                
                // Mark content as present (prevents placeholder from reappearing)
                window.vmoHasContent = true;
                window.isUnsaved = true;
                if (typeof window.autoSaveCheckpoint === 'function') {
                    window.autoSaveCheckpoint();
                }

                let mediaResult = { complete: true, total: 0, completed: 0, failed: [] };
                if (typeof window.vmoMediaGenerator !== 'undefined' && typeof window.vmoMediaGenerator.resolvePendingMedia === 'function') {
                    try {
                        const pendingTargets = typeof window.vmoMediaGenerator.collectPendingMediaTargets === 'function'
                            ? window.vmoMediaGenerator.collectPendingMediaTargets()
                            : [];

                        if (pendingTargets.length && typeof window.vmoSaveProjectSnapshot === 'function' && typeof window.vmoMediaGenerator.queueProjectMedia === 'function') {
                            const savedProject = await window.vmoSaveProjectSnapshot({
                                title: window.currentProjectTitle || 'Auto-saved Website',
                                html: cleanHtml,
                                state: window.vmoState,
                                markSaved: false
                            });

                            mediaResult = await window.vmoMediaGenerator.queueProjectMedia({
                                source: 'generation',
                                targets: pendingTargets,
                                projectId: savedProject && savedProject.projectId ? savedProject.projectId : (window.currentProjectId || 0),
                                html: cleanHtml,
                                state: window.vmoState,
                                title: window.currentProjectTitle || 'Auto-saved Website'
                            });
                        } else {
                            mediaResult = await window.vmoMediaGenerator.resolvePendingMedia({
                                source: 'generation',
                                requireComplete: true,
                                onProgress: function(progress) {
                                    if (typeof window.showToast !== 'function') return;
                                    const progressText = progress.total
                                        ? `Finishing website media ${progress.completed}/${progress.total}: ${progress.message}`
                                        : progress.message;
                                    window.showToast(progressText, progress.failed ? 'error' : 'info', true, true);
                                }
                            });
                        }
                    } catch (mediaError) {
                        console.error('[VMO] Background media queue failed, falling back to foreground completion:', mediaError);
                        try {
                            mediaResult = await window.vmoMediaGenerator.resolvePendingMedia({
                                source: 'generation',
                                requireComplete: true,
                                onProgress: function(progress) {
                                    if (typeof window.showToast !== 'function') return;
                                    const progressText = progress.total
                                        ? `Finishing website media ${progress.completed}/${progress.total}: ${progress.message}`
                                        : progress.message;
                                    window.showToast(progressText, progress.failed ? 'error' : 'info', true, true);
                                }
                            });
                        } catch (fallbackMediaError) {
                            console.error('[VMO] Media completion failed:', fallbackMediaError);
                            mediaResult = {
                                complete: false,
                                total: 0,
                                completed: 0,
                                failed: [{ message: fallbackMediaError?.message || 'Required media generation failed.' }]
                            };
                        }
                    }
                }

                const mediaQueuedInBackground = !!(mediaResult && mediaResult.queued);
                const websiteReady = mediaQueuedInBackground || !mediaResult || mediaResult.complete !== false;
                const builderMessage = mediaQueuedInBackground
                    ? 'Generated new website content and started the required media in the background. You can keep refining or leave the builder while the remaining assets finish.'
                    : (websiteReady
                        ? (isRefinement ? 'Refinement complete. I applied the requested changes.' : 'Generated new website content, finished the required media, and updated the preview.')
                        : 'Website layout drafted, but required media generation is still incomplete. The website is not ready yet.');
                const toastMessage = mediaQueuedInBackground
                    ? 'Website generated. Media is continuing in the background.'
                    : (websiteReady
                        ? (isRefinement ? 'Content refined successfully!' : 'Website generated successfully!')
                        : (mediaResult.failed.length
                            ? mediaResult.failed.map((item) => item.message).slice(0, 2).join(' ')
                            : 'Required media generation did not complete. The website is not ready yet.'));

                if (websiteReady) {
                    $('#vmo-prompt-input').val('');
                }

                if (typeof window.showToast === 'function') {
                    window.showToast(toastMessage, websiteReady ? 'success' : 'error');
                }

                console.log('[VMO] Content generation completed:', { websiteReady, mediaResult });
                appendChatMessage('ai', builderMessage);

                // Voice integration disabled - text only interface
                console.log('� Voice auto-speak disabled in no-voice version');

                // Update balance
                if (vmo_ajax.token_balance) {
                    const tokensUsed = parseInt(res.data?.tokens_used || (isRefinement ? 225 : 2500), 10);
                    if (res.data?.new_balance !== undefined && res.data?.new_balance !== null) {
                        vmo_ajax.token_balance = Math.max(0, parseInt(res.data.new_balance, 10));
                    } else {
                        vmo_ajax.token_balance = Math.max(0, parseInt(vmo_ajax.token_balance, 10) - tokensUsed);
                    }
                    $('#vmo-token-balance').text('Balance: ' + vmo_ajax.token_balance);
                    
                    // Check for upgrade notification (use plan allocation, not hardcoded value)
                    const planAllocation = parseInt(vmo_ajax.plan_tokens || vmo_ajax.token_balance || 72326);
                    const remainingPercentage = (parseInt(vmo_ajax.token_balance) / planAllocation) * 100;
                    if (remainingPercentage <= 30 && remainingPercentage > 0) {
                        setTimeout(() => {
                            if (typeof window.showToast === 'function') {
                                window.showToast('⚡ Low balance. Consider upgrading for more capacity.', 'warning', false, true);
                            }
                        }, 3000);
                    }
                }

            } else {
                console.error('[VMO] Generation failed:', res);
                let errorMessage = 'Failed to generate content.';
                
                // Handle specific error cases
                if (res.data?.code === 'insufficient_tokens') {
                    errorMessage = '⚠️ Insufficient balance. Please upgrade or add an add-on.';
                } else if (res.data?.message) {
                    errorMessage = res.data.message;
                }
                
                if (typeof window.showToast === 'function') {
                    window.showToast(errorMessage, 'error');
                }
                appendChatMessage('ai', errorMessage);
            }
        }).fail(function(jqXHR, textStatus, errorThrown) {
            console.error('[VMO] AJAX Error:', textStatus, errorThrown, jqXHR.responseText);
            if (typeof window.hideToast === 'function') {
                window.hideToast(true);
            }
            if (typeof window.showToast === 'function') {
                let errorMsg = 'Server error. Please try again.';
                if (jqXHR.status === 403) {
                    errorMsg = 'Permission denied. Please refresh and try again.';
                } else if (jqXHR.status === 500) {
                    errorMsg = 'Server error. Please check your service configuration and try again.';
                }
                window.showToast(errorMsg, 'error');
            }
            appendChatMessage('ai', 'There was a problem contacting the server. Please try again.');
        }).always(function() {
            window.isGenerating = false;
            $('#vmo-send-btn').prop('disabled', false).removeClass('vmo-btn-loading');
        });
    }

    /* ========== 🔧 FIX: MISSING CORE FUNCTIONS ========== */
    
    // 🔧 FIX: Main AI communication router
    window.sendToAI = function(prompt, type = 'refinement') {
        if (!prompt?.trim()) {
            window.showToast('Please enter a prompt.', 'error');
            return;
        }

        console.log(`[VMO] Sending ${type} request to AI:`, prompt);

        switch(type) {
            case 'refinement':
                if (window.refineWithUserEdit) {
                    window.refineWithUserEdit(prompt);
                } else {
                    console.error('[VMO] Refinement function not available');
                    window.showToast('Refinement system not loaded.', 'error');
                }
                break;
                
            case 'media':
                window.insertMediaViaAI(prompt);
                break;
                
            case 'generation':
                window.generateContentViaAI(prompt);
                break;
                
            default:
                console.warn(`[VMO] Unknown AI request type: ${type}`);
                window.showToast('Unknown request type.', 'error');
        }
    };

    // 🔧 FIX: Handle prompt sending from UI
    window.handlePromptSend = function() {
        const prompt = $('#vmo-prompt-input').val()?.trim();
        if (!prompt) {
            window.showToast('Please enter a prompt.', 'error');
            return;
        }

        console.log('[VMO] Processing prompt:', prompt);

        // Determine if this is new content generation or refinement
        const hasExistingContent = window.vmoState?.blocks?.length > 0;
        const pendingMedia = Array.isArray(window.vmoPendingMediaSelection) ? window.vmoPendingMediaSelection.slice() : [];

        if (pendingMedia.length && typeof window.vmoApplyMediaInstructionToPreview === 'function') {
            console.log('[VMO] Applying pending media with Jarvis instruction:', pendingMedia);
            appendChatMessage('user', prompt);
            const applied = window.vmoApplyMediaInstructionToPreview(prompt, pendingMedia, { clearPending: true });
            appendChatMessage('ai', applied ? 'Done. I applied the selected media to the live preview.' : 'I could not apply that media instruction. Please select the media again and try a more specific placement.');
            $('#vmo-prompt-input').val('');
            return;
        }
        
        // Check if this is a media-specific request
        if (prompt.toLowerCase().includes('insert') && (prompt.toLowerCase().includes('image') || prompt.toLowerCase().includes('video') || prompt.toLowerCase().includes('media'))) {
            console.log('[VMO] Routing to media insertion');
            appendChatMessage('user', prompt);
            window.insertMediaViaAI(prompt);
            $('#vmo-prompt-input').val('');
            return;
        }
        
        // Identify explicit build/generate intents
        const wantsGeneration = /\b(build|generate|create|make)\b.*\b(site|website|page|homepage|landing)\b/i.test(prompt);
        const wantsRefine = hasExistingContent && /(change|modify|update|refine|edit)/i.test(prompt);

        if (wantsGeneration) {
            // Respect consultation gate
            if (!window.vmoEnsureConsultationReady(() => {})) {
                $('#vmo-prompt-input').val('');
                return;
            }
            console.log('[VMO] User explicitly asked to generate');
            appendChatMessage('user', prompt);
            generateContent(prompt, false);
        } else if (wantsRefine) {
            // Only allow refinement if generation has been unlocked
            if (!window.vmoEnsureConsultationReady(() => {})) {
                $('#vmo-prompt-input').val('');
                return;
            }
            console.log('[VMO] Routing to refinement');
            appendChatMessage('user', prompt);
            window.sendToAI(prompt, 'refinement');
        } else {
            // Default: conversational assistant
            console.log('[VMO] Routing to conversational assistant');
            sendConversationMessage(prompt);
        }
        
        $('#vmo-prompt-input').val(''); // Clear input
    };

    // 🔧 FIX: Media insertion trigger
    window.insertMedia = function(type = 'image', sectionIndex = null) {
        const description = prompt(`Enter description for the ${type}:`);
        if (!description?.trim()) return;

        if (sectionIndex !== null) {
            window.insertMediaIntoSection(sectionIndex, type, description);
        } else {
            // Insert into current/last section
            const lastIndex = (window.vmoState?.blocks?.length || 1) - 1;
            window.insertMediaIntoSection(lastIndex, type, description);
        }
    };

    // 🔧 FIX: Enhanced content generation
    window.generateContentViaAI = function(prompt) {
        console.log('[VMO] Starting AI content generation:', prompt);
        window.showToast('Generating content...', 'info', true, true);

        // Use the main generation function
        generateContent(prompt, false);
    };

    // 🔧 FIX: Media insertion via AI
    window.insertMediaViaAI = function(prompt) {
        const pendingMedia = Array.isArray(window.vmoPendingMediaSelection) ? window.vmoPendingMediaSelection.slice() : [];
        if (pendingMedia.length && typeof window.vmoApplyMediaInstructionToPreview === 'function') {
            window.vmoApplyMediaInstructionToPreview(prompt, pendingMedia, { clearPending: true });
            return;
        }

        // Extract media type and description from prompt
        let mediaType = 'image';
        if (prompt.toLowerCase().includes('video')) mediaType = 'video';
        
        // Use refinement system to add media
        const refinementPrompt = `Add a ${mediaType} with description: "${prompt}". Use [GENERATED_${mediaType.toUpperCase()}: ${prompt}] placeholder.`;
        
        if (window.refineWithUserEdit) {
            window.refineWithUserEdit(refinementPrompt);
        }
    };

    // 🔧 Helper: Call multi-agent router (SEO, website, research, legal, stripe, reports)
    window.callAgent = async function(taskType, userInput, context = {}) {
        const ajaxUrl = (window.vmo_ajax && vmo_ajax.ajaxurl) || (window.vmoAjax && (vmoAjax.ajaxurl || vmoAjax.ajax_url)) || '/wp-admin/admin-ajax.php';
        const nonce = (window.vmo_ajax && vmo_ajax.nonce) || (window.vmoAjax && (vmoAjax.security || vmoAjax.nonce)) || '';

        if (!taskType || !userInput) {
            if (typeof window.showToast === 'function') window.showToast('Missing task type or input for agent.', 'warning');
            return { error: 'Missing task type or input' };
        }

        try {
            const form = new FormData();
            form.append('action', 'vmo_route_agent_task');
            form.append('security', nonce);
            form.append('task_type', taskType);
            form.append('user_input', userInput);
            if (context && Object.keys(context).length) form.append('context', JSON.stringify(context));

            const resp = await fetch(ajaxUrl, { method: 'POST', body: form });
            if (!resp.ok) throw new Error('HTTP ' + resp.status);
            const data = await resp.json();
            if (!data.success) throw new Error(data.data?.message || 'Agent call failed');
            return data.data;
        } catch (e) {
            console.error('[VMO] Agent error:', e);
            if (typeof window.showToast === 'function') window.showToast('Agent error: ' + e.message, 'error');
            return { error: e.message };
        }
    };

    // 🔧 FIX: Bind missing event handlers on document ready
    $doc.ready(function() {
        // Bind prompt send button
        $(document).on('click', '#vmo-send-btn, #vmo-prompt-send', function() {
            window.handlePromptSend();
        });

        // 📷 Media Integration Button - opens AI-powered media modal
        $(document).on('click', '#vmo-media-btn', function(e) {
            e.preventDefault();
            e.stopPropagation();
            
            // Check if openInsertMediaModal is available
            if (typeof window.openInsertMediaModal === 'function') {
                window.openInsertMediaModal('insert', null, 'image');
            } else {
                console.error('[VMO Media] openInsertMediaModal not available');
                if (typeof window.showToast === 'function') {
                    window.showToast('Media integration is loading, please try again.', 'info');
                }
            }
        });

        // Bind enter key in prompt input
        $(document).on('keypress', '#vmo-prompt-input', function(e) {
            if (e.which === 13 && !e.shiftKey) {
                e.preventDefault();
                window.handlePromptSend();
            }
        });

        // Bind media insertion buttons
        $(document).on('click', '[data-action="insert-image"]', function() {
            window.insertMedia('image');
        });

        $(document).on('click', '[data-action="insert-video"]', function() {
            window.insertMedia('video');
        });

        // Bind refinement shortcuts
        $(document).on('click', '[data-action="quick-refine"]', function() {
            const promptTemplate = $(this).data('prompt');
            if (promptTemplate) {
                window.sendToAI(promptTemplate, 'refinement');
            }
        });

        // Section click handler (selection only; never block form controls)
        $(document).on('click', '.vmo-section, [class*="section"]', function(e) {
            // If the user clicked a child control, do nothing (let the control behave normally)
            const $t = $(e.target);
            if ($t.is('input, textarea, select, button, a, label') || $t.closest('input, textarea, select, button, a, label').length) {
                return;
            }
            // Only handle clicks on the section background itself
            if (e.target !== this) return;

            // Clear previous selections
            $('.vmo-selected').removeClass('vmo-selected');

            // Mark clicked section as selected
            $(this).addClass('vmo-selected');
            const sectionIndex = $(this).index();

            console.log('[VMO] Section clicked:', sectionIndex);

            // Notify selection (inspector removed)
            if (window.showToast) window.showToast(`Section ${sectionIndex + 1} selected`, 'info');

            // Store selected section globally
            window.vmoSelectedSection = sectionIndex;
        });

        // Escape key handler removed (inspector removed)

        console.log('[VMO] Main script loaded with fixed communication handlers');

        // Bind Insert Media action button
        $(document).on('click', '#vmo-insert-image-btn', function(e){
            e.preventDefault();
            openInsertMediaModal();
        });

        // Dashboard open must stay silent. Voice, TTS, and consultation greetings
        // start only after an explicit user action such as opening the wizard.
        try {
            if (window.speechSynthesis) window.speechSynthesis.cancel();
            document.querySelectorAll('audio[data-vmo-greeting="true"], #vmo-tts-container audio').forEach(audio => {
                try { audio.pause(); } catch(_) {}
                try { audio.removeAttribute('src'); } catch(_) {}
                try { audio.load(); } catch(_) {}
            });
        } catch (_) {}
    });

    // Enhance content for mobile responsiveness
    function enhanceContentForMobile(html) {
        // Ensure viewport meta tag is present
        if (!html.includes('viewport')) {
            html = html.replace('<head>', '<head>\n<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">');
        }
        
        // Add mobile-responsive CSS if not present
        if (!html.includes('box-sizing: border-box')) {
            const mobileCSS = `
<style>
/* Mobile-First Responsive Framework */
* {
    box-sizing: border-box;
}

html {
    font-size: 16px;
    scroll-behavior: smooth;
}

body {
    margin: 0;
    padding: 0;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
    line-height: 1.6;
    color: #333;
    overflow-x: hidden;
}

/* Container responsive behavior */
.container, .section {
    width: 100%;
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 16px;
}

/* Responsive images and media */
img, video {
    max-width: 100%;
    height: auto;
    display: block;
}

/* Touch-friendly buttons */
button, .btn, a.button {
    min-height: 44px;
    min-width: 44px;
    padding: 12px 24px;
    font-size: 16px;
    border-radius: 8px;
    cursor: pointer;
    touch-action: manipulation;
    text-decoration: none;
    display: inline-block;
}

/* Responsive typography */
h1 { font-size: clamp(24px, 4vw, 48px); }
h2 { font-size: clamp(20px, 3.5vw, 36px); }
h3 { font-size: clamp(18px, 3vw, 28px); }
h4 { font-size: clamp(16px, 2.5vw, 24px); }
p, li { font-size: clamp(14px, 2vw, 18px); }

/* Flex layouts for responsiveness */
.flex {
    display: flex;
    flex-wrap: wrap;
}

.grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 20px;
}

/* Tablet adjustments */
@media (min-width: 768px) {
    .container, .section {
        padding: 0 32px;
    }
}

/* Desktop adjustments */
@media (min-width: 1024px) {
    .container, .section {
        padding: 0 48px;
    }
}

/* Ensure iframe content fits properly */
@media (max-width: 767px) {
    body {
        padding: 0 !important;
        margin: 0 !important;
    }
    
    .section {
        padding-left: 16px !important;
        padding-right: 16px !important;
    }
}
</style>`;

            // Insert mobile CSS after the first <style> tag or before </head>
            if (html.includes('<style>')) {
                html = html.replace('<style>', mobileCSS + '\n<style>');
            } else if (html.includes('</head>')) {
                html = html.replace('</head>', mobileCSS + '\n</head>');
            } else {
                html = mobileCSS + '\n' + html;
            }
        }
        
        return html;
    }

    /* other original code (GPT generation etc.) remains UNTOUCHED BELOW */

})(window.jQuery);
