jQuery(document).ready(function ($) {

    var lastFocusedField = null;
    var linksScanDone = false;

    // Combined SEO score: weighted blend of the three analysis panels. Shown
    // in the metabox header and persisted (#seo_fury_seo_score) so the posts
    // list can render a sortable SEO column without re-running this analysis.
    var sfScores = { basic: 0, readability: 0, extra: 0 };
    function updateOverallScore() {
        var overall = Math.round(sfScores.basic * 0.5 + sfScores.readability * 0.3 + sfScores.extra * 0.2);
        if (overall < 0) { overall = 0; } else if (overall > 100) { overall = 100; }

        var $val = $('#seo-fury-overall-score');
        if ($val.length) { $val.text(overall); }

        var $gauge = $('#seo-fury-overall-gauge');
        if ($gauge.length) {
            $gauge.removeClass('seo-fury-score--good seo-fury-score--ok seo-fury-score--warn');
            $gauge.addClass(overall >= 80 ? 'seo-fury-score--good' : (overall >= 50 ? 'seo-fury-score--ok' : 'seo-fury-score--warn'));
        }

        $('#seo_fury_seo_score').val(overall);
    }

    var debounce = function (fn, delay) {
        var timer;
        return function () {
            var context = this;
            var args = arguments;
            clearTimeout(timer);
            timer = setTimeout(function () {
                fn.apply(context, args);
            }, delay);
        };
    };

    $('.seo-fury-tab').on('click', function () {
        var targetTab = $(this).data('tab');

        $('.seo-fury-tab').removeClass('active');
        $('.seo-fury-tab-content').removeClass('active');

        $(this).addClass('active');
        $('#tab-' + targetTab).addClass('active');

        // Auto-scan links when Links tab is first activated
        if (targetTab === 'links' && !linksScanDone) {
            var $scanBtn = $('#seo-fury-scan-links');
            if ($scanBtn.length && !$scanBtn.prop('disabled')) {
                $scanBtn.trigger('click');
            }
        }
    });

    $(document).on('click', '.seo-fury-edit-snippet', function (e) {
        e.preventDefault();
        e.stopPropagation();

        $('#seo-fury-snippet-modal').show().css('display', 'block');
        $('body').addClass('modal-open').css('overflow', 'hidden');

        var targetTab = $(this).data('modal-tab') || 'basic';
        $('.seo-fury-modal-tab').removeClass('active');
        $('.seo-fury-modal-panel').removeClass('active');
        $('.seo-fury-modal-tab[data-tab="' + targetTab + '"]').addClass('active');
        $('.seo-fury-modal-panel[data-panel="' + targetTab + '"]').addClass('active');

        // Populate permalink field from Gutenberg if the field still holds the server-rendered default
        var $permalinkField = $('#seo_fury_permalink');
        if ($permalinkField.length && typeof wp !== 'undefined' && wp.data && wp.data.select) {
            try {
                var editorStore = wp.data.select('core/editor');
                if (editorStore) {
                    var gutenbergPermalink = editorStore.getPermalink ? editorStore.getPermalink() : '';
                    if (gutenbergPermalink && $permalinkField.val() === (seoFuryData.postPermalink || '')) {
                        $permalinkField.val(gutenbergPermalink);
                        syncModalToHiddenFields();
                    }
                }
            } catch (e) { /* Gutenberg not available */ }
        }

        updateCharCount();
        updateGooglePreview();
        updateSeoAnalysis();
        updateReadabilityAnalysis();
        updateExtraSeoAnalysis();
    });

    $('.seo-fury-modal-close').on('click', function (e) {
        e.preventDefault();
        $('#seo-fury-snippet-modal').hide();
        $('body').removeClass('modal-open').css('overflow', '');
    });

    $('.seo-fury-modal-overlay').on('click', function (e) {
        e.preventDefault();
        $('#seo-fury-snippet-modal').hide();
        $('body').removeClass('modal-open').css('overflow', '');
    });

    /**
     * Sync modal fields → hidden fields inside the metabox (inside <form>).
     * Modal is in admin_footer (outside form), so we need hidden proxies.
     */
    function syncModalToHiddenFields() {
        var map = {
            'seo_fury_title':              'sf_hidden_title',
            'seo_fury_description':        'sf_hidden_description',
            'seo_fury_keywords':           'sf_hidden_keywords',
            'seo_fury_fb_title':           'sf_hidden_fb_title',
            'seo_fury_fb_description':     'sf_hidden_fb_description',
            'seo_fury_fb_image':           'sf_hidden_fb_image',
            'seo_fury_twitter_title':      'sf_hidden_twitter_title',
            'seo_fury_twitter_description':'sf_hidden_twitter_description',
            'seo_fury_twitter_image':      'sf_hidden_twitter_image',
            'seo_fury_twitter_card_type':  'sf_hidden_twitter_card_type',
            'seo_fury_og_title':           'sf_hidden_og_title',
            'seo_fury_og_description':     'sf_hidden_og_description',
            'seo_fury_og_image':           'sf_hidden_og_image',
            'seo_fury_og_type':            'sf_hidden_og_type',
            'seo_fury_permalink':          'sf_hidden_permalink'
        };
        for (var modalId in map) {
            var $modal = $('#' + modalId);
            var $hidden = $('#' + map[modalId]);
            if ($modal.length && $hidden.length) {
                $hidden.val($modal.val());
            }
        }
    }

    // Also sync on any change in modal fields (live sync)
    $(document).on('input change', '#seo_fury_title, #seo_fury_description, #seo_fury_keywords, #seo_fury_fb_title, #seo_fury_fb_description, #seo_fury_fb_image, #seo_fury_twitter_title, #seo_fury_twitter_description, #seo_fury_twitter_image, #seo_fury_twitter_card_type, #seo_fury_og_title, #seo_fury_og_description, #seo_fury_og_image, #seo_fury_og_type, #seo_fury_permalink', function() {
        syncModalToHiddenFields();
    });

    // Duplicate SEO title / meta description detector.
    function checkDuplicateMeta() {
        var $warn = $('#seo-fury-duplicate-warning');
        if (!$warn.length || typeof seoFuryData === 'undefined') { return; }
        var title = $('#seo_fury_title').val() || '';
        var desc = $('#seo_fury_description').val() || '';
        if (!title.trim() && !desc.trim()) { $warn.hide().empty(); return; }

        $.post(seoFuryData.ajaxUrl, {
            action: 'seo_fury_check_duplicate_meta',
            nonce: seoFuryData.nonce,
            post_id: seoFuryData.postId,
            title: title,
            description: desc
        }, function (r) {
            if (!r || !r.success) { $warn.hide().empty(); return; }
            function block(items, label) {
                if (!items || !items.length) { return ''; }
                var links = items.map(function (it) {
                    var t = $('<span>').text(it.title).html();
                    return it.edit ? '<a href="' + it.edit + '" target="_blank" rel="noopener">' + t + '</a>' : t;
                }).join(', ');
                return '<div>⚠️ ' + label + ' ' + links + '</div>';
            }
            var html = block(r.data.title, (seoFuryData.i18n.dupTitle || 'This SEO title is also used by:'))
                     + block(r.data.description, (seoFuryData.i18n.dupDescription || 'This meta description is also used by:'));
            if (html) { $warn.html(html).show(); } else { $warn.hide().empty(); }
        });
    }
    var debouncedDupCheck = debounce(checkDuplicateMeta, 700);
    $(document).on('input change', '#seo_fury_title, #seo_fury_description', debouncedDupCheck);
    checkDuplicateMeta();

    // Sync before form submit (Classic Editor)
    $(document).on('submit', '#post', function() {
        syncModalToHiddenFields();
    });

    // Sync before Gutenberg save
    if (window.wp && wp.data && wp.data.subscribe && wp.data.select) {
        var wasSaving = false;
        wp.data.subscribe(function() {
            var editorStore = wp.data.select('core/editor');
            if (!editorStore || !editorStore.isSavingPost) return;
            var saving = editorStore.isSavingPost();
            if (saving && !wasSaving) {
                syncModalToHiddenFields();
            }
            wasSaving = saving;
        });
    }

    $('.seo-fury-modal-save').on('click', function (e) {
        e.preventDefault();
        syncModalToHiddenFields();
        $('#seo-fury-snippet-modal').hide();
        $('body').removeClass('modal-open').css('overflow', '');

        updateGooglePreview();
        updateSeoAnalysis();
        updateReadabilityAnalysis();
        updateExtraSeoAnalysis();
    });

    $(document).on('keydown', function (e) {
        if (e.key === 'Escape' && $('#seo-fury-snippet-modal').is(':visible')) {
            $('#seo-fury-snippet-modal').hide();
            $('body').removeClass('modal-open').css('overflow', '');
        }
    });

    $('#seo-fury-snippet-modal').on('focus', 'input[type="text"], textarea', function () {
        lastFocusedField = $(this);
        // Active field tracked for variable insertion
    });

    $(document).on('click', '.seo-fury-modal-tab:not([data-editor-tab])', function () {
        var tab = $(this).data('tab');
        var $modal = $(this).closest('.seo-fury-modal');
        $modal.find('.seo-fury-modal-tab').removeClass('active');
        $modal.find('.seo-fury-modal-panel').removeClass('active');
        $(this).addClass('active');
        $modal.find('.seo-fury-modal-panel[data-panel="' + tab + '"]').addClass('active');
    });

    $(document).on('click', '.seo-fury-social-tab', function () {
        var $container = $(this).closest('.seo-fury-section, .seo-fury-modal-panel');
        var tab = $(this).data('social-tab');
        $container.find('.seo-fury-social-tab').removeClass('active');
        $container.find('.seo-fury-social-panel').removeClass('active');
        $(this).addClass('active');
        $container.find('.seo-fury-social-panel[data-social-panel="' + tab + '"]').addClass('active');
    });

    $(document).on('click', '.seo-fury-social-settings-tab', function () {
        var $container = $(this).closest('.seo-fury-social-settings');
        var tab = $(this).data('settings-tab');
        $container.find('.seo-fury-social-settings-tab').removeClass('active');
        $container.find('.seo-fury-social-settings-panel').removeClass('active');
        $(this).addClass('active');
        $container.find('.seo-fury-social-settings-panel[data-settings-panel="' + tab + '"]').addClass('active');
    });

    function syncSocialAllFields($container) {
        var title = $container.find('.seo-fury-social-all-title').val() || '';
        var description = $container.find('.seo-fury-social-all-description').val() || '';
        var image = $container.find('.seo-fury-social-all-image').val() || '';

        if (title) {
            $('#seo_fury_fb_title, #seo_fury_twitter_title').val(title).trigger('input');
            $('#sf_hidden_og_title').val(title);
        }
        if (description) {
            $('#seo_fury_fb_description, #seo_fury_twitter_description').val(description).trigger('input');
            $('#sf_hidden_og_description').val(description);
        }
        if (image) {
            $('#seo_fury_fb_image, #seo_fury_twitter_image').val(image).trigger('input');
            $('#sf_hidden_og_image').val(image);
        }
        // Directly trigger social preview updates for immediate visual feedback
        if (typeof debouncedFacebookPreview === 'function') debouncedFacebookPreview();
        if (typeof debouncedTwitterPreview === 'function') debouncedTwitterPreview();
    }

    $(document).on('input', '.seo-fury-social-all-title, .seo-fury-social-all-description, .seo-fury-social-all-image', function () {
        var $container = $(this).closest('.seo-fury-social-settings');
        if ($container.length) {
            syncSocialAllFields($container);
        }
    });

    function getPostTitle() {
        var postTitle = $('#title').val();
        if (!postTitle && typeof wp !== 'undefined' && wp.data && wp.data.select) {
            try {
                postTitle = wp.data.select('core/editor').getEditedPostAttribute('title');
            } catch (e) {
                postTitle = postTitle || '';
            }
        }
        if (!postTitle && typeof seoFuryData !== 'undefined' && seoFuryData.postTitle) {
            postTitle = seoFuryData.postTitle;
        }
        return postTitle || '';
    }

    function getPostExcerpt() {
        var postExcerpt = '';
        if (typeof wp !== 'undefined' && wp.data && wp.data.select) {
            try {
                postExcerpt = wp.data.select('core/editor').getEditedPostAttribute('excerpt');
            } catch (e) {
                postExcerpt = postExcerpt || '';
            }
        }
        if (!postExcerpt && typeof seoFuryData !== 'undefined' && seoFuryData.postExcerpt) {
            postExcerpt = seoFuryData.postExcerpt;
        }
        return postExcerpt || '';
    }

    function getPostContent() {
        var content = '';
        if (typeof wp !== 'undefined' && wp.data && wp.data.select) {
            try {
                content = wp.data.select('core/editor').getEditedPostContent();
            } catch (e) {
                content = content || '';
            }
        }
        if (!content) {
            content = $('#content').val() || '';
        }
        return content || '';
    }

    function getPostSlug() {
        var slug = '';
        if (typeof wp !== 'undefined' && wp.data && wp.data.select) {
            try {
                slug = wp.data.select('core/editor').getEditedPostAttribute('slug') || '';
            } catch (e) {
                slug = slug || '';
            }
        }
        return slug || '';
    }

    function stripHtml(input) {
        if (!input) return '';
        return input.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
    }

    // ===== KEYWORD VARIATION MATCHING =====

    function generateKeywordVariations(keyword, lang) {
        if (!keyword) return [];
        var variations = [];
        var words = keyword.split(/\s+/);
        var isRu = lang && lang.indexOf('ru') === 0;

        if (isRu) {
            var ruEndings = ['', 'а', 'я', 'ы', 'и', 'у', 'ю', 'е', 'ом', 'ем', 'ой', 'ей', 'ов', 'ев', 'ам', 'ям', 'ами', 'ями', 'ах', 'ях'];
            words.forEach(function(w) {
                if (w.length < 4) return;
                var stem = w;
                // Try stripping common endings to get a stem
                ['ами', 'ями', 'ого', 'ому', 'ыми', 'ими', 'ать', 'ить', 'ять', 'ает', 'ают', 'ует', 'ить', 'ов', 'ев', 'ей', 'ах', 'ях', 'ам', 'ям', 'ом', 'ем', 'ой', 'ые', 'ий', 'ая', 'ое', 'ую', 'ых', 'их', 'ет', 'ут', 'ют', 'ит', 'ы', 'и', 'а', 'я', 'у', 'ю', 'е', 'о'].some(function(e) {
                    if (w.length > e.length + 2 && w.slice(-e.length) === e) {
                        stem = w.slice(0, -e.length);
                        return true;
                    }
                    return false;
                });
                if (stem.length >= 3) {
                    ruEndings.forEach(function(e) {
                        var v = stem + e;
                        if (v !== keyword && v.length >= 3) variations.push(v);
                    });
                }
            });
        } else {
            words.forEach(function(w) {
                if (w.length < 3) return;
                // English: generate plural/singular, verb forms
                if (w.slice(-1) === 's' && w.slice(-2) !== 'ss') variations.push(w.slice(0, -1));
                if (w.slice(-2) === 'es') variations.push(w.slice(0, -2));
                if (w.slice(-3) === 'ies') variations.push(w.slice(0, -3) + 'y');
                if (w.slice(-2) === 'ed') { variations.push(w.slice(0, -2)); variations.push(w.slice(0, -1)); }
                if (w.slice(-3) === 'ing') { variations.push(w.slice(0, -3)); variations.push(w.slice(0, -3) + 'e'); }
                if (w.slice(-2) === 'er') variations.push(w.slice(0, -2));
                if (w.slice(-2) === 'ly') variations.push(w.slice(0, -2));
                // Add common suffixes
                if (w.slice(-1) !== 's') { variations.push(w + 's'); variations.push(w + 'es'); }
                if (w.slice(-3) !== 'ing') { variations.push(w + 'ing'); variations.push(w + 'ed'); }
            });
        }

        // Multi-word permutations (2-3 words)
        if (words.length >= 2 && words.length <= 3) {
            for (var i = 0; i < words.length; i++) {
                for (var j = 0; j < words.length; j++) {
                    if (i !== j) {
                        var perm = words.slice();
                        var tmp = perm[i]; perm[i] = perm[j]; perm[j] = tmp;
                        var p = perm.join(' ');
                        if (p !== keyword) variations.push(p);
                    }
                }
            }
        }

        // Deduplicate
        var unique = {};
        return variations.filter(function(v) {
            if (unique[v] || v === keyword) return false;
            unique[v] = true;
            return true;
        });
    }

    function contentContainsKeywordOrVariation(text, keyword, lang) {
        if (!keyword || !text) return {found: false, exact: false};
        if (text.indexOf(keyword) !== -1) return {found: true, exact: true};
        var variations = generateKeywordVariations(keyword, lang);
        for (var i = 0; i < variations.length; i++) {
            if (text.indexOf(variations[i]) !== -1) return {found: true, exact: false};
        }
        return {found: false, exact: false};
    }

    // ===== SYLLABLE COUNTING & FLESCH SCORE =====

    function countSyllablesEnglish(word) {
        word = word.toLowerCase().replace(/[^a-z]/g, '');
        if (word.length <= 3) return 1;
        word = word.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, '');
        word = word.replace(/^y/, '');
        var m = word.match(/[aeiouy]{1,2}/g);
        return m ? m.length : 1;
    }

    function countSyllablesRussian(word) {
        var m = word.toLowerCase().match(/[аеёиоуыэюя]/g);
        return m ? m.length : 1;
    }

    function calculateFleschScore(sentences, words, text, lang) {
        if (words.length < 50) return -1; // too short
        var isRu = lang && lang.indexOf('ru') === 0;
        var totalSyllables = 0;
        words.forEach(function(w) {
            totalSyllables += isRu ? countSyllablesRussian(w) : countSyllablesEnglish(w);
        });
        var asl = words.length / Math.max(sentences.length, 1);
        var asw = totalSyllables / Math.max(words.length, 1);
        var score;
        if (isRu) {
            score = 206.835 - (1.3 * asl) - (60.1 * asw);
        } else {
            score = 206.835 - (1.015 * asl) - (84.6 * asw);
        }
        return Math.max(0, Math.min(100, Math.round(score)));
    }

    function getFleschLabel(score) {
        var i18n = seoFuryData.i18n || {};
        if (score >= 80) return i18n.fleschVeryEasy || 'Very Easy';
        if (score >= 60) return i18n.fleschEasy || 'Easy';
        if (score >= 40) return i18n.fleschModerate || 'Moderate';
        if (score >= 20) return i18n.fleschDifficult || 'Difficult';
        return i18n.fleschVeryDifficult || 'Very Difficult';
    }

    // ===== PASSIVE VOICE DETECTION =====

    var EN_PASSIVE_RE = /\b(was|were|is|are|am|been|being|be|get|got|gets)\s+([\w]+ed|written|taken|given|made|done|seen|shown|known|found|built|sent|told|sold|held|kept|left|lost|met|paid|put|read|run|said|set|thought|understood|won|worn|broken|chosen|driven|eaten|fallen|forgotten|frozen|hidden|ridden|risen|spoken|stolen|sworn|thrown|woken)\b/i;
    // Passive in Russian = auxiliary (был/будет…) + short passive participle.
    // The bare "-ся/-сь" reflexive ending was removed: it matched every middle/
    // reflexive verb (улыбается, находится…), almost none of which are passive,
    // hugely over-counting and training users to ignore the warning.
    var RU_PASSIVE_RE = /\b(был[аио]?|будет|будут|является|являются)\s+\S*(н[аоы]|т[аоы]|ен|ён)\b/i;

    function detectPassiveVoice(sentences, lang) {
        var isRu = lang && lang.indexOf('ru') === 0;
        var re = isRu ? RU_PASSIVE_RE : EN_PASSIVE_RE;
        var passiveCount = 0;
        sentences.forEach(function(s) {
            if (re.test(s)) passiveCount++;
        });
        var pct = sentences.length > 0 ? Math.round((passiveCount / sentences.length) * 100) : 0;
        return {passiveCount: passiveCount, total: sentences.length, percentage: pct};
    }

    // ===== TRANSITION WORDS =====

    var TRANSITION_WORDS_EN = ['however', 'therefore', 'furthermore', 'moreover', 'consequently', 'nevertheless', 'meanwhile', 'additionally', 'similarly', 'likewise', 'in addition', 'as a result', 'on the other hand', 'in contrast', 'for example', 'for instance', 'in conclusion', 'in summary', 'first', 'second', 'third', 'finally', 'next', 'then', 'also', 'besides', 'hence', 'thus', 'accordingly', 'instead', 'otherwise', 'still', 'yet', 'although', 'whereas'];
    var TRANSITION_WORDS_RU = ['однако', 'следовательно', 'кроме того', 'более того', 'тем не менее', 'между тем', 'также', 'таким образом', 'например', 'в результате', 'с другой стороны', 'в заключение', 'во-первых', 'во-вторых', 'в-третьих', 'наконец', 'далее', 'затем', 'помимо этого', 'вместе с тем', 'несмотря на это', 'впрочем', 'итак', 'прежде всего', 'к тому же', 'в частности', 'иначе говоря', 'тогда как', 'поэтому', 'вследствие этого'];

    var _transitionRe = {};
    function getTransitionRegex(isRu) {
        var key = isRu ? 'ru' : 'en';
        if (Object.prototype.hasOwnProperty.call(_transitionRe, key)) {
            return _transitionRe[key];
        }
        var words = isRu ? TRANSITION_WORDS_RU : TRANSITION_WORDS_EN;
        var escaped = words.map(function (w) { return w.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); });
        var re = null;
        try {
            // Whole-phrase match on word boundaries so "then" doesn't match
            // inside "strengthen". Unicode look-around covers Cyrillic too.
            re = new RegExp('(?<![\\p{L}\\p{N}])(?:' + escaped.join('|') + ')(?![\\p{L}\\p{N}])', 'iu');
        } catch (e) {
            re = null; // older browsers: fall back to substring matching
        }
        _transitionRe[key] = re;
        return re;
    }

    function checkTransitionWords(sentences, lang) {
        var isRu = lang && lang.indexOf('ru') === 0;
        var words = isRu ? TRANSITION_WORDS_RU : TRANSITION_WORDS_EN;
        var re = getTransitionRegex(isRu);
        var count = 0;
        sentences.forEach(function(s) {
            var lower = s.toLowerCase();
            if (re) {
                if (re.test(lower)) { count++; }
            } else {
                for (var i = 0; i < words.length; i++) {
                    if (lower.indexOf(words[i]) !== -1) { count++; break; }
                }
            }
        });
        var pct = sentences.length > 0 ? Math.round((count / sentences.length) * 100) : 0;
        return {count: count, total: sentences.length, percentage: pct};
    }

    function getContentMetrics() {
        var html = getPostContent();
        var parser = new DOMParser();
        var doc = parser.parseFromString(html, 'text/html');
        var text = doc.body ? doc.body.textContent || '' : '';
        var words = text.trim().split(/\s+/).filter(Boolean);
        var sentences = text.split(/[.!?]+/).map(function (s) { return s.trim(); }).filter(Boolean);
        var paragraphs = [];
        if (doc.body) {
            doc.body.querySelectorAll('p, li, blockquote').forEach(function (node) {
                var pText = (node.textContent || '').trim();
                if (pText) {
                    paragraphs.push(pText);
                }
            });
        }
        if (paragraphs.length === 0 && text.trim()) {
            paragraphs = text.split(/\n+/).map(function (p) { return p.trim(); }).filter(Boolean);
        }
        var images = doc.querySelectorAll('img');
        var imagesWithAlt = 0;
        images.forEach(function (img) {
            var alt = (img.getAttribute('alt') || '').trim();
            if (alt.length > 0) {
                imagesWithAlt += 1;
            }
        });
        var links = doc.querySelectorAll('a[href]');
        var internalLinks = 0;
        var externalLinks = 0;
        var host = window.location.host;
        links.forEach(function (link) {
            var href = link.getAttribute('href') || '';
            if (!href) return;
            if (href.indexOf('#') === 0 || href.indexOf('mailto:') === 0 || href.indexOf('tel:') === 0) {
                return;
            }
            if (href.indexOf('/') === 0 || href.indexOf(host) !== -1) {
                internalLinks += 1;
            } else if (href.indexOf('http://') === 0 || href.indexOf('https://') === 0) {
                externalLinks += 1;
            }
        });

        var h1Count = doc.querySelectorAll('h1').length;
        var h2Count = doc.querySelectorAll('h2').length;
        var listsCount = doc.querySelectorAll('ul, ol').length;

        return {
            wordCount: words.length,
            textLower: text.toLowerCase(),
            sentenceCount: sentences.length,
            sentences: sentences,
            paragraphs: paragraphs,
            images: images.length,
            imagesWithAlt: imagesWithAlt,
            internalLinks: internalLinks,
            externalLinks: externalLinks,
            h1Count: h1Count,
            h2Count: h2Count,
            listsCount: listsCount
        };
    }

    function updateSeoAnalysis() {
        var focusKeyword = ($('#seo_fury_focus_keyword').val() || '').trim();
        var focusLower = focusKeyword.toLowerCase();
        var title = $('#seo_fury_title').val() ? parseVariables($('#seo_fury_title').val()) : (getPostTitle() || '');
        var description = $('#seo_fury_description').val() ? parseVariables($('#seo_fury_description').val()) : (getPostExcerpt() || '');
        var metrics = getContentMetrics();
        var content = metrics.textLower;
        var slug = getPostSlug().toLowerCase();
        var titleLength = title.length;
        var descriptionLength = description.length;
        var minWordCount = 300;
        var lang = (typeof seoFuryData !== 'undefined' && seoFuryData.language) ? seoFuryData.language : 'en_US';
        var kwSlug = focusLower.length > 0 ? contentContainsKeywordOrVariation(slug.replace(/-/g, ' '), focusLower, lang) : {found: false, exact: false};
        var kwContent = focusLower.length > 0 ? contentContainsKeywordOrVariation(content, focusLower, lang) : {found: false, exact: false};
        var kwTitle = focusLower.length > 0 ? contentContainsKeywordOrVariation(title.toLowerCase(), focusLower, lang) : {found: false, exact: false};
        var kwDesc = focusLower.length > 0 ? contentContainsKeywordOrVariation(description.toLowerCase(), focusLower, lang) : {found: false, exact: false};
        var keywordInSlug = kwSlug.found;
        var keywordInContent = kwContent.found;
        var keywordInTitle = kwTitle.found;
        var keywordInDescription = kwDesc.found;
        var words = metrics.wordCount;
        var keywordCount = 0;
        if (focusLower.length > 0 && words > 0) {
            var escapedKw = focusLower.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
            // Count whole-word occurrences (so "art" doesn't match "start").
            // Falls back to a substring match if Unicode look-behind isn't
            // supported (older Safari).
            var pattern;
            try {
                pattern = new RegExp('(?<![\\p{L}\\p{N}])' + escapedKw + '(?![\\p{L}\\p{N}])', 'giu');
            } catch (e) {
                pattern = new RegExp(escapedKw, 'gi');
            }
            keywordCount = (content.match(pattern) || []).length;
        }
        var keywordDensity = words > 0 ? (keywordCount / words) * 100 : 0;
        var keywordDensityOk = focusLower.length > 0 && keywordDensity >= 0.5 && keywordDensity <= 2.5;
        // The post title is rendered as the page H1 by virtually all themes,
        // so an in-content H1 or a non-empty post title satisfies this check.
        var hasH1 = metrics.h1Count > 0 || getPostTitle().length > 0;
        var hasH2 = metrics.h2Count > 0;
        var imagesOk = metrics.images === 0 || (metrics.imagesWithAlt / metrics.images) >= 0.8;
        var hasInternalLink = metrics.internalLinks > 0;

        var checks = [
            {
                ok: focusLower.length > 0,
                textOk: seoFuryData.i18n.focusKeywordSet,
                textWarn: seoFuryData.i18n.addFocusKeyword
            },
            {
                ok: keywordInTitle,
                partial: kwTitle.found && !kwTitle.exact,
                textOk: kwTitle.exact ? seoFuryData.i18n.keywordInTitle : seoFuryData.i18n.keywordInTitlePartial,
                textWarn: seoFuryData.i18n.addKeywordToTitle
            },
            {
                ok: keywordInDescription,
                partial: kwDesc.found && !kwDesc.exact,
                textOk: kwDesc.exact ? seoFuryData.i18n.keywordInDescription : seoFuryData.i18n.keywordInDescriptionPartial,
                textWarn: seoFuryData.i18n.addKeywordToDescription
            },
            {
                ok: keywordInContent,
                partial: kwContent.found && !kwContent.exact,
                textOk: kwContent.exact ? seoFuryData.i18n.keywordInContent : seoFuryData.i18n.keywordInContentPartial,
                textWarn: seoFuryData.i18n.addKeywordToContent
            },
            {
                ok: keywordInSlug,
                partial: kwSlug.found && !kwSlug.exact,
                textOk: kwSlug.exact ? seoFuryData.i18n.keywordInUrl : seoFuryData.i18n.keywordInUrlPartial,
                textWarn: seoFuryData.i18n.addKeywordToUrl
            },
            {
                ok: keywordDensityOk,
                textOk: seoFuryData.i18n.keywordDensityOk,
                textWarn: seoFuryData.i18n.keywordDensityBad
            },
            {
                ok: titleLength >= 50 && titleLength <= 60,
                textOk: seoFuryData.i18n.titleLengthOk,
                textWarn: seoFuryData.i18n.titleLengthBad
            },
            {
                ok: descriptionLength >= 120 && descriptionLength <= 160,
                textOk: seoFuryData.i18n.descLengthOk,
                textWarn: seoFuryData.i18n.descLengthBad
            },
            {
                ok: words >= minWordCount,
                textOk: seoFuryData.i18n.contentLengthOk,
                textWarn: seoFuryData.i18n.contentLengthBad
            },
            {
                ok: hasH1,
                textOk: seoFuryData.i18n.h1Present,
                textWarn: seoFuryData.i18n.addH1
            },
            {
                ok: hasH2,
                textOk: seoFuryData.i18n.h2Present,
                textWarn: seoFuryData.i18n.addH2
            },
            {
                ok: imagesOk,
                textOk: seoFuryData.i18n.imagesAltOk,
                textWarn: seoFuryData.i18n.addImageAlt
            },
            {
                ok: hasInternalLink,
                textOk: seoFuryData.i18n.internalLinkPresent,
                textWarn: seoFuryData.i18n.addInternalLink
            }
        ];

        var $list = $('#seo-fury-analysis-list');
        if (!$list.length) {
            return;
        }

        $list.empty();
        var passed = 0;
        checks.forEach(function (check) {
            var statusClass = check.ok ? (check.partial ? 'is-partial' : 'is-ok') : 'is-warn';
            if (check.ok) passed += (check.partial ? 0.5 : 1);
            $list.append('<li class="seo-fury-analysis-item ' + statusClass + '">' + (check.ok ? check.textOk : check.textWarn) + '</li>');
        });

        var score = Math.round((passed / checks.length) * 100);
        var $score = $('#seo-fury-score');
        $score.text(score + '/100');
        $score.removeClass('seo-fury-score--good seo-fury-score--ok seo-fury-score--warn');
        if (score >= 80) {
            $score.addClass('seo-fury-score--good');
        } else if (score >= 50) {
            $score.addClass('seo-fury-score--ok');
        } else {
            $score.addClass('seo-fury-score--warn');
        }
        sfScores.basic = score;
        updateOverallScore();

        var densityText = focusLower.length > 0 ? keywordDensity.toFixed(2) + '%' : '—';
        $('#seo-fury-keyword-density').text(densityText);
        var densityBar = $('#seo-fury-keyword-density-bar');
        var densityPercent = Math.min(100, Math.round((keywordDensity / 3) * 100));
        densityBar.css('width', densityPercent + '%');
        densityBar.removeClass('is-good is-ok is-warn');
        if (keywordDensityOk) {
            densityBar.addClass('is-good');
        } else if (keywordDensity > 0) {
            densityBar.addClass('is-warn');
        } else {
            densityBar.addClass('is-ok');
        }
    }

    function updateReadabilityAnalysis() {
        var metrics = getContentMetrics();
        var sentences = metrics.sentences;
        var paragraphs = metrics.paragraphs;
        var lang = (typeof seoFuryData !== 'undefined' && seoFuryData.language) ? seoFuryData.language : 'en_US';

        var sentenceLengths = sentences.map(function (s) {
            return s.split(/\s+/).filter(Boolean).length;
        });
        var avgSentenceLength = sentenceLengths.length ? Math.round(sentenceLengths.reduce(function (a, b) { return a + b; }, 0) / sentenceLengths.length) : 0;
        var maxSentenceLength = sentenceLengths.length ? Math.max.apply(null, sentenceLengths) : 0;
        var paragraphLengths = paragraphs.map(function (p) {
            return p.split(/\s+/).filter(Boolean).length;
        });
        var maxParagraphLength = paragraphLengths.length ? Math.max.apply(null, paragraphLengths) : 0;

        // Passive voice, transition words, Flesch
        var passive = detectPassiveVoice(sentences, lang);
        var transitions = checkTransitionWords(sentences, lang);
        var allWords = metrics.textLower ? metrics.textLower.split(/\s+/).filter(Boolean) : [];
        var fleschScore = calculateFleschScore(sentences, allWords, metrics.textLower || '', lang);
        var fleschLabel = fleschScore >= 0 ? getFleschLabel(fleschScore) : '';

        var checks = [
            {
                ok: sentences.length >= 3,
                textOk: seoFuryData.i18n.enoughSentences,
                textWarn: seoFuryData.i18n.addMoreSentences
            },
            {
                ok: avgSentenceLength > 0 && avgSentenceLength <= 20,
                textOk: seoFuryData.i18n.avgSentenceLengthOk,
                textWarn: seoFuryData.i18n.makeSentencesShorter
            },
            {
                ok: maxSentenceLength === 0 || maxSentenceLength <= 30,
                textOk: seoFuryData.i18n.noLongSentences,
                textWarn: seoFuryData.i18n.longSentences
            },
            {
                ok: paragraphs.length >= 2,
                textOk: seoFuryData.i18n.multipleParagraphs,
                textWarn: seoFuryData.i18n.splitParagraphs
            },
            {
                ok: maxParagraphLength === 0 || maxParagraphLength <= 150,
                textOk: seoFuryData.i18n.paragraphLengthOk,
                textWarn: seoFuryData.i18n.makeParagraphsShorter
            },
            {
                ok: metrics.listsCount > 0,
                textOk: seoFuryData.i18n.listsPresent,
                textWarn: seoFuryData.i18n.addLists
            },
            {
                ok: passive.percentage <= 15,
                textOk: seoFuryData.i18n.passiveVoiceOk,
                textWarn: (seoFuryData.i18n.tooMuchPassiveVoice || '').replace('%d', passive.percentage)
            },
            {
                ok: transitions.percentage >= 30,
                textOk: (seoFuryData.i18n.transitionWordsOk || '').replace('%d', transitions.percentage),
                textWarn: (seoFuryData.i18n.addTransitionWords || '').replace('%d', transitions.percentage)
            }
        ];

        // Add Flesch check only if enough content
        if (fleschScore >= 0) {
            checks.push({
                ok: fleschScore >= 40,
                textOk: (seoFuryData.i18n.fleschScoreOk || 'Flesch score is good') + ' (' + fleschScore + ' — ' + fleschLabel + ')',
                textWarn: (seoFuryData.i18n.fleschScoreBad || 'Content may be difficult') + ' (' + fleschScore + ' — ' + fleschLabel + ')'
            });
        }

        var $list = $('#seo-fury-readability-list');
        if (!$list.length) {
            return;
        }

        $list.empty();
        var passed = 0;
        checks.forEach(function (check) {
            var statusClass = check.ok ? 'is-ok' : 'is-warn';
            if (check.ok) passed += 1;
            $list.append('<li class="seo-fury-analysis-item ' + statusClass + '">' + (check.ok ? check.textOk : check.textWarn) + '</li>');
        });

        var score = Math.round((passed / checks.length) * 100);
        var $score = $('#seo-fury-readability-score');
        if ($score.length) {
            $score.text(score + '/100');
            $score.removeClass('seo-fury-score--good seo-fury-score--ok seo-fury-score--warn');
            if (score >= 80) {
                $score.addClass('seo-fury-score--good');
            } else if (score >= 50) {
                $score.addClass('seo-fury-score--ok');
            } else {
                $score.addClass('seo-fury-score--warn');
            }
        }
        sfScores.readability = score;
        updateOverallScore();

        var readabilityText = score >= 80 ? seoFuryData.i18n.excellent : (score >= 50 ? seoFuryData.i18n.okay : seoFuryData.i18n.needsWork);
        $('#seo-fury-readability-text').text(readabilityText);
        var readabilityBar = $('#seo-fury-readability-bar');
        readabilityBar.css('width', score + '%');
        readabilityBar.removeClass('is-good is-ok is-warn');
        if (score >= 80) {
            readabilityBar.addClass('is-good');
        } else if (score >= 50) {
            readabilityBar.addClass('is-ok');
        } else {
            readabilityBar.addClass('is-warn');
        }

        // Update Flesch score display
        var $flesch = $('#seo-fury-flesch-score');
        if ($flesch.length && fleschScore >= 0) {
            $flesch.text(fleschScore + ' — ' + fleschLabel);
        }
    }

    function updateExtraSeoAnalysis() {
        var metrics = getContentMetrics();
        var focusKeyword = ($('#seo_fury_focus_keyword').val() || '').trim().toLowerCase();
        var slug = getPostSlug();
        var firstParagraph = metrics.paragraphs.length ? metrics.paragraphs[0].toLowerCase() : '';

        var lang = (typeof seoFuryData !== 'undefined' && seoFuryData.language) ? seoFuryData.language : 'en_US';
        var kwFirstP = focusKeyword.length > 0 ? contentContainsKeywordOrVariation(firstParagraph, focusKeyword, lang) : {found: false, exact: false};
        var keywordInFirstParagraph = kwFirstP.found;
        var urlLengthOk = slug.length > 0 && slug.length <= 75;
        var hasExternalLink = metrics.externalLinks > 0;
        var hasInternalLink = metrics.internalLinks > 0;
        var hasImage = metrics.images > 0;

        var checks = [
            {
                ok: keywordInFirstParagraph,
                partial: kwFirstP.found && !kwFirstP.exact,
                textOk: kwFirstP.exact ? seoFuryData.i18n.keywordInFirstParagraph : seoFuryData.i18n.keywordInFirstParagraphPartial,
                textWarn: seoFuryData.i18n.addKeywordToFirstParagraph
            },
            {
                ok: urlLengthOk,
                textOk: seoFuryData.i18n.urlLengthOk,
                textWarn: seoFuryData.i18n.shortenUrl
            },
            {
                ok: hasInternalLink,
                textOk: seoFuryData.i18n.internalLinkPresent,
                textWarn: seoFuryData.i18n.addInternalLink
            },
            {
                ok: hasExternalLink,
                textOk: seoFuryData.i18n.externalLinkPresent,
                textWarn: seoFuryData.i18n.addExternalLink
            },
            {
                ok: hasImage,
                textOk: seoFuryData.i18n.imagesInContent,
                textWarn: seoFuryData.i18n.addImage
            }
        ];

        var $list = $('#seo-fury-extra-list');
        if (!$list.length) {
            return;
        }

        $list.empty();
        var passed = 0;
        checks.forEach(function (check) {
            var statusClass = check.ok ? (check.partial ? 'is-partial' : 'is-ok') : 'is-warn';
            if (check.ok) passed += (check.partial ? 0.5 : 1);
            $list.append('<li class="seo-fury-analysis-item ' + statusClass + '">' + (check.ok ? check.textOk : check.textWarn) + '</li>');
        });

        var score = Math.round((passed / checks.length) * 100);
        var $score = $('#seo-fury-extra-score');
        if ($score.length) {
            $score.text(score + '/100');
            $score.removeClass('seo-fury-score--good seo-fury-score--ok seo-fury-score--warn');
            if (score >= 80) {
                $score.addClass('seo-fury-score--good');
            } else if (score >= 50) {
                $score.addClass('seo-fury-score--ok');
            } else {
                $score.addClass('seo-fury-score--warn');
            }
        }
        sfScores.extra = score;
        updateOverallScore();
    }

    function parseVariables(text) {
        if (!text) return text;

        var postTitle = getPostTitle();
        if (!postTitle) {
            postTitle = 'Page Title';
        }

        var postExcerpt = getPostExcerpt() || 'Page description';

        var siteName = typeof seoFuryData !== 'undefined' ? seoFuryData.siteName : 'Site name';
        var separator = typeof seoFuryData !== 'undefined' ? seoFuryData.separator : '—';

        var fmt = function (d) {
            return String(d.getDate()).padStart(2, '0') + '.' + String(d.getMonth() + 1).padStart(2, '0') + '.' + d.getFullYear();
        };

        // %date%/%year%/%month%/%day% resolve to the post's PUBLISH date on the
        // server, so the preview must too (fall back to now for an unsaved post).
        var pub = (typeof seoFuryData !== 'undefined' && seoFuryData.postDate) ? new Date(seoFuryData.postDate) : new Date();
        if (isNaN(pub.getTime())) { pub = new Date(); }
        var year = pub.getFullYear();
        var month = String(pub.getMonth() + 1).padStart(2, '0');
        var day = String(pub.getDate()).padStart(2, '0');
        var date = fmt(pub);

        // %currentdate%/%currentyear% are the actual current date on the server.
        var now = new Date();
        var currentYear = now.getFullYear();
        var currentDate = fmt(now);

        var author = (typeof seoFuryData !== 'undefined' && seoFuryData.postAuthor) ? seoFuryData.postAuthor : '';
        var modified = (typeof seoFuryData !== 'undefined' && seoFuryData.postModified) ? seoFuryData.postModified : date;

        text = text.replace(/%title%/g, postTitle);
        text = text.replace(/%seo_title%/g, postTitle);
        text = text.replace(/%excerpt%/g, postExcerpt);
        text = text.replace(/%seo_description%/g, postExcerpt);
        text = text.replace(/%sitename%/g, siteName);
        text = text.replace(/%sep%/g, separator);
        text = text.replace(/%separator%/g, separator);
        text = text.replace(/%date%/g, date);
        text = text.replace(/%modified%/g, modified);
        text = text.replace(/%year%/g, year);
        text = text.replace(/%currentyear%/g, currentYear);
        text = text.replace(/%month%/g, month);
        text = text.replace(/%day%/g, day);
        text = text.replace(/%currentdate%/g, currentDate);
        text = text.replace(/%name%/g, author);
        text = text.replace(/%post_author%/g, author);

        return text;
    }

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

    function escapeRegExp(text) {
        return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    }

    function highlightKeyword(text, keyword) {
        if (!keyword) {
            return escapeHtml(text);
        }
        var safeText = escapeHtml(text);
        var pattern = new RegExp(escapeRegExp(keyword), 'gi');
        return safeText.replace(pattern, function (match) {
            return '<mark class="seo-fury-focus-highlight">' + match + '</mark>';
        });
    }

    var textMeasureCanvas = document.createElement('canvas');
    var textMeasureContext = textMeasureCanvas.getContext('2d');
    function getTextWidth(text, font) {
        textMeasureContext.font = font;
        return Math.ceil(textMeasureContext.measureText(text).width);
    }

    function updateCharCount() {
        var titleVal = $('#seo_fury_title').val() || '';
        var descVal = $('#seo_fury_description').val() || '';
        var permalinkVal = $('#seo_fury_permalink').val() || '';

        var titleLength = parseVariables(titleVal).length;
        var descLength = parseVariables(descVal).length;
        var permalinkLength = permalinkVal.length;

        var titlePixels = getTextWidth(parseVariables(titleVal), '16px Arial');
        var descPixels = getTextWidth(parseVariables(descVal), '13px Arial');

        $('#title-length').text(titleLength);
        $('#description-length').text(descLength);
        $('#title-pixels').text(titlePixels);
        $('#description-pixels').text(descPixels);
        $('#permalink-length').text(permalinkLength);

        if (titleLength > 60) {
            $('#title-length').css('color', '#ef4444');
        } else if (titleLength > 50) {
            $('#title-length').css('color', '#f59e0b');
        } else {
            $('#title-length').css('color', '#10b981');
        }

        if (descLength > 160) {
            $('#description-length').css('color', '#ef4444');
        } else if (descLength > 150) {
            $('#description-length').css('color', '#f59e0b');
        } else {
            $('#description-length').css('color', '#10b981');
        }

        if (titlePixels > 580) {
            $('#title-pixels').css('color', '#ef4444');
        } else if (titlePixels > 520) {
            $('#title-pixels').css('color', '#f59e0b');
        } else {
            $('#title-pixels').css('color', '#10b981');
        }

        if (descPixels > 920) {
            $('#description-pixels').css('color', '#ef4444');
        } else if (descPixels > 820) {
            $('#description-pixels').css('color', '#f59e0b');
        } else {
            $('#description-pixels').css('color', '#10b981');
        }

        if (permalinkLength > 75) {
            $('#permalink-length').css('color', '#ef4444');
        } else if (permalinkLength > 65) {
            $('#permalink-length').css('color', '#f59e0b');
        } else {
            $('#permalink-length').css('color', '#10b981');
        }
    }

    var debouncedCharCount = debounce(updateCharCount, 150);
    $('#seo_fury_title, #seo_fury_description, #seo_fury_permalink').on('input', debouncedCharCount);
    $('#title').on('input', debouncedCharCount);

    setTimeout(function () {
        updateCharCount();
    }, 100);

    function updateGooglePreview() {
        var titleVal = $('#seo_fury_title').val();
        var descVal = $('#seo_fury_description').val();
        var focus = ($('#seo_fury_focus_keyword').val() || '').trim();

        var title = titleVal ? parseVariables(titleVal) : (getPostTitle() || (seoFuryData.i18n.titlePlaceholder || 'Page Title'));
        var description = descVal ? parseVariables(descVal) : (seoFuryData.i18n.descriptionPlaceholder || 'Page description will appear here');

        $('.preview-title').html(highlightKeyword(title, focus));
        $('.preview-description').html(highlightKeyword(description, focus));

        var permalinkVal = $('#seo_fury_permalink').val() || seoFuryData.postPermalink || '';
        if (permalinkVal) {
            $('.google-preview-modal .preview-url').text(permalinkVal);
        }
    }

    var debouncedGooglePreview = debounce(updateGooglePreview, 150);
    var debouncedSeoAnalysis = debounce(updateSeoAnalysis, 200);
    var debouncedReadability = debounce(updateReadabilityAnalysis, 200);
    var debouncedExtraSeo = debounce(updateExtraSeoAnalysis, 200);

    $('#seo_fury_title, #seo_fury_description, #seo_fury_focus_keyword, #seo_fury_permalink').on('input', debouncedGooglePreview);
    $('#title').on('input', debouncedGooglePreview);

    $('#seo_fury_title, #seo_fury_description, #seo_fury_focus_keyword').on('input', function () {
        debouncedSeoAnalysis();
        debouncedReadability();
        debouncedExtraSeo();
    });

    setTimeout(function () {
        updateGooglePreview();
        updateSeoAnalysis();
        updateReadabilityAnalysis();
        updateExtraSeoAnalysis();
    }, 100);

    function updateOGPreview() {
        var title = $('#seo_fury_og_title').val() || $('#seo_fury_title').val() || getPostTitle() || seoFuryData.i18n.titlePlaceholder;
        var description = $('#seo_fury_og_description').val() || $('#seo_fury_description').val() || seoFuryData.i18n.descriptionPlaceholder;
        var image = $('#seo_fury_og_image').val();
        var focus = ($('#seo_fury_focus_keyword').val() || '').trim();

        title = parseVariables(title);
        description = parseVariables(description);

        $('.og-title').html(highlightKeyword(title, focus));
        $('.og-description').html(highlightKeyword(description, focus));

        if (image) {
            if ($('.og-image img').length === 0) {
                $('.og-image').html('<img src="' + image + '" alt="OG Preview" />');
            } else {
                $('.og-image img').attr('src', image);
            }
            $('.og-image').show();
        } else {
            $('.og-image').hide();
        }
    }

    var debouncedOgPreview = debounce(updateOGPreview, 150);
    $('#seo_fury_og_title, #seo_fury_og_description, #seo_fury_og_image, #seo_fury_title, #seo_fury_description, #seo_fury_focus_keyword').on('input', debouncedOgPreview);
    $('#title').on('input', debouncedOgPreview);

    setTimeout(function () {
        updateOGPreview();
    }, 100);

    function updateFacebookPreview() {
        var title = $('#seo_fury_fb_title').val() || $('#seo_fury_og_title').val() || $('#seo_fury_title').val() || getPostTitle() || seoFuryData.i18n.titlePlaceholder;
        var description = $('#seo_fury_fb_description').val() || $('#seo_fury_og_description').val() || $('#seo_fury_description').val() || seoFuryData.i18n.descriptionPlaceholder;
        var image = $('#seo_fury_fb_image').val() || $('#seo_fury_og_image').val() || $('#seo_fury_social_image').val();
        var focus = ($('#seo_fury_focus_keyword').val() || '').trim();

        title = parseVariables(title);
        description = parseVariables(description);

        $('.facebook-card .fb-title').html(highlightKeyword(title, focus));
        $('.facebook-card .fb-description').html(highlightKeyword(description, focus));

        if (image) {
            var $img = $('.facebook-card .social-post-image img');
            if ($img.length === 0) {
                $('.facebook-card .social-post-image').html('<img src="' + image + '" alt="FB Preview" />');
            } else {
                $img.attr('src', image);
            }
            $('.facebook-card .social-post-image').show();
        } else {
            $('.facebook-card .social-post-image').hide();
        }
    }

    var debouncedFacebookPreview = debounce(updateFacebookPreview, 150);
    $('#seo_fury_fb_title, #seo_fury_fb_description, #seo_fury_fb_image').on('input', debouncedFacebookPreview);
    $('#seo_fury_og_title, #seo_fury_og_description, #seo_fury_og_image').on('input', debouncedFacebookPreview);
    $('#seo_fury_title, #seo_fury_description, #seo_fury_focus_keyword').on('input', debouncedFacebookPreview);
    $('#seo_fury_social_image').on('input', debouncedFacebookPreview);
    $('#title').on('input', debouncedFacebookPreview);

    setTimeout(function () {
        updateFacebookPreview();
    }, 100);

    function updateTwitterPreview() {
        var title = $('#seo_fury_twitter_title').val() || $('#seo_fury_og_title').val() || $('#seo_fury_title').val() || getPostTitle() || seoFuryData.i18n.titlePlaceholder;
        var description = $('#seo_fury_twitter_description').val() || $('#seo_fury_og_description').val() || $('#seo_fury_description').val() || seoFuryData.i18n.descriptionPlaceholder;
        var image = $('#seo_fury_twitter_image').val() || $('#seo_fury_og_image').val() || $('#seo_fury_social_image').val();
        var focus = ($('#seo_fury_focus_keyword').val() || '').trim();

        title = parseVariables(title);
        description = parseVariables(description);

        $('.twitter-card .tw-title').html(highlightKeyword(title, focus));
        $('.twitter-card .tw-description').html(highlightKeyword(description, focus));

        if (image) {
            var $img = $('.twitter-card .social-post-image img');
            if ($img.length === 0) {
                $('.twitter-card .social-post-image').html('<img src="' + image + '" alt="Twitter Preview" />');
            } else {
                $img.attr('src', image);
            }
            $('.twitter-card .social-post-image').show();
        } else {
            $('.twitter-card .social-post-image').hide();
        }
    }

    var debouncedTwitterPreview = debounce(updateTwitterPreview, 150);
    $('#seo_fury_twitter_title, #seo_fury_twitter_description, #seo_fury_twitter_image, #seo_fury_twitter_card_type').on('input change', debouncedTwitterPreview);
    $('#seo_fury_og_title, #seo_fury_og_description, #seo_fury_og_image').on('input', debouncedTwitterPreview);
    $('#seo_fury_title, #seo_fury_description, #seo_fury_focus_keyword').on('input', debouncedTwitterPreview);
    $('#seo_fury_social_image').on('input', debouncedTwitterPreview);
    $('#title').on('input', debouncedTwitterPreview);

    setTimeout(function () {
        updateTwitterPreview();
    }, 100);

    var mediaUploader;

    $(document).on('click', '.sf-upload-image', function (e) {
        e.preventDefault();
        var button = $(this);
        var targetField = button.data('target');

        mediaUploader = wp.media({
            title: seoFuryData.i18n.selectImage,
            button: {
                text: seoFuryData.i18n.useThisImage
            },
            multiple: false
        });

        mediaUploader.on('select', function () {
            var attachment = mediaUploader.state().get('selection').first().toJSON();
            $('#' + targetField).val(attachment.url);

            $('[data-target="' + targetField + '"].sf-remove-image-button').show();

            var preview = $('#' + targetField).siblings('.sf-image-preview');
            if (preview.length) {
                preview.find('img').attr('src', attachment.url);
            } else {
                $('#' + targetField).after('<div class="sf-image-preview"><img src="' + attachment.url + '" style="max-width: 200px; margin-top: 10px;" /></div>');
            }

            $('#' + targetField).trigger('input');
        });

        mediaUploader.open();
    });

    $(document).on('click', '.sf-remove-image-button', function (e) {
        e.preventDefault();
        var button = $(this);
        var targetField = button.data('target');

        $('#' + targetField).val('');

        button.hide();

        $('#' + targetField).siblings('.sf-image-preview').remove();

        $('#' + targetField).trigger('input');
    });

    $('#seo_fury_canonical').each(function () {
        if (!$(this).val()) {
            var currentUrl = window.location.href.split('?')[0];
            if (typeof wp !== 'undefined' && wp.data && wp.data.select && wp.data.select('core/editor')) {
                try {
                    var permalink = wp.data.select('core/editor').getPermalink();
                    if (permalink) {
                        $(this).attr('placeholder', permalink);
                    }
                } catch (e) {
                    // Permalink not available in this context
                }
            }
        }
    });

    $('.seo-fury-tab[data-tab="basic"]').trigger('click');

    var schemaItems = [];

    // Schema Editor Modal State
    var editingSchemaId = null;
    var editingSchemaData = {};
    var builderModified = false; // true only when the user has actually edited in the advanced builder

    // Schema Builder editing state (for custom_builder items)
    var builderEditingId = null;

    function loadSchemaItems() {
        try {
            var raw = $('#seo_fury_schema_items').val() || '[]';
            var parsed = JSON.parse(raw);
            schemaItems = Array.isArray(parsed) ? parsed : [];
        } catch (e) {
            schemaItems = [];
        }
    }

    function saveSchemaItems() {
        $('#seo_fury_schema_items').val(JSON.stringify(schemaItems));
    }

    function getActiveSchemaId() {
        return $('#seo_fury_schema_active').val() || '';
    }

    function setActiveSchemaId(id) {
        $('#seo_fury_schema_active').val(id || '');
    }

    function getActiveSchemaItem() {
        var activeId = getActiveSchemaId();
        return schemaItems.find(function (item) { return item.id === activeId; }) || null;
    }

    function setSchemaMode(mode) {
        $('#seo_fury_schema_mode').val(mode);
    }

    function setSchemaType(type) {
        $('#seo_fury_schema_type').val(type || 'none');
    }

    function setDeepValue(obj, path, value) {
        var current = obj;
        for (var i = 0; i < path.length; i++) {
            var key = path[i];
            if (i === path.length - 1) {
                current[key] = value;
            } else {
                if (current[key] === undefined) {
                    current[key] = isNaN(path[i + 1]) ? {} : [];
                }
                current = current[key];
            }
        }
    }

    function getSchemaDataFromInputs() {
        var type = $('#seo_fury_schema_type').val() || 'none';
        var $container = $('.seo-fury-schema-fields[data-schema-type="' + type + '"]').first();
        if (!$container.length) $container = $('.seo-fury-schema-fields:visible').first();
        if (!$container.length) $container = $(document);
        var data = {};
        $container.find('[name^="schema_data["]').each(function () {
            var name = $(this).attr('name');
            if (!name) return;
            var value = $(this).val();
            // Handle PHP-style array fields (e.g. schema_data[field][])
            if (/\[\]$/.test(name)) {
                var arrKey = name.replace(/^schema_data\[/, '').replace(/\]\[\]$/, '');
                if (!data[arrKey]) data[arrKey] = [];
                if (value) data[arrKey].push(value);
                return;
            }
            var path = name.replace(/^schema_data\[/, '').replace(/\]$/, '').split('][');
            setDeepValue(data, path, value);
        });
        return data;
    }

    function clearSchemaDataInputs() {
        $('.seo-fury-schema-fields').find('[name^="schema_data["]').each(function () {
            $(this).val('');
        });
        // Reset HowTo steps container to a single empty default step
        var $stepsContainer = $('#howto-steps-container');
        if ($stepsContainer.length) {
            $stepsContainer.empty().append(
                '<div class="howto-step" data-index="0">' +
                    '<div class="seo-fury-field">' +
                        '<label>Step 1 Name</label>' +
                        '<input type="text" name="schema_data[steps][0][name]" value="">' +
                    '</div>' +
                    '<div class="seo-fury-field">' +
                        '<label>Step Description</label>' +
                        '<textarea name="schema_data[steps][0][text]" rows="2"></textarea>' +
                    '</div>' +
                    '<button type="button" class="button remove-howto-step">Remove Step</button>' +
                    '<hr style="margin: 15px 0;">' +
                '</div>'
            );
        }
        // Reset FAQ items container to a single empty default item
        var $faqContainer = $('#faq-items-container');
        if ($faqContainer.length) {
            var questionLabel       = (seoFuryData.i18n && seoFuryData.i18n.question)           || 'Question';
            var answerLabel         = (seoFuryData.i18n && seoFuryData.i18n.answer)             || 'Answer';
            var removeLabel         = (seoFuryData.i18n && seoFuryData.i18n.remove)             || 'Remove';
            var questionPlaceholder = (seoFuryData.i18n && seoFuryData.i18n.questionPlaceholder) || 'What is your question?';
            var answerPlaceholder   = (seoFuryData.i18n && seoFuryData.i18n.answerPlaceholder)   || 'Type the answer here...';
            $faqContainer.empty().append(
                '<div class="faq-item" data-index="0">' +
                    '<div class="seo-fury-field">' +
                        '<label>' + questionLabel + ' 1</label>' +
                        '<input type="text" name="schema_data[faq_items][0][question]" value="" placeholder="' + questionPlaceholder + '">' +
                    '</div>' +
                    '<div class="seo-fury-field">' +
                        '<label>' + answerLabel + '</label>' +
                        '<textarea name="schema_data[faq_items][0][answer]" rows="3" placeholder="' + answerPlaceholder + '"></textarea>' +
                    '</div>' +
                    '<button type="button" class="button remove-faq-item">' + removeLabel + '</button>' +
                    '<hr style="margin: 15px 0;">' +
                '</div>'
            );
        }
        // Reset date fields to "Use WordPress date" state
        $('.sf-schema-date-field').each(function () {
            $(this).find('.sf-date-custom-row').hide();
            $(this).find('.sf-date-auto-row').show();
        });
    }

    function buildArrayRow(fieldName, value, placeholder) {
        return $('<div class="sf-array-row">')
            .append(
                $('<input type="text">').attr('name', fieldName).val(value || '').attr('placeholder', placeholder || '')
            )
            .append(
                $('<button type="button" class="sf-array-remove-btn">').attr('title', 'Remove').html('&#x2715;')
            );
    }

    function setSchemaDataInputs(data, base) {
        base = base || 'schema_data';
        Object.keys(data || {}).forEach(function (key) {
            var value = data[key];
            var name = base + '[' + key + ']';

            // Special case: HowTo steps (array of objects) — only at the top level
            if (key === 'steps' && base === 'schema_data' && Array.isArray(value) && value.length > 0) {
                var $stepsContainer = $('#howto-steps-container');
                if ($stepsContainer.length) {
                    $stepsContainer.empty();
                    value.forEach(function (step, i) {
                        var html = '<div class="howto-step" data-index="' + i + '">' +
                            '<div class="seo-fury-field">' +
                                '<label>Step ' + (i + 1) + ' Name</label>' +
                                '<input type="text" name="schema_data[steps][' + i + '][name]" value="' + $('<span>').text(step.name || '').html() + '">' +
                            '</div>' +
                            '<div class="seo-fury-field">' +
                                '<label>Step Description</label>' +
                                '<textarea name="schema_data[steps][' + i + '][text]" rows="2">' + $('<span>').text(step.text || '').html() + '</textarea>' +
                            '</div>' +
                            '<button type="button" class="button remove-howto-step">Remove Step</button>' +
                            '<hr style="margin: 15px 0;">' +
                        '</div>';
                        $stepsContainer.append(html);
                    });
                }
                return;
            }

            // Special case: FAQ items (array of objects) — only at the top level
            if (key === 'faq_items' && base === 'schema_data' && Array.isArray(value) && value.length > 0) {
                var $faqContainer = $('#faq-items-container');
                if ($faqContainer.length) {
                    $faqContainer.empty();
                    var questionLabel = (seoFuryData.i18n && seoFuryData.i18n.question) || 'Question';
                    var answerLabel   = (seoFuryData.i18n && seoFuryData.i18n.answer)   || 'Answer';
                    var removeLabel   = (seoFuryData.i18n && seoFuryData.i18n.remove)   || 'Remove';
                    var questionPlaceholder = (seoFuryData.i18n && seoFuryData.i18n.questionPlaceholder) || 'What is your question?';
                    var answerPlaceholder   = (seoFuryData.i18n && seoFuryData.i18n.answerPlaceholder)   || 'Type the answer here...';
                    value.forEach(function (faq, i) {
                        var html = '<div class="faq-item" data-index="' + i + '">' +
                            '<div class="seo-fury-field">' +
                                '<label>' + questionLabel + ' ' + (i + 1) + '</label>' +
                                '<input type="text" name="schema_data[faq_items][' + i + '][question]" value="' + $('<span>').text(faq.question || '').html() + '" placeholder="' + questionPlaceholder + '">' +
                            '</div>' +
                            '<div class="seo-fury-field">' +
                                '<label>' + answerLabel + '</label>' +
                                '<textarea name="schema_data[faq_items][' + i + '][answer]" rows="3" placeholder="' + answerPlaceholder + '">' + $('<span>').text(faq.answer || '').html() + '</textarea>' +
                            '</div>' +
                            '<button type="button" class="button remove-faq-item">' + removeLabel + '</button>' +
                            '<hr style="margin: 15px 0;">' +
                        '</div>';
                        $faqContainer.append(html);
                    });
                }
                return;
            }

            if (Array.isArray(value)) {
                // Repopulate array list and activate toggle
                var $addBtn = $('[data-field-name="' + name + '[]"]');
                if ($addBtn.length) {
                    var $optBody = $addBtn.closest('.sf-opt-body');
                    var $list = $optBody.find('.sf-array-list');
                    $list.empty();
                    var placeholder = $addBtn.data('placeholder') || '';
                    value.forEach(function (val) {
                        if (val) $list.append(buildArrayRow(name + '[]', val, placeholder));
                    });
                    if (value.length > 0) {
                        $optBody.show();
                        $optBody.closest('.sf-opt-item').find('.sf-opt-toggle-btn').addClass('active');
                    }
                }
            } else if (typeof value === 'object' && value !== null) {
                setSchemaDataInputs(value, name);
            } else {
                var $checkboxField = $('[name="' + name + '"][type="checkbox"]');
                if ($checkboxField.length) {
                    var isChecked = (value === '1' || value === 1 || value === true);
                    $checkboxField.prop('checked', isChecked).trigger('change');
                } else {
                    $('[name="' + name + '"]').val(value);
                }
                // Toggle date field display mode when a custom date is present
                if ((key === 'date_published' || key === 'date_modified') && value) {
                    var $dateWrap = $('[name="' + name + '"]').closest('.sf-schema-date-field');
                    if ($dateWrap.length) {
                        $dateWrap.find('.sf-date-auto-row').hide();
                        $dateWrap.find('.sf-date-custom-row').show();
                    }
                }
            }
        });
    }

    function storeActiveSchemaData() {
        var item = getActiveSchemaItem();
        if (!item) return;
        var mode = $('#seo_fury_schema_mode').val() || 'generator';
        var type = $('#seo_fury_schema_type').val() || 'none';
        item.mode = mode;
        item.type = mode === 'custom' ? 'custom' : type;
        if (mode === 'custom') {
            item.custom = $('#seo_fury_schema_custom').val() || '';
            item.data = {};
        } else {
            item.custom = '';
            item.data = getSchemaDataFromInputs();
        }
        saveSchemaItems();
    }

    function applyActiveSchemaItem(item) {
        if (!item) {
            setSchemaMode('generator');
            setSchemaType('none');
            clearSchemaDataInputs();
            return;
        }
        setSchemaMode(item.mode || 'generator');
        setSchemaType(item.mode === 'custom' ? 'none' : (item.type || 'none'));
        clearSchemaDataInputs();
        if (item.mode === 'custom') {
            $('#seo_fury_schema_custom').val(item.custom || '');
            $('#schema-custom-json-output').text(item.custom || seoFuryData.i18n.pasteJsonLd);
        } else {
            setSchemaDataInputs(item.data || {});
        }
    }

    function getSchemaDefaultData(type) {
        var idMap = {
            article:     '%url%#article',
            product:     '%url%#product',
            review:      '%url%#review',
            recipe:      '%url%#recipe',
            faq:         '%url%#faqpage',
            how_to:      '%url%#howto',
            breadcrumb:  '%url%#breadcrumb',
            table:       '%url%#table',
            service:     '%url%#service',
            job_posting: '%url%#jobposting',
            event:       '%url%#event',
            video:       '%url%#video'
        };
        var defaults = {
            article: {
                article_type: 'Article',
                headline: '%seo_title%',
                description: '%seo_description%',
                keywords: '%keywords%',
                author_type: 'Person',
                author: '%name%',
                publisher_use_org: '1'
            }
        };
        var result = defaults[type] ? JSON.parse(JSON.stringify(defaults[type])) : {};
        if (idMap[type]) {
            result.id = idMap[type];
        }
        return result;
    }

    function getSchemaIconClass(type, mode) {
        if (mode === 'custom') {
            return 'dashicons-media-code';
        }
        var map = {
            article: 'dashicons-media-document',
            product: 'dashicons-cart',
            review: 'dashicons-star-filled',
            recipe: 'dashicons-carrot',
            faq: 'dashicons-editor-help',
            how_to: 'dashicons-welcome-learn-more',
            breadcrumb: 'dashicons-menu',
            table: 'dashicons-editor-table',
            service: 'dashicons-hammer',
            job_posting: 'dashicons-id-alt',
            course: 'dashicons-welcome-learn-more',
            event: 'dashicons-calendar-alt',
            local_business: 'dashicons-location',
            organization: 'dashicons-building',
            person: 'dashicons-admin-users',
            video: 'dashicons-video-alt3',
            book: 'dashicons-book-alt',
            software: 'dashicons-desktop'
        };
        return map[type] || 'dashicons-tag';
    }

    function normalizeSchemaType(type) {
        if (!type) {
            return '';
        }
        var raw = Array.isArray(type) ? type[0] : type;
        if (!raw) {
            return '';
        }
        var normalized = String(raw).trim();
        var map = {
            Article: 'article',
            BlogPosting: 'article',
            NewsArticle: 'article',
            FAQPage: 'faq',
            HowTo: 'how_to',
            VideoObject: 'video',
            Product: 'product',
            Review: 'review',
            Recipe: 'recipe',
            Event: 'event',
            Course: 'course',
            LocalBusiness: 'local_business',
            Organization: 'organization',
            Person: 'person',
            Book: 'book',
            SoftwareApplication: 'software',
            BreadcrumbList: 'breadcrumb',
            Table: 'table',
            Service: 'service',
            JobPosting: 'job_posting'
        };
        return map[normalized] || '';
    }

    function extractName(value) {
        if (!value) {
            return '';
        }
        if (typeof value === 'string') {
            return value;
        }
        if (Array.isArray(value)) {
            return extractName(value[0]);
        }
        return value.name || '';
    }

    function extractUrl(value) {
        if (!value) {
            return '';
        }
        if (typeof value === 'string') {
            return value;
        }
        if (Array.isArray(value)) {
            return extractUrl(value[0]);
        }
        return value.url || value['@id'] || '';
    }

    function toDateTimeLocal(value) {
        if (!value) {
            return '';
        }
        var str = String(value);
        if (str.length >= 16 && str.indexOf('T') > -1) {
            return str.slice(0, 16);
        }
        if (str.length === 10) {
            return str + 'T00:00';
        }
        return str;
    }

    function parseJsonLd(input) {
        if (!input) {
            return null;
        }
        var cleaned = input.replace(/<script[^>]*>/gi, '').replace(/<\/script>/gi, '');
        try {
            var parsed = JSON.parse(cleaned);
            $('#seo-fury-schema-error').hide().text('');
            return parsed;
        } catch (e) {
            var message = e && e.message ? e.message : 'Invalid JSON';
            $('#seo-fury-schema-error').text(message).show();
            return null;
        }
    }

    function findSchemaNode(parsed) {
        if (!parsed) {
            return null;
        }
        if (Array.isArray(parsed)) {
            return parsed.find(function (item) { return item && item['@type']; }) || parsed[0] || null;
        }
        if (parsed['@graph'] && Array.isArray(parsed['@graph'])) {
            return parsed['@graph'].find(function (item) { return item && item['@type']; }) || parsed['@graph'][0] || null;
        }
        return parsed;
    }

    function extractSchemaData(schemaType, node) {
        var data = {};
        if (!node) {
            return data;
        }
        if (node['@id']) {
            data.id = node['@id'];
        }
        if (schemaType === 'article') {
            var nodeType = node['@type'];
            if (nodeType === 'BlogPosting' || nodeType === 'NewsArticle') {
                data.article_type = nodeType;
            } else {
                data.article_type = 'Article';
            }
            if (node.headline) data.headline = node.headline;
            if (node.description) data.description = node.description;
            if (node.keywords) data.keywords = node.keywords;
            if (node.author) {
                data.author = extractName(node.author);
                if (node.author['@type']) data.author_type = node.author['@type'];
                if (node.author['url']) data.author_url = node.author['url'];
                if (node.author['@id']) data.author_id = node.author['@id'];
                if (node.author['sameAs']) {
                    var aSameAs = node.author['sameAs'];
                    data.author_same_as = Array.isArray(aSameAs) ? aSameAs : [aSameAs];
                }
                if (node.author['knowsAbout']) {
                    var aKnows = node.author['knowsAbout'];
                    data.author_knows_about = Array.isArray(aKnows) ? aKnows : [aKnows];
                }
                if (node.author['alumniOf']) {
                    var aAlumni = Array.isArray(node.author['alumniOf']) ? node.author['alumniOf'][0] : node.author['alumniOf'];
                    if (aAlumni) {
                        data.author_alumni_of_type = aAlumni['@type'] || 'EducationalOrganization';
                        data.author_alumni_of_name = typeof aAlumni === 'string' ? aAlumni : (aAlumni.name || '');
                    }
                }
                if (node.author['hasCredential']) {
                    var aCred = Array.isArray(node.author['hasCredential']) ? node.author['hasCredential'][0] : node.author['hasCredential'];
                    if (aCred) {
                        data.author_credential_name = typeof aCred === 'string' ? aCred : (aCred.name || '');
                    }
                }
            }
            if (node.publisher) {
                data.publisher = extractName(node.publisher);
                if (node.publisher['@type']) data.publisher_type = node.publisher['@type'];
                if (node.publisher['sameAs']) {
                    var pSameAs = node.publisher['sameAs'];
                    data.publisher_same_as = Array.isArray(pSameAs) ? pSameAs : [pSameAs];
                }
            }
            if (node.publisher && node.publisher.logo) data.publisher_logo = extractUrl(node.publisher.logo);
            if (node.image) data.image = extractUrl(node.image);
            if (node.datePublished) data.date_published = toDateTimeLocal(node.datePublished);
            if (node.dateModified) data.date_modified = toDateTimeLocal(node.dateModified);
        } else if (schemaType === 'product') {
            if (node.name) data.name = node.name;
            if (node.offers) {
                var offers = Array.isArray(node.offers) ? node.offers[0] : node.offers;
                if (offers && offers.price) data.price = offers.price;
                if (offers && offers.priceCurrency) data.currency = offers.priceCurrency;
                if (offers && offers.availability) {
                    data.availability = String(offers.availability).split('/').pop();
                }
            }
            if (node.brand) data.brand = extractName(node.brand);
            if (node.sku) data.sku = node.sku;
        } else if (schemaType === 'review') {
            if (node.itemReviewed) data.item_name = extractName(node.itemReviewed);
            if (node.reviewRating && node.reviewRating.ratingValue) data.rating = node.reviewRating.ratingValue;
            if (node.author) data.author = extractName(node.author);
            if (node.datePublished) data.review_date = String(node.datePublished).slice(0, 10);
        } else if (schemaType === 'recipe') {
            if (node.name) data.name = node.name;
            if (node.prepTime) data.prep_time = node.prepTime;
            if (node.cookTime) data.cook_time = node.cookTime;
            if (node.recipeYield) data.recipe_yield = node.recipeYield;
            if (node.nutrition && node.nutrition.calories) data.calories = node.nutrition.calories;
            if (node.recipeIngredient) data.ingredients = Array.isArray(node.recipeIngredient) ? node.recipeIngredient.join('\n') : node.recipeIngredient;
            if (node.recipeInstructions) {
                if (Array.isArray(node.recipeInstructions)) {
                    var instructions = node.recipeInstructions.map(function (step) {
                        return typeof step === 'string' ? step : (step.text || step.name || '');
                    });
                    data.instructions = instructions.filter(Boolean).join('\n');
                } else {
                    data.instructions = node.recipeInstructions;
                }
            }
        } else if (schemaType === 'faq') {
            if (Array.isArray(node.mainEntity)) {
                data.faq_items = node.mainEntity.map(function (entry) {
                    return {
                        question: entry.name || '',
                        answer: entry.acceptedAnswer ? (entry.acceptedAnswer.text || '') : ''
                    };
                }).filter(function (entry) { return entry.question || entry.answer; });
            }
        } else if (schemaType === 'how_to') {
            if (node.name) data.name = node.name;
            if (node.description) data.description = node.description;
            if (Array.isArray(node.step)) {
                data.steps = node.step.map(function (step) {
                    return {
                        name: step.name || '',
                        text: step.text || ''
                    };
                }).filter(function (entry) { return entry.name || entry.text; });
            }
        } else if (schemaType === 'event') {
            if (node.name) data.name = node.name;
            if (node.startDate) data.start_date = toDateTimeLocal(node.startDate);
            if (node.endDate) data.end_date = toDateTimeLocal(node.endDate);
            if (node.location) {
                data.location_name = extractName(node.location);
                if (node.location.address) data.address = typeof node.location.address === 'string' ? node.location.address : (node.location.address.streetAddress || '');
            }
            if (node.offers) {
                var eventOffer = Array.isArray(node.offers) ? node.offers[0] : node.offers;
                if (eventOffer && eventOffer.price) data.price = eventOffer.price;
            }
        } else if (schemaType === 'video') {
            if (node.name) data.name = node.name;
            if (node.description) data.description = node.description;
        } else if (schemaType === 'breadcrumb') {
            if (Array.isArray(node.itemListElement)) {
                var lines = node.itemListElement.map(function (item) {
                    var name = item.name || '';
                    var url = item.item || (item.item && item.item['@id']) || '';
                    if (name && url) {
                        return name + ' | ' + url;
                    }
                    return '';
                }).filter(Boolean);
                data.items = lines.join('\n');
            }
        } else if (schemaType === 'service') {
            if (node.name) data.name = node.name;
            if (node.description) data.description = node.description;
            if (node.provider) data.provider = extractName(node.provider);
            if (node.areaServed) data.area_served = extractName(node.areaServed) || node.areaServed;
            if (node.serviceType) data.service_type = node.serviceType;
        } else if (schemaType === 'job_posting') {
            if (node.title) data.title = node.title;
            if (node.description) data.description = node.description;
            if (node.datePosted) data.date_posted = String(node.datePosted).slice(0, 10);
            if (node.validThrough) data.valid_through = String(node.validThrough).slice(0, 10);
            if (node.employmentType) data.employment_type = node.employmentType;
            if (node.hiringOrganization) data.hiring_org = extractName(node.hiringOrganization);
            if (node.jobLocation) data.job_location = extractName(node.jobLocation.address) || extractName(node.jobLocation);
        } else if (schemaType === 'table') {
            if (node.name) data.name = node.name;
            if (node.description) data.description = node.description;
            if (node.tableRows) data.rows = Array.isArray(node.tableRows) ? node.tableRows.join('\n') : node.tableRows;
        }
        return data;
    }

    function buildSchemaJsonClient(type, data) {
        var typeMap = {
            article: 'Article',
            product: 'Product',
            faq: 'FAQPage',
            breadcrumb: 'BreadcrumbList',
            table: 'Table',
            service: 'Service',
            job_posting: 'JobPosting',
            review: 'Review',
            recipe: 'Recipe',
            event: 'Event',
            video: 'VideoObject',
            how_to: 'HowTo'
        };
        var schema = {
            '@context': 'https://schema.org',
            '@type': typeMap[type] || type
        };
        if (data.id) {
            schema['@id'] = data.id;
        }

        if (type === 'article') {
            schema['@type'] = data.article_type || 'Article';
            schema.headline = data.headline || seoFuryData.postTitle || '';
            if (data.description) schema.description = data.description;
            if (data.keywords) schema.keywords = data.keywords;
            var authorName = data.author || seoFuryData.postAuthor || '';
            schema.author = { '@type': 'Person', 'name': authorName };
            if (data.author_id) schema.author['@id'] = data.author_id;
            var pubType = data.publisher_type || 'Organization';
            var pubName = data.publisher || seoFuryData.siteName || '';
            schema.publisher = {
                '@type': pubType,
                'name': pubName
            };
            if (data.publisher_logo) {
                schema.publisher.logo = { '@type': 'ImageObject', 'url': data.publisher_logo };
            }
            if (data.date_published) {
                schema.datePublished = data.date_published;
            } else if (seoFuryData.postDate) {
                schema.datePublished = seoFuryData.postDate;
            }
            if (data.date_modified) {
                schema.dateModified = data.date_modified;
            } else if (seoFuryData.postModified) {
                schema.dateModified = seoFuryData.postModified;
            }
            if (data.image) {
                schema.image = data.image;
            } else if (seoFuryData.postThumbnail) {
                schema.image = seoFuryData.postThumbnail;
            }
        } else if (type === 'product') {
            schema.name = data.name || seoFuryData.postTitle || '';
            if (data.price) {
                schema.offers = {
                    '@type': 'Offer',
                    'price': data.price,
                    'priceCurrency': data.currency || 'USD',
                    'availability': 'https://schema.org/' + (data.availability || 'InStock')
                };
            }
            if (data.brand) schema.brand = { '@type': 'Brand', 'name': data.brand };
            if (data.sku) schema.sku = data.sku;
        } else if (type === 'faq') {
            schema.mainEntity = [];
            if (data.faq_items && Array.isArray(data.faq_items)) {
                data.faq_items.forEach(function (item) {
                    if (item.question && item.answer) {
                        schema.mainEntity.push({
                            '@type': 'Question',
                            'name': item.question,
                            'acceptedAnswer': { '@type': 'Answer', 'text': item.answer }
                        });
                    }
                });
            }
        } else if (type === 'breadcrumb') {
            schema.itemListElement = [];
            if (data.items) {
                var lines = data.items.split(/\r?\n/);
                var pos = 1;
                lines.forEach(function (line) {
                    var parts = line.split('|').map(function (s) { return s.trim(); });
                    if (parts.length >= 2 && parts[0] && parts[1]) {
                        schema.itemListElement.push({
                            '@type': 'ListItem',
                            'position': pos,
                            'name': parts[0],
                            'item': parts[1]
                        });
                        pos++;
                    }
                });
            }
        } else if (type === 'table') {
            schema.name = data.name || seoFuryData.postTitle || '';
            if (data.description) schema.description = data.description;
            if (data.rows) {
                schema.tableRows = data.rows.split(/\r?\n/);
            }
        } else if (type === 'service') {
            schema.name = data.name || seoFuryData.postTitle || '';
            if (data.description) schema.description = data.description;
            if (data.provider) schema.provider = { '@type': 'Organization', 'name': data.provider };
            if (data.area_served) schema.areaServed = data.area_served;
            if (data.service_type) schema.serviceType = data.service_type;
        } else if (type === 'job_posting') {
            schema.title = data.title || seoFuryData.postTitle || '';
            if (data.description) schema.description = data.description;
            if (data.date_posted) schema.datePosted = data.date_posted;
            if (data.valid_through) schema.validThrough = data.valid_through;
            if (data.employment_type) schema.employmentType = data.employment_type;
            if (data.hiring_org) schema.hiringOrganization = { '@type': 'Organization', 'name': data.hiring_org };
            if (data.job_location) schema.jobLocation = { '@type': 'Place', 'address': data.job_location };
        } else if (type === 'review') {
            if (data.item_name) schema.itemReviewed = { '@type': 'Thing', 'name': data.item_name };
            if (data.rating) schema.reviewRating = { '@type': 'Rating', 'ratingValue': data.rating };
            var reviewAuthor = data.author || seoFuryData.postAuthor || '';
            if (reviewAuthor) schema.author = { '@type': 'Person', 'name': reviewAuthor };
            if (data.review_date) schema.datePublished = data.review_date;
        } else if (type === 'recipe') {
            schema.name = data.name || seoFuryData.postTitle || '';
            if (data.prep_time) schema.prepTime = data.prep_time;
            if (data.cook_time) schema.cookTime = data.cook_time;
            if (data.recipe_yield) schema.recipeYield = data.recipe_yield;
            if (data.calories) schema.nutrition = { '@type': 'NutritionInformation', 'calories': data.calories };
            if (data.ingredients) schema.recipeIngredient = data.ingredients.split(/\r?\n/).filter(Boolean);
            if (data.instructions) {
                schema.recipeInstructions = data.instructions.split(/\r?\n/).filter(Boolean).map(function (step) {
                    return { '@type': 'HowToStep', 'text': step };
                });
            }
        } else if (type === 'event') {
            schema.name = data.name || seoFuryData.postTitle || '';
            if (data.start_date) schema.startDate = data.start_date;
            if (data.end_date) schema.endDate = data.end_date;
            if (data.location_name || data.address) {
                schema.location = { '@type': 'Place' };
                if (data.location_name) schema.location.name = data.location_name;
                if (data.address) schema.location.address = data.address;
            }
            if (data.price) schema.offers = { '@type': 'Offer', 'price': data.price };
        } else if (type === 'video') {
            schema.name = data.name || seoFuryData.postTitle || '';
            if (data.description) schema.description = data.description;
        } else if (type === 'how_to') {
            schema.name = data.name || seoFuryData.postTitle || '';
            if (data.description) schema.description = data.description;
            if (data.steps && Array.isArray(data.steps)) {
                schema.step = data.steps.filter(function (s) { return s.name || s.text; }).map(function (s) {
                    return { '@type': 'HowToStep', 'name': s.name || '', 'text': s.text || '' };
                });
            }
        }

        return schema;
    }

    function generateSchemaPreview() {
        var mode = $('#seo_fury_schema_mode').val() || 'generator';
        var type = $('#seo_fury_schema_type').val() || 'none';
        if (mode === 'custom' || type === 'none') {
            return;
        }
        var data = getSchemaDataFromInputs();
        var schema = buildSchemaJsonClient(type, data);
        var json = JSON.stringify(schema, null, 2);
        $('#schema-json-output').text(json);
    }

    var schemaPreviewTimer;
    function debouncedSchemaPreview() {
        clearTimeout(schemaPreviewTimer);
        schemaPreviewTimer = setTimeout(generateSchemaPreview, 300);
    }

    function renderSchemaLists() {
        var $lists = $('.seo-fury-schema-list');
        var labelMap = {};
        $('#seo_fury_schema_type option').each(function () {
            var value = $(this).attr('value');
            var label = $(this).text();
            labelMap[value] = label;
        });
        $lists.each(function () {
            var $list = $(this);
            if (!schemaItems.length) {
                $list.html('<p class="description">' + seoFuryData.i18n.noSchemaSelected + '</p>');
                return;
            }
            var editIcon = 'dashicons-edit';
            var editTitle = seoFuryData.i18n.edit;
            var html = schemaItems.map(function (item) {
                var label = item.mode === 'custom' ? seoFuryData.i18n.customJsonLd : (item.label || labelMap[item.type] || item.type);
                var icon = getSchemaIconClass(item.type, item.mode);
                return '' +
                    '<div class="seo-fury-schema-card ' + (item.id === getActiveSchemaId() ? 'is-active' : '') + '" data-schema-id="' + item.id + '">' +
                    '<div class="seo-fury-schema-label">' +
                    '<span class="seo-fury-schema-icon dashicons ' + icon + '" aria-hidden="true"></span>' +
                    '<div class="seo-fury-schema-label-text">' +
                    '<span class="seo-fury-schema-badge">' + label + '</span>' +
                    '<span class="seo-fury-schema-name seo-fury-sr-only">' + label + '</span>' +
                    '</div>' +
                    '</div>' +
                    '<div class="seo-fury-schema-actions">' +
                    '<a href="#" class="seo-fury-icon-link seo-fury-schema-edit" data-schema-id="' + item.id + '" title="' + editTitle + '" aria-label="' + editTitle + '">' +
                    '<span class="dashicons ' + editIcon + '"></span>' +
                    '</a>' +
                    '<a href="#" class="seo-fury-icon-link seo-fury-schema-preview" data-schema-id="' + item.id + '" title="' + seoFuryData.i18n.preview + '" aria-label="' + seoFuryData.i18n.preview + '">' +
                    '<span class="dashicons dashicons-visibility"></span>' +
                    '</a>' +
                    '<a href="#" class="seo-fury-icon-link seo-fury-schema-remove" data-schema-id="' + item.id + '" title="' + seoFuryData.i18n.remove + '" aria-label="' + seoFuryData.i18n.remove + '">' +
                    '<span class="dashicons dashicons-trash"></span>' +
                    '</a>' +
                    '</div>' +
                    '</div>';
            }).join('');
            $list.html(html);
        });
    }


    function updateSchemaUI() {
        var mode = $('#seo_fury_schema_mode').val() || 'generator';
        var type = $('#seo_fury_schema_type').val() || 'none';

        $('.seo-fury-schema-fields').hide();
        $('.seo-fury-schema-fields[data-schema-type="' + type + '"]').show();
        $('.seo-fury-schema-type-card').removeClass('is-active');
        $('.seo-fury-schema-type-card[data-schema-type="' + type + '"]').addClass('is-active');

        if (mode === 'custom') {
            $('#schema-preview-section').hide();
            $('#schema-custom-preview-section').show();
        } else {
            $('#schema-custom-preview-section').hide();
            $('#schema-preview-section').toggle(type !== 'none');
        }
        generateSchemaPreview();
    }

    function openSchemaModal(tab, focusSettings) {
        $('#seo-fury-schema-modal').show().css('display', 'block');
        $('body').addClass('modal-open').css('overflow', 'hidden');
        if (tab) {
            $('.seo-fury-schema-modal-tab[data-schema-tab="' + tab + '"]').trigger('click');
        }
        renderSchemaLists();
        updateSchemaUI();
        if (focusSettings) {
            var $target = $('#seo-fury-schema-settings');
            if ($target.length) {
                var $body = $('#seo-fury-schema-modal .seo-fury-modal-body');
                $body.animate({ scrollTop: $body.scrollTop() + $target.position().top - 8 }, 200);
                $target.addClass('seo-fury-highlight');
                setTimeout(function () {
                    $target.removeClass('seo-fury-highlight');
                }, 1000);
            }
        }
    }

    // Open Schema Generator modal in Builder tab, pre-loading saved builder_data for editing
    function openBuilderForEdit(schemaId) {
        var item = schemaItems.find(function(i) { return i.id === schemaId; });
        if (!item) return;
        builderEditingId = schemaId;
        if (window.SeoFurySchemaBuilder) {
            if (item.builder_data) {
                window.SeoFurySchemaBuilder.loadBuilderFromData(item.builder_data);
            } else {
                window.SeoFurySchemaBuilder.clearBuilder();
            }
        }
        openSchemaModal('builder');
    }

    // ========================================
    // Gutenberg FAQ Block Detection & Import
    // ========================================

    /**
     * Strip HTML tags from a string using a temporary jQuery element.
     */
    function sfStripHtml(html) {
        if (!html) return '';
        return $('<div>').html(html).text().trim();
    }

    /**
     * Recursively walk all Gutenberg blocks and collect FAQ items.
     * Supports: yoast/faq-block, rank-math/faq-block, uagb/faq,
     * and any block whose name contains "faq".
     * @returns {Array} Array of {question, answer} objects
     */
    function detectGutenbergFaqItems() {
        if (typeof wp === 'undefined' || !wp.data || !wp.data.select('core/block-editor')) {
            return [];
        }

        var faqItems = [];

        function extractFromBlock(block) {
            var name = (block.name || '').toLowerCase();
            var attrs = block.attributes || {};

            // Yoast SEO FAQ block
            if (block.name === 'yoast/faq-block') {
                var questions = attrs.questions || [];
                questions.forEach(function (q) {
                    var question = q.jsonQuestion || sfStripHtml(
                        Array.isArray(q.question)
                            ? q.question.map(function (p) { return p.value || ''; }).join('')
                            : (q.question || '')
                    );
                    var answer = q.jsonAnswer || sfStripHtml(
                        Array.isArray(q.answer)
                            ? q.answer.map(function (p) { return p.value || ''; }).join('')
                            : (q.answer || '')
                    );
                    if (question || answer) {
                        faqItems.push({ question: String(question), answer: String(answer) });
                    }
                });
                return;
            }

            // Rank Math FAQ block
            if (block.name === 'rank-math/faq-block') {
                var faqs = attrs.faqs || [];
                faqs.forEach(function (f) {
                    var question = sfStripHtml(f.question || f.title || '');
                    var answer = sfStripHtml(f.answer || f.content || '');
                    if (question || answer) {
                        faqItems.push({ question: question, answer: answer });
                    }
                });
                return;
            }

            // UAGB / Ultimate Addons FAQ block
            if (block.name === 'uagb/faq') {
                var faqs = attrs.faqs || attrs.questions || [];
                faqs.forEach(function (f) {
                    var question = sfStripHtml(f.question || f.title || f.jsonQuestion || '');
                    var answer = sfStripHtml(f.answer || f.content || f.jsonAnswer || '');
                    if (question || answer) {
                        faqItems.push({ question: question, answer: answer });
                    }
                });
                return;
            }

            // Generic: any block whose name contains "faq"
            if (name.indexOf('faq') !== -1) {
                var genericItems = attrs.faqs || attrs.questions || attrs.items || attrs.faqItems || [];
                if (Array.isArray(genericItems) && genericItems.length > 0) {
                    genericItems.forEach(function (f) {
                        var question = sfStripHtml(f.question || f.title || f.name || f.jsonQuestion || '');
                        var answer = sfStripHtml(f.answer || f.content || f.description || f.jsonAnswer || '');
                        if (question || answer) {
                            faqItems.push({ question: question, answer: answer });
                        }
                    });
                    return;
                }
            }

            // Recurse into inner blocks
            if (block.innerBlocks && block.innerBlocks.length > 0) {
                block.innerBlocks.forEach(function (inner) {
                    extractFromBlock(inner);
                });
            }
        }

        var allBlocks = wp.data.select('core/block-editor').getBlocks();
        allBlocks.forEach(function (block) {
            extractFromBlock(block);
        });

        return faqItems;
    }

    /**
     * Show a styled confirmation dialog.
     * @param {Function} onYes - callback when user clicks "Да"
     * @param {Function} onNo  - callback when user clicks "Нет" or dismisses
     */
    function showFaqBlockImportPrompt(onYes, onNo) {
        var i18n = (seoFuryData && seoFuryData.i18n) || {};
        var message = i18n.faqBlockImportPrompt || 'Add the questions and answers from this block to FAQPage schema markup?';
        var yesLabel = i18n.faqBlockImportYes || 'Yes';
        var noLabel = i18n.faqBlockImportNo || 'No';

        var $dialog = $(
            '<div id="sf-faq-import-confirm" style="position:fixed;top:0;left:0;width:100%;height:100%;z-index:999999;display:flex;align-items:center;justify-content:center;">' +
                '<div class="sf-faq-confirm-overlay" style="position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.55);"></div>' +
                '<div style="position:relative;background:#fff;border-radius:10px;padding:28px 32px;max-width:480px;width:90%;box-shadow:0 8px 32px rgba(0,0,0,0.22);z-index:1;">' +
                    '<p style="margin:0 0 22px;font-size:15px;line-height:1.6;color:#1e1e1e;">' + message + '</p>' +
                    '<div style="display:flex;gap:10px;justify-content:flex-end;">' +
                        '<button type="button" class="button button-secondary sf-faq-confirm-no">' + noLabel + '</button>' +
                        '<button type="button" class="button button-primary sf-faq-confirm-yes">' + yesLabel + '</button>' +
                    '</div>' +
                '</div>' +
            '</div>'
        ).appendTo('body');

        $dialog.find('.sf-faq-confirm-yes').on('click', function () {
            $dialog.remove();
            if (typeof onYes === 'function') { onYes(); }
        });

        $dialog.find('.sf-faq-confirm-no, .sf-faq-confirm-overlay').on('click', function () {
            $dialog.remove();
            if (typeof onNo === 'function') { onNo(); }
        });
    }

    /**
     * Entry point for editing a FAQPage schema.
     * Checks for Gutenberg FAQ blocks; if found prompts the user to import them.
     */
    function maybeImportGutenbergFaq(schemaId) {
        var faqItems = detectGutenbergFaqItems();
        if (faqItems.length === 0) {
            openSchemaEditorModal(schemaId, 'editor');
            return;
        }

        showFaqBlockImportPrompt(
            function () {
                // User confirmed — inject block FAQ items into schema data
                var item = schemaItems.find(function (i) { return i.id === schemaId; });
                if (item) {
                    item.data = item.data || {};
                    item.data.faq_items = faqItems;
                }
                openSchemaEditorModal(schemaId, 'editor');
            },
            function () {
                // User declined — open editor without changes
                openSchemaEditorModal(schemaId, 'editor');
            }
        );
    }

    // Schema Editor Modal Functions
    function openSchemaEditorModal(schemaId, initialTab) {
        var item = schemaItems.find(function(i) { return i.id === schemaId; });
        if (!item) {
            return;
        }

        // Store editing state
        editingSchemaId = schemaId;
        editingSchemaData = JSON.parse(JSON.stringify(item)); // Deep clone
        builderModified = false;

        // Populate fields based on mode
        if (item.mode === 'generator') {
            loadSchemaFieldsIntoEditor(item.type, item.data || {});
        } else if (item.mode === 'custom' || item.mode === 'custom_builder') {
            loadCustomSchemaIntoEditor(item.custom || '');
        }

        var $editorModal = $('#seo-fury-schema-editor-modal');

        // Move modal to body to escape any containing block (e.g. Gutenberg sidebar)
        if (!$editorModal.parent().is('body')) {
            $editorModal.appendTo('body');
        }

        // Show modal
        $editorModal.show().css('display', 'block');
        $('body').addClass('modal-open').css('overflow', 'hidden');

        // Switch to appropriate tab via direct class manipulation (avoid triggering generic handlers)
        var targetTab = initialTab || 'editor';
        $editorModal.find('.seo-fury-modal-tab[data-editor-tab]').removeClass('active');
        $editorModal.find('.seo-fury-modal-panel[data-editor-panel]').removeClass('active');
        $editorModal.find('.seo-fury-modal-tab[data-editor-tab="' + targetTab + '"]').addClass('active');
        $editorModal.find('.seo-fury-modal-panel[data-editor-panel="' + targetTab + '"]').addClass('active');

        // If switching to preview, fetch data
        if (targetTab === 'preview') {
            collectEditorFieldData();
            fetchResolvedSchemaPreview();
        }
    }

    function loadSchemaFieldsIntoEditor(type, data) {
        var $container = $('#schema-editor-fields-container');
        $container.empty();

        // Clone fields from main modal
        var $sourceFields = $('.seo-fury-schema-fields[data-schema-type="' + type + '"]').first();
        if (!$sourceFields.length) {
            if (!type || type === 'none') {
                $container.html('<p class="description">' + (seoFuryData.i18n.noSchemaSelected || 'No schema type selected. Use the Schema Generator to select a type.') + '</p>');
            } else {
                $container.html('<p class="description">Schema fields not found for type: ' + type + '</p>');
            }
            return;
        }

        var $clonedFields = $sourceFields.clone();
        $clonedFields.show(); // Ensure visible

        // Populate with current data
        $.each(data, function(key, value) {
            if ((key === 'date_published' || key === 'date_modified') && value) {
                // Show custom date row and populate input
                var $dateWrap = $clonedFields.find('[name="schema_data[' + key + ']"]').closest('.sf-schema-date-field');
                if ($dateWrap.length) {
                    $dateWrap.find('.sf-date-auto-row').hide();
                    $dateWrap.find('.sf-date-custom-row').show();
                }
                $clonedFields.find('[name="schema_data[' + key + ']"]').val(value);
            } else if (Array.isArray(value) && key !== 'faq_items') {
                // Handle array fields (sameAs, knowsAbout, etc.)
                var $addBtn = $clonedFields.find('[data-field-name="schema_data[' + key + '][]"]');
                if ($addBtn.length) {
                    var $optBody = $addBtn.closest('.sf-opt-body');
                    var $list = $optBody.find('.sf-array-list');
                    $list.empty();
                    var ph = $addBtn.data('placeholder') || '';
                    value.forEach(function (val) {
                        if (val) $list.append(buildArrayRow('schema_data[' + key + '][]', val, ph));
                    });
                    if (value.length > 0) {
                        $optBody.show();
                        $addBtn.closest('.sf-opt-item').find('.sf-opt-toggle-btn').addClass('active');
                    }
                }
            } else if (key === 'faq_items' && Array.isArray(value)) {
                // Handle FAQ items: rebuild the container with saved items
                var $faqContainer = $clonedFields.find('#faq-items-container');
                if ($faqContainer.length) {
                    $faqContainer.empty();
                    var questionLabel = (seoFuryData.i18n && seoFuryData.i18n.question) || 'Question';
                    var answerLabel = (seoFuryData.i18n && seoFuryData.i18n.answer) || 'Answer';
                    var removeLabel = (seoFuryData.i18n && seoFuryData.i18n.remove) || 'Remove';
                    var questionPlaceholder = (seoFuryData.i18n && seoFuryData.i18n.questionPlaceholder) || 'What is your question?';
                    var answerPlaceholder = (seoFuryData.i18n && seoFuryData.i18n.answerPlaceholder) || 'Type the answer here...';

                    value.forEach(function(faq, i) {
                        var html = '<div class="faq-item" data-index="' + i + '">' +
                            '<div class="seo-fury-field">' +
                                '<label>' + questionLabel + ' ' + (i + 1) + '</label>' +
                                '<input type="text" name="schema_data[faq_items][' + i + '][question]" value="' + $('<span>').text(faq.question || '').html() + '" placeholder="' + questionPlaceholder + '">' +
                            '</div>' +
                            '<div class="seo-fury-field">' +
                                '<label>' + answerLabel + '</label>' +
                                '<textarea name="schema_data[faq_items][' + i + '][answer]" rows="3" placeholder="' + answerPlaceholder + '">' + $('<span>').text(faq.answer || '').html() + '</textarea>' +
                            '</div>' +
                            '<button type="button" class="button remove-faq-item">' + removeLabel + '</button>' +
                            '<hr style="margin: 15px 0;">' +
                        '</div>';
                        $faqContainer.append(html);
                    });
                }
            } else if (key === 'steps' && Array.isArray(value)) {
                // Handle HowTo steps: rebuild the container with saved steps
                var $stepsContainer = $clonedFields.find('#howto-steps-container');
                if ($stepsContainer.length) {
                    $stepsContainer.empty();
                    value.forEach(function(step, i) {
                        var html = '<div class="howto-step" data-index="' + i + '">' +
                            '<div class="seo-fury-field">' +
                                '<label>Step ' + (i + 1) + ' Name</label>' +
                                '<input type="text" name="schema_data[steps][' + i + '][name]" value="' + $('<span>').text(step.name || '').html() + '">' +
                            '</div>' +
                            '<div class="seo-fury-field">' +
                                '<label>Step Description</label>' +
                                '<textarea name="schema_data[steps][' + i + '][text]" rows="2">' + $('<span>').text(step.text || '').html() + '</textarea>' +
                            '</div>' +
                            '<button type="button" class="button remove-howto-step">Remove Step</button>' +
                            '<hr style="margin: 15px 0;">' +
                        '</div>';
                        $stepsContainer.append(html);
                    });
                }
            } else {
                $clonedFields.find('[name="schema_data[' + key + ']"]').val(value);
            }
        });

        $container.append($clonedFields);
    }

    function loadCustomSchemaIntoEditor(customJson) {
        var $container = $('#schema-editor-fields-container');
        $container.empty();

        var html = '<div class="seo-fury-section">' +
            '<h4>' + (seoFuryData.i18n.customJsonLd || 'Custom JSON-LD') + '</h4>' +
            '<textarea id="schema-editor-custom-json" rows="15" style="width:100%; font-family:monospace; padding:10px; border-radius:6px;">' +
            (customJson || '') +
            '</textarea>' +
            '</div>';

        $container.html(html);
    }

    function fetchResolvedSchemaPreview() {
        var item = editingSchemaData;
        if (!item) {
            $('#schema-editor-preview-output').text('No schema data available');
            return;
        }

        var postId = $('#post_ID').val() || seoFuryData.postId;

        var requestData = {
            action: 'seo_fury_get_schema_preview',
            nonce: seoFuryData.nonce,
            post_id: postId,
            schema_type: item.type || 'none',
            mode: item.mode || 'generator',
            schema_data: item.data || {},
            custom_json: item.custom || ''
        };

        $('#schema-editor-preview-output').text('Loading preview...');

        $.ajax({
            url: seoFuryData.ajaxUrl,
            type: 'POST',
            data: requestData,
            success: function(response) {
                if (response.success && response.data && 'preview' in response.data) {
                    $('#schema-editor-preview-output').text(response.data.preview || '');
                } else {
                    var errorMsg = (response.data && response.data.message) ? response.data.message : (typeof response.data === 'string' ? response.data : 'Unknown error');
                    $('#schema-editor-preview-output').text('Error: ' + errorMsg);
                }
            },
            error: function(xhr, status, error) {
                $('#schema-editor-preview-output').text('Failed to load preview: ' + error);
            }
        });
    }

    function collectEditorFieldData() {
        // If advanced tab is active AND the user has actually edited in the builder,
        // collect from builder (which will also update mode/type).
        // If the user only opened the advanced tab to view without editing,
        // fall through and collect from the generator form fields as normal.
        if ($('#seo-fury-schema-editor-modal .seo-fury-modal-tab[data-editor-tab="advanced"]').hasClass('active')) {
            if (builderModified) {
                collectEditorBuilderData();
                return;
            }
            // Builder not modified — fall through to collect from generator form below
        }

        if (editingSchemaData.mode === 'generator') {
            var data = {};
            // Collect FAQ items separately (nested array fields)
            var faqItems = [];
            $('#schema-editor-fields-container').find('.faq-item').each(function() {
                var question = $(this).find('input[name*="[question]"]').val() || '';
                var answer = $(this).find('textarea[name*="[answer]"]').val() || '';
                if (question || answer) {
                    faqItems.push({ question: question, answer: answer });
                }
            });
            if (faqItems.length > 0) {
                data.faq_items = faqItems;
            }

            // Collect HowTo steps separately (nested indexed array fields)
            var howtoSteps = [];
            $('#schema-editor-fields-container').find('.howto-step').each(function() {
                var stepName = $(this).find('input[name*="[name]"]').val() || '';
                var stepText = $(this).find('textarea[name*="[text]"]').val() || '';
                if (stepName || stepText) {
                    howtoSteps.push({ name: stepName, text: stepText });
                }
            });
            if (howtoSteps.length > 0) {
                data.steps = howtoSteps;
            }

            // Collect PHP-style array fields (name ending in [])
            var seenArrayKeys = {};
            $('#schema-editor-fields-container').find('[name$="[]"]').each(function() {
                var name = $(this).attr('name');
                if (!name || name.indexOf('schema_data[') !== 0) return;
                var arrKey = name.replace(/^schema_data\[/, '').replace(/\]\[\]$/, '');
                if (!seenArrayKeys[arrKey]) {
                    seenArrayKeys[arrKey] = true;
                    data[arrKey] = [];
                }
                var val = $(this).val();
                if (val) data[arrKey].push(val);
            });

            // Collect simple (non-array) fields
            $('#schema-editor-fields-container').find('input, select, textarea').each(function() {
                var $field = $(this);
                var name = $field.attr('name');
                if (name && name.indexOf('schema_data[') === 0) {
                    // Skip FAQ nested fields — already collected above
                    if (name.indexOf('faq_items') !== -1) return;
                    // Skip HowTo steps fields — already collected above
                    if (name.indexOf('[steps]') !== -1) return;
                    // Skip PHP-style array fields — already collected above
                    if (/\[\]$/.test(name)) return;
                    var key = name.replace('schema_data[', '').replace(']', '');
                    data[key] = $field.val();
                }
            });
            editingSchemaData.data = data;
        } else if (editingSchemaData.mode === 'custom') {
            // custom_builder mode: custom JSON is managed exclusively by collectEditorBuilderData()
            editingSchemaData.custom = $('#schema-editor-custom-json').val();
        }
    }

    /* ================================================
       EDITOR BUILDER (advanced tab) helpers
    ================================================ */

    /**
     * Convert a JSON-LD object into the builder properties array.
     * Handles nested objects (→ groups) and arrays recursively.
     */
    function jsonToBuilderProperties(obj) {
        var props = [];
        var keys = Object.keys(obj).filter(function(k) {
            return k !== '@context' && k !== '@type';
        });
        keys.forEach(function(key) {
            var val = obj[key];
            if (val !== null && val !== undefined && typeof val === 'object' && !Array.isArray(val)) {
                var groupType = val['@type'] || '';
                var children = jsonToBuilderProperties(val);
                props.push({ type: 'group', key: key, groupType: groupType, children: children });
            } else if (Array.isArray(val)) {
                // Arrays of objects → expand each element as a numbered group (key[0], key[1], ...)
                // so they are visible and editable in the builder.
                // Plain arrays (strings/numbers) remain as a raw JSON array field.
                var allObjects = val.length > 0 && val.every(function(item) {
                    return item !== null && typeof item === 'object' && !Array.isArray(item);
                });
                if (allObjects) {
                    val.forEach(function(item, index) {
                        var groupType = item['@type'] || '';
                        var children = jsonToBuilderProperties(item);
                        props.push({ type: 'group', key: key + '[' + index + ']', groupType: groupType, children: children });
                    });
                } else {
                    props.push({ key: key, value: JSON.stringify(val), valueType: 'array' });
                }
            } else if (val !== null && val !== undefined) {
                props.push({ key: key, value: String(val), valueType: 'text' });
            }
        });
        return props;
    }

    /** Render a properties array (from jsonToBuilderProperties) into the builder container. */
    function loadPropsIntoBuilder($container, props) {
        props.forEach(function(prop) {
            if (prop.type === 'group') {
                $container.append(SeoFurySchemaBuilder.createGroup(prop.key, prop.groupType, prop.children));
            } else {
                $container.append(SeoFurySchemaBuilder.createPropertyRow(prop.key, prop.value, prop.valueType));
            }
        });
    }

    function initEditorBuilder() {
        var item = editingSchemaData;
        var $container = $('#schema-editor-builder-properties');
        var $typeInput = $('#schema-editor-builder-type');

        $container.empty();

        if (item.builder_data && item.builder_data.properties && item.builder_data.properties.length > 0) {
            // Load from existing builder_data
            $typeInput.val(item.builder_data.type || '');
            loadPropsIntoBuilder($container, item.builder_data.properties);
            SeoFurySchemaBuilder.injectSchemaTypesDropdown();
            updateEditorBuilderPreview();
            builderModified = false;

        } else if (item.mode === 'custom' || item.mode === 'custom_builder') {
            // Parse JSON and reconstruct properties with full nested-object support
            try {
                var parsed = JSON.parse(item.custom || '{}');
                $typeInput.val(parsed['@type'] || '');
                var cProps = jsonToBuilderProperties(parsed);
                if (cProps.length > 0) {
                    loadPropsIntoBuilder($container, cProps);
                } else {
                    $container.append(SeoFurySchemaBuilder.createPropertyRow('', '', 'text'));
                }
            } catch (e) {
                $container.append(SeoFurySchemaBuilder.createPropertyRow('', '', 'text'));
            }
            SeoFurySchemaBuilder.injectSchemaTypesDropdown();
            updateEditorBuilderPreview();
            builderModified = false;

        } else if (item.mode === 'generator') {
            // Fetch JSON-LD structure from server (shortcodes kept as-is) then populate builder
            var typeStr = item.type ? item.type.charAt(0).toUpperCase() + item.type.slice(1) : '';
            $typeInput.val(typeStr);
            $container.html('<p class="description" style="padding:8px 0;">' + ((seoFuryData.i18n && seoFuryData.i18n.loadingSchemaFields) || 'Loading schema fields...') + '</p>');

            var postId = $('#post_ID').val() || seoFuryData.postId;
            $.ajax({
                url: seoFuryData.ajaxUrl,
                type: 'POST',
                data: {
                    action: 'seo_fury_generator_to_json',
                    nonce: seoFuryData.nonce,
                    post_id: postId,
                    schema_type: item.type || '',
                    schema_data: item.data || {}
                },
                success: function(response) {
                    $container.empty();
                    if (response.success && response.data && response.data.schema) {
                        // Use the correct @type from the server response (e.g. 'FAQPage' not 'Faq', 'HowTo' not 'How_to')
                        if (response.data.schema['@type']) {
                            $typeInput.val(response.data.schema['@type']);
                        }
                        var gProps = jsonToBuilderProperties(response.data.schema);
                        // Add default @id = %url%#<type> if not already present
                        var hasIdProp = gProps.some(function(p) { return p.key === '@id'; });
                        if (!hasIdProp) {
                            var schemaTypeForId = ($typeInput.val() || '').toLowerCase();
                            gProps.unshift({ key: '@id', value: '%url%#' + schemaTypeForId, valueType: 'text' });
                        }
                        if (gProps.length > 0) {
                            loadPropsIntoBuilder($container, gProps);
                        } else {
                            $container.append(SeoFurySchemaBuilder.createPropertyRow('', '', 'text'));
                        }
                    } else {
                        $container.append(SeoFurySchemaBuilder.createPropertyRow('', '', 'text'));
                    }
                    SeoFurySchemaBuilder.injectSchemaTypesDropdown();
                    updateEditorBuilderPreview();
                    builderModified = false;
                },
                error: function() {
                    $container.empty();
                    $container.append(SeoFurySchemaBuilder.createPropertyRow('', '', 'text'));
                    SeoFurySchemaBuilder.injectSchemaTypesDropdown();
                    updateEditorBuilderPreview();
                    builderModified = false;
                }
            });
            return; // injectSchemaTypesDropdown / updateEditorBuilderPreview called in callbacks

        } else {
            $container.append(SeoFurySchemaBuilder.createPropertyRow('', '', 'text'));
            SeoFurySchemaBuilder.injectSchemaTypesDropdown();
            updateEditorBuilderPreview();
            builderModified = false;
        }
    }

    function getEditorBuilderData() {
        return {
            type: $('#schema-editor-builder-type').val().trim(),
            properties: SeoFurySchemaBuilder.getDataFromContainer($('#schema-editor-builder-properties'))
        };
    }

    function buildEditorBuilderJson() {
        var schemaType = $('#schema-editor-builder-type').val().trim();
        if (!schemaType) return null;
        var schema = { '@context': 'https://schema.org', '@type': schemaType };
        var props = SeoFurySchemaBuilder.buildJsonFromContainer($('#schema-editor-builder-properties'));
        $.extend(schema, props);
        return schema;
    }

    function updateEditorBuilderPreview() {
        var schema = buildEditorBuilderJson();
        var $output = $('#schema-editor-builder-json-output');
        var $section = $('#schema-editor-builder-preview-section');
        if (schema) {
            $output.text(JSON.stringify(schema, null, 2));
            $section.show();
        } else {
            $output.text('');
            $section.hide();
        }
    }

    function collectEditorBuilderData() {
        var builderData = getEditorBuilderData();
        var jsonSchema = buildEditorBuilderJson();
        if (jsonSchema) {
            editingSchemaData.builder_data = builderData;
            editingSchemaData.custom = JSON.stringify(jsonSchema, null, 2);
            editingSchemaData.mode = 'custom_builder';
            editingSchemaData.label = builderData.type || editingSchemaData.label || 'Custom';
            editingSchemaData.type = 'custom_builder';
        }
    }

    // Editor builder — add property (top level)
    $(document).on('click', '#schema-editor-builder-add-prop', function() {
        $('#schema-editor-builder-properties').append(SeoFurySchemaBuilder.createPropertyRow('', '', 'text'));
        builderModified = true;
        updateEditorBuilderPreview();
    });

    // Editor builder — add group (top level)
    $(document).on('click', '#schema-editor-builder-add-group', function() {
        $('#schema-editor-builder-properties').append(SeoFurySchemaBuilder.createGroup('', '', null));
        builderModified = true;
        updateEditorBuilderPreview();
    });

    // Editor builder — real-time preview update on input changes
    $(document).on('input change', '#schema-editor-builder-type', function() {
        builderModified = true;
        updateEditorBuilderPreview();
    });

    $(document).on('input change',
        '#schema-editor-builder-properties .schema-builder-key, ' +
        '#schema-editor-builder-properties .schema-builder-value, ' +
        '#schema-editor-builder-properties .schema-builder-value-type, ' +
        '#schema-editor-builder-properties .schema-builder-group-key, ' +
        '#schema-editor-builder-properties .schema-builder-group-type',
        function() {
            builderModified = true;
            updateEditorBuilderPreview();
        }
    );

    // Editor builder — update preview after row/group remove
    $(document).on('click',
        '#schema-editor-builder-properties .schema-builder-remove, ' +
        '#schema-editor-builder-properties .schema-builder-remove-group',
        function() {
            builderModified = true;
            // Let the existing handler remove the element first
            setTimeout(updateEditorBuilderPreview, 0);
        }
    );

    function closeSchemaEditorModal() {
        $('#seo-fury-schema-editor-modal').hide();
        $('#schema-editor-fields-container').empty();
        $('#schema-editor-builder-properties').empty();
        $('#schema-editor-builder-type').val('');
        $('#schema-editor-builder-json-output').text('');
        $('#schema-editor-builder-preview-section').hide();
        editingSchemaId = null;
        editingSchemaData = {};

        // Only remove modal-open if no other modal is visible
        var hasVisibleModal = $('#seo-fury-schema-modal').is(':visible') || $('#seo-fury-snippet-modal').is(':visible');
        if (!hasVisibleModal) {
            $('body').removeClass('modal-open').css('overflow', '');
        }
    }

    loadSchemaItems();
    renderSchemaLists();
    applyActiveSchemaItem(getActiveSchemaItem());
    updateSchemaUI();

    // Allow external code (e.g. template editor page) to open the schema editor modal.
    $(document).on('seo-fury:open-schema-editor', function (e, schemaId, tab) {
        openSchemaEditorModal(schemaId, tab || 'editor');
    });

    // Allow external code to open the schema generator modal.
    $(document).on('seo-fury:open-schema-generator', function (e, tab) {
        openSchemaModal(tab || 'templates');
    });

    // Allow external code to reset schemaItems (used by inline template editor on list page).
    $(document).on('seo-fury:reset-schema-items', function (e, newItems, activeId) {
        schemaItems = Array.isArray(newItems) ? newItems : [];
        saveSchemaItems();
        if (typeof activeId === 'string') {
            $('#seo_fury_schema_active').val(activeId);
        }
        renderSchemaLists();
    });

    $(document).on('click', '.seo-fury-open-schema-modal', function (e) {
        if (e && e.preventDefault) {
            e.preventDefault();
        }
        openSchemaModal('templates');
    });

    $(document).on('click', '.seo-fury-schema-modal-close, #seo-fury-schema-modal .seo-fury-modal-overlay', function (e) {
        e.preventDefault();
        storeActiveSchemaData();
        $('#seo-fury-schema-modal').hide();
        $('body').removeClass('modal-open').css('overflow', '');
    });

    $(document).on('click', '.seo-fury-schema-modal-tab', function () {
        var tab = $(this).data('schema-tab');
        $('.seo-fury-schema-modal-tab').removeClass('active');
        $('.seo-fury-schema-modal-panel').removeClass('active');
        $(this).addClass('active');
        $('.seo-fury-schema-modal-panel[data-schema-panel="' + tab + '"]').addClass('active');
        if (tab === 'import') {
            setSchemaMode('custom');
            setSchemaType('none');
        } else {
            setSchemaMode('generator');
        }
        updateSchemaUI();
    });

    $(document).on('click', '.seo-fury-schema-apply', function () {
        storeActiveSchemaData();
        var type = $(this).data('schema-type');
        var label = $(this).closest('.seo-fury-schema-type-card').find('.seo-fury-schema-type-name').text() || type;
        var existing = schemaItems.find(function (item) {
            return item.mode === 'generator' && item.type === type;
        });
        if (existing) {
            setActiveSchemaId(existing.id);
            applyActiveSchemaItem(existing);
            saveSchemaItems();
            renderSchemaLists();
            updateSchemaUI();
            return;
        }
        var item = {
            id: 'schema_' + Date.now(),
            type: type,
            mode: 'generator',
            label: label,
            data: getSchemaDefaultData(type),
            custom: ''
        };
        schemaItems.push(item);
        setActiveSchemaId(item.id);
        setSchemaType(type);
        setSchemaMode('generator');
        clearSchemaDataInputs();
        setSchemaDataInputs(item.data);
        saveSchemaItems();
        renderSchemaLists();
        updateSchemaUI();
    });

    // Types that have full round-trip support (JS extractor + PHP builder).
    // All other recognized types fall back to custom mode to preserve the imported JSON exactly.
    var GENERATOR_TYPES = {
        article: true, product: true, faq: true, how_to: true,
        review: true, recipe: true, event: true, video: true,
        breadcrumb: true, table: true, service: true, job_posting: true
    };

    $(document).on('click', '.seo-fury-schema-import-apply', function () {
        storeActiveSchemaData();
        var custom = $('#seo_fury_schema_custom').val() || '';
        $('#seo-fury-schema-error').hide().text('');
        if (!custom.trim()) {
            $('#seo-fury-schema-error').text(seoFuryData.i18n.jsonLdEmpty).show();
            return;
        }
        var parsed = parseJsonLd(custom);
        if (!parsed) {
            return;
        }
        var node = findSchemaNode(parsed);
        var detectedType = node ? normalizeSchemaType(node['@type']) : '';

        // Only convert to generator when the type has full extractor + builder support.
        // For all other recognized types (Organization, Person, etc.) store raw JSON so
        // the imported markup is output exactly as provided.
        if (detectedType && GENERATOR_TYPES[detectedType]) {
            var labelMap = {};
            $('#seo_fury_schema_type option').each(function () {
                labelMap[$(this).attr('value')] = $(this).text();
            });
            var existing = schemaItems.find(function (item) {
                return item.mode === 'generator' && item.type === detectedType;
            });
            var data = extractSchemaData(detectedType, node);
            if (existing) {
                existing.data = data;
                setActiveSchemaId(existing.id);
                applyActiveSchemaItem(existing);
            } else {
                var item = {
                    id: 'schema_' + Date.now(),
                    type: detectedType,
                    mode: 'generator',
                    label: labelMap[detectedType] || detectedType,
                    data: data,
                    custom: ''
                };
                schemaItems.push(item);
                setActiveSchemaId(item.id);
                applyActiveSchemaItem(item);
            }
            setSchemaMode('generator');
            setSchemaType(detectedType);
            saveSchemaItems();
            renderSchemaLists();
            updateSchemaUI();
            $('#seo_fury_schema_custom').val('');
            $('#schema-custom-json-output').text(seoFuryData.i18n.pasteJsonLd);
            return;
        }

        // Custom / unrecognized-type path: store the raw JSON so it is output verbatim.
        // For recognized-but-no-extractor types (Organization, Person, etc.) use their
        // label if available, otherwise fall back to "Custom JSON-LD".
        var labelMap2 = {};
        $('#seo_fury_schema_type option').each(function () {
            labelMap2[$(this).attr('value')] = $(this).text();
        });
        var customLabel = (detectedType && labelMap2[detectedType])
            ? labelMap2[detectedType]
            : seoFuryData.i18n.customJsonLd;

        var item = {
            id: 'schema_' + Date.now(),
            type: 'custom',
            mode: 'custom',
            label: customLabel,
            data: {},
            custom: custom
        };
        schemaItems.push(item);
        setActiveSchemaId(item.id);
        setSchemaMode('custom');
        setSchemaType('none');
        saveSchemaItems();
        renderSchemaLists();
        updateSchemaUI();
        $('#schema-custom-json-output').text(custom || seoFuryData.i18n.pasteJsonLd);
        $('#seo_fury_schema_custom').val('');
    });

    // Open editor modal when clicking edit icon inside Schema Generator modal
    $(document).on('click', '#seo-fury-schema-modal .seo-fury-schema-edit', function(e) {
        e.preventDefault();
        e.stopPropagation();
        var schemaId = $(this).data('schema-id');
        var item = schemaItems.find(function(i) { return i.id === schemaId; });
        if (item && item.mode === 'custom_builder') {
            openBuilderForEdit(schemaId);
        } else if (item && item.type === 'faq' && item.mode === 'generator') {
            maybeImportGutenbergFaq(schemaId);
        } else {
            openSchemaEditorModal(schemaId, 'editor');
        }
    });

    // Open editor modal when clicking edit icon in sidebar
    $(document).on('click', '#tab-schema .seo-fury-schema-edit', function(e) {
        e.preventDefault();
        e.stopPropagation();
        var schemaId = $(this).data('schema-id');
        var item = schemaItems.find(function(i) { return i.id === schemaId; });
        if (item && item.mode === 'custom_builder') {
            openBuilderForEdit(schemaId);
        } else if (item && item.type === 'faq' && item.mode === 'generator') {
            maybeImportGutenbergFaq(schemaId);
        } else {
            openSchemaEditorModal(schemaId, 'editor');
        }
    });

    // Open preview modal when clicking preview icon
    $(document).on('click', '.seo-fury-schema-preview', function(e) {
        e.preventDefault();
        e.stopPropagation();
        var schemaId = $(this).data('schema-id');
        openSchemaEditorModal(schemaId, 'preview');
    });

    // Editor modal tab switching
    $(document).on('click', '.seo-fury-modal-tab[data-editor-tab]', function() {
        var tab = $(this).data('editor-tab');
        var $tab = $(this);

        // If leaving the advanced tab and the builder was modified, persist builder data
        // so that switching back to advanced restores the user's work.
        var currentTab = $('.seo-fury-modal-tab[data-editor-tab].active').data('editor-tab');
        if (currentTab === 'advanced' && tab !== 'advanced' && builderModified) {
            collectEditorBuilderData();
        }

        // For advanced tab: collect editor form data BEFORE switching tabs.
        // Doing it before addClass('active') ensures collectEditorFieldData() sees
        // the editor tab (not advanced) as active, so it reads from the form inputs
        // (not the builder), correctly capturing array fields like sameAs/knowsAbout.
        if (tab === 'advanced') {
            collectEditorFieldData();
        }

        $('.seo-fury-modal-tab[data-editor-tab]').removeClass('active');
        $('.seo-fury-modal-panel[data-editor-panel]').removeClass('active');

        $tab.addClass('active');
        $('.seo-fury-modal-panel[data-editor-panel="' + tab + '"]').addClass('active');

        if (tab === 'preview') {
            collectEditorFieldData();
            fetchResolvedSchemaPreview();
        } else if (tab === 'advanced') {
            initEditorBuilder();
        }
    });

    // Save button
    $(document).on('click', '.schema-editor-save', function() {
        collectEditorFieldData();

        // Update item in schemaItems array
        var itemIndex = schemaItems.findIndex(function(i) { return i.id === editingSchemaId; });
        if (itemIndex !== -1) {
            schemaItems[itemIndex] = editingSchemaData;
            saveSchemaItems();
            renderSchemaLists();
        }

        // Notify external listeners (e.g. template editor) of the saved schema data
        $(document).trigger('seo-fury:schema-editor-saved', [editingSchemaId, JSON.parse(JSON.stringify(editingSchemaData))]);

        // Close modal
        closeSchemaEditorModal();
    });

    // Cancel button and close button
    $(document).on('click', '.schema-editor-cancel, .schema-editor-modal-close', function() {
        closeSchemaEditorModal();
    });

    // Close on overlay click
    $(document).on('click', '#seo-fury-schema-editor-modal .seo-fury-modal-overlay', function() {
        closeSchemaEditorModal();
    });

    // Publisher: "Use Organization markup from homepage" toggle
    $(document).on('change', '#schema_publisher_use_org', function() {
        var checked = $(this).is(':checked');
        // Use closest grid for context-aware lookup (works in both original and cloned modal forms)
        var $grid = $(this).closest('.seo-fury-fields-grid');
        $grid.find('#sf-publisher-manual-fields').toggle(!checked);
        $grid.find('.sf-publisher-org-preview').toggle(checked);
    });

    // Schema optional field toggles (sameAs, knowsAbout, alumniOf, hasCredential)
    $(document).on('click', '.sf-opt-toggle-btn', function(e) {
        e.preventDefault();
        var $btn = $(this);
        var $body = $btn.closest('.sf-opt-item').find('.sf-opt-body');
        $btn.toggleClass('active');
        $body.slideToggle(180);
    });

    // Array field: add new row
    $(document).on('click', '.sf-array-add-btn', function(e) {
        e.preventDefault();
        var $btn = $(this);
        var fieldName = $btn.data('field-name');
        var placeholder = $btn.data('placeholder') || '';
        var $list = $btn.siblings('.sf-array-list');
        if (!$list.length) $list = $btn.closest('.sf-opt-body').find('.sf-array-list');
        $list.append(buildArrayRow(fieldName, '', placeholder));
        $list.find('.sf-array-row:last-child input').focus();
    });

    // Array field: remove row
    $(document).on('click', '.sf-array-remove-btn', function(e) {
        e.preventDefault();
        $(this).closest('.sf-array-row').remove();
    });

    // Article Schema — "Set custom date" / "Use WordPress date" toggles
    $(document).on('click', '.sf-schema-date-custom-btn', function(e) {
        e.preventDefault();
        var $field = $(this).closest('.sf-schema-date-field');
        var wpDate = $field.data('wp-date') || '';
        $field.find('.sf-date-auto-row').hide();
        $field.find('.sf-date-custom-row').show();
        $field.find('input[type="datetime-local"]').val(wpDate).focus();
    });

    $(document).on('click', '.sf-schema-date-wp-btn', function(e) {
        e.preventDefault();
        var $field = $(this).closest('.sf-schema-date-field');
        $field.find('.sf-date-custom-row').hide();
        $field.find('.sf-date-auto-row').show();
        $field.find('input[type="datetime-local"]').val('');
    });

    // FAQ: Add new FAQ item
    $(document).on('click', '#add-faq-item', function(e) {
        e.preventDefault();
        var $container = $(this).siblings('#faq-items-container');
        if (!$container.length) {
            $container = $(this).closest('.seo-fury-section').find('#faq-items-container');
        }
        var newIndex = $container.find('.faq-item').length;
        var questionLabel = (seoFuryData.i18n && seoFuryData.i18n.question) || 'Question';
        var answerLabel = (seoFuryData.i18n && seoFuryData.i18n.answer) || 'Answer';
        var removeLabel = (seoFuryData.i18n && seoFuryData.i18n.remove) || 'Remove';
        var questionPlaceholder = (seoFuryData.i18n && seoFuryData.i18n.questionPlaceholder) || 'What is your question?';
        var answerPlaceholder = (seoFuryData.i18n && seoFuryData.i18n.answerPlaceholder) || 'Type the answer here...';

        var html = '<div class="faq-item" data-index="' + newIndex + '">' +
            '<div class="seo-fury-field">' +
                '<label>' + questionLabel + ' ' + (newIndex + 1) + '</label>' +
                '<input type="text" name="schema_data[faq_items][' + newIndex + '][question]" value="" placeholder="' + questionPlaceholder + '">' +
            '</div>' +
            '<div class="seo-fury-field">' +
                '<label>' + answerLabel + '</label>' +
                '<textarea name="schema_data[faq_items][' + newIndex + '][answer]" rows="3" placeholder="' + answerPlaceholder + '"></textarea>' +
            '</div>' +
            '<button type="button" class="button remove-faq-item">' + removeLabel + '</button>' +
            '<hr style="margin: 15px 0;">' +
        '</div>';

        $container.append(html);
    });

    // FAQ: Remove FAQ item
    $(document).on('click', '.remove-faq-item', function(e) {
        e.preventDefault();
        var $item = $(this).closest('.faq-item');
        var $container = $item.closest('#faq-items-container');
        $item.remove();

        // Re-index remaining items
        $container.find('.faq-item').each(function(i) {
            $(this).attr('data-index', i);
            $(this).find('label').first().text(((seoFuryData.i18n && seoFuryData.i18n.question) || 'Question') + ' ' + (i + 1));
            $(this).find('input[name*="faq_items"]').attr('name', 'schema_data[faq_items][' + i + '][question]');
            $(this).find('textarea[name*="faq_items"]').attr('name', 'schema_data[faq_items][' + i + '][answer]');
        });
    });

    // HowTo: Add new step
    $(document).on('click', '#add-howto-step', function(e) {
        e.preventDefault();
        var $container = $(this).siblings('#howto-steps-container');
        if (!$container.length) {
            $container = $(this).closest('.seo-fury-section').find('#howto-steps-container');
        }
        var newIndex = $container.find('.howto-step').length;
        var html = '<div class="howto-step" data-index="' + newIndex + '">' +
            '<div class="seo-fury-field">' +
                '<label>Step ' + (newIndex + 1) + ' Name</label>' +
                '<input type="text" name="schema_data[steps][' + newIndex + '][name]" value="">' +
            '</div>' +
            '<div class="seo-fury-field">' +
                '<label>Step Description</label>' +
                '<textarea name="schema_data[steps][' + newIndex + '][text]" rows="2"></textarea>' +
            '</div>' +
            '<button type="button" class="button remove-howto-step">Remove Step</button>' +
            '<hr style="margin: 15px 0;">' +
        '</div>';
        $container.append(html);
    });

    // HowTo: Remove step and re-index
    $(document).on('click', '.remove-howto-step', function(e) {
        e.preventDefault();
        var $item = $(this).closest('.howto-step');
        var $container = $item.closest('#howto-steps-container');
        $item.remove();
        $container.find('.howto-step').each(function(i) {
            $(this).attr('data-index', i);
            $(this).find('label').first().text('Step ' + (i + 1) + ' Name');
            $(this).find('input[name*="steps"]').attr('name', 'schema_data[steps][' + i + '][name]');
            $(this).find('textarea[name*="steps"]').attr('name', 'schema_data[steps][' + i + '][text]');
        });
    });

    $(document).on('click', '.seo-fury-schema-card', function (e) {
        if ($(e.target).closest('.seo-fury-schema-actions').length) {
            return;
        }
        if ($(e.target).closest('.seo-fury-schema-drag').length) {
            return;
        }
        var id = $(this).data('schema-id');
        if (!id) {
            return;
        }
        storeActiveSchemaData();
        setActiveSchemaId(id);
        var item = getActiveSchemaItem();
        applyActiveSchemaItem(item);
        saveSchemaItems();
        renderSchemaLists();
        openSchemaModal(item && item.mode === 'custom' ? 'import' : 'templates', item && item.mode !== 'custom');
    });

    $(document).on('click', '.seo-fury-schema-remove', function () {
        var id = $(this).data('schema-id');
        schemaItems = schemaItems.filter(function (item) { return item.id !== id; });
        if (schemaItems.length) {
            setActiveSchemaId(schemaItems[0].id);
        } else {
            setActiveSchemaId('');
            setSchemaMode('generator');
            setSchemaType('none');
        }
        saveSchemaItems();
        renderSchemaLists();
        applyActiveSchemaItem(getActiveSchemaItem());
        updateSchemaUI();
    });


    $(document).on('input', '#seo_fury_schema_custom', function () {
        var value = $(this).val() || seoFuryData.i18n.pasteJsonLd;
        $('#seo-fury-schema-error').hide().text('');
        $('#schema-custom-json-output').text(value);
    });

    $(document).on('input change', '[name^="schema_data["]', function () {
        debouncedSchemaPreview();
    });

    $(document).on('submit', '#post', function () {
        storeActiveSchemaData();
        saveSchemaItems();
    });

    if (typeof wp !== 'undefined' && wp.data && wp.data.subscribe) {
        var lastTitle = getPostTitle();
        var lastExcerpt = getPostExcerpt();
        var lastContent = getPostContent();
        var lastSlug = getPostSlug();
        wp.data.subscribe(function () {
            var nextTitle = getPostTitle();
            var nextExcerpt = getPostExcerpt();
            var nextContent = getPostContent();
            var nextSlug = getPostSlug();
            if (nextTitle !== lastTitle || nextExcerpt !== lastExcerpt || nextContent !== lastContent || nextSlug !== lastSlug) {
                lastTitle = nextTitle;
                lastExcerpt = nextExcerpt;
                lastContent = nextContent;
                lastSlug = nextSlug;
                debouncedCharCount();
                debouncedGooglePreview();
                debouncedOgPreview();
                debouncedFacebookPreview();
                debouncedTwitterPreview();
                debouncedSeoAnalysis();
                debouncedReadability();
                debouncedExtraSeo();
            }
        });
    }

    // SEO plugin JS initialized

    $(document).on('click', '.shortcode-dropdown-btn', function (e) {
        e.preventDefault();
        e.stopPropagation();

        var $btn = $(this);
        var $menu = $btn.siblings('.shortcode-dropdown-menu');
        var targetId = $btn.data('target');

        $('.shortcode-dropdown-menu').not($menu).removeClass('active');
        $('.shortcode-dropdown-btn').not($btn).removeClass('active');

        $btn.toggleClass('active');
        $menu.toggleClass('active');

        // Reset search when opening
        if ($menu.hasClass('active')) {
            var $search = $menu.find('.sf-shortcode-search');
            $search.val('');
            $menu.find('.shortcode-item').show();
            setTimeout(function() { $search.focus(); }, 50);
        }

        // Поддержка полей с ID и без (для конструктора)
        if (targetId) {
            $('#' + targetId).focus();
        } else {
            var $dropdown = $btn.closest('.input-with-dropdown');
            $dropdown.find('input, textarea').first().focus();
        }
    });

    $(document).on('click', '.shortcode-item', function (e) {
        e.preventDefault();
        e.stopPropagation();

        var variable = $(this).data('var');
        var $menu = $(this).closest('.shortcode-dropdown-menu');
        var $dropdown = $menu.closest('.input-with-dropdown');
        var $btn = $dropdown.find('.shortcode-dropdown-btn');
        var targetId = $btn.data('target');
        var $field;

        // Поддержка полей с ID и без (для конструктора)
        if (targetId) {
            $field = $('#' + targetId);
        } else {
            $field = $dropdown.find('input, textarea').first();
        }

        if ($field.length) {
            var cursorPos = $field[0].selectionStart || 0;
            var currentVal = $field.val();

            var valueToInsert = variable + ' ';
            var newVal = currentVal.substring(0, cursorPos) + valueToInsert + currentVal.substring(cursorPos);

            $field.val(newVal);

            var newCursorPos = cursorPos + valueToInsert.length;
            $field[0].setSelectionRange(newCursorPos, newCursorPos);

            $field.trigger('input');
            $field.focus();
        }
    });


    $(document).on('click', function (e) {
        if (!$(e.target).closest('.input-with-dropdown').length) {
            $('.shortcode-dropdown-menu').removeClass('active');
            $('.shortcode-dropdown-btn').removeClass('active');
        }
    });

    $(document).on('keydown', function (e) {
        if (e.key === 'Escape') {
            $('.shortcode-dropdown-menu').removeClass('active');
            $('.shortcode-dropdown-btn').removeClass('active');
        }
    });

    // Shortcode dropdown search filter (post-meta context)
    $(document).on('input', '.shortcode-dropdown-menu .sf-shortcode-search', function () {
        var query = $(this).val().toLowerCase();
        $(this).closest('.shortcode-dropdown-menu').find('.shortcode-item').each(function () {
            var text = $(this).text().toLowerCase();
            var varName = ($(this).data('var') || '').toLowerCase();
            $(this).toggle(text.indexOf(query) !== -1 || varName.indexOf(query) !== -1);
        });
    });

    // Prevent search input clicks from closing the dropdown
    $(document).on('click', '.sf-shortcode-search', function (e) {
        e.stopPropagation();
    });

    $('#seo-fury-scan-links').on('click', function () {
        var $btn = $(this);
        var $loading = $('#seo-fury-link-loading');
        var $suggestions = $('#seo-fury-link-suggestions');
        var $empty = $('#seo-fury-link-scan-empty');
        var $orphans = $('#seo-fury-orphan-pages');

        $btn.prop('disabled', true).text(seoFuryData.i18n.scanning);
        $loading.show();
        $suggestions.hide();
        $empty.hide();
        $orphans.hide();
        $('#seo-fury-broken-links').remove();

        $.ajax({
            url: seoFuryData.ajaxUrl,
            type: 'POST',
            data: {
                action: 'seo_fury_scan_internal_links',
                nonce: seoFuryData.internalLinksNonce,
                post_id: seoFuryData.postId,
                force: '1'
            },
            success: function (response) {
                $loading.hide();
                $btn.prop('disabled', false).text(seoFuryData.i18n.scanContent);
                linksScanDone = true;

                if (!response.success) return;

                var data = response.data;
                $('#sf-stat-internal').text(data.stats.internal);
                $('#sf-stat-external').text(data.stats.external);
                $('#sf-stat-suggestions').text(data.suggestions.length);

                if (data.suggestions.length > 0) {
                    var html = '';
                    var _i18n = seoFuryData.i18n || {};
                    var exactLabel  = _i18n.matchExact  || 'Exact';
                    var partialLabel = _i18n.matchPartial || 'Partial';
                    var wordLabel   = _i18n.matchWord    || 'Word';
                    var findTip     = _i18n.findInText   || 'Find in text';
                    var copyTip     = _i18n.copyUrl      || 'Copy URL';
                    var linkTip     = _i18n.insertLinkTip || 'Insert link';
                    var sfBtnStyle  = 'border:none;background:none;cursor:pointer;padding:2px 5px;font-size:14px;line-height:1;border-radius:4px;opacity:0.65;';
                    var sfBtnHover  = 'onmouseover="this.style.opacity=1;this.style.background=\'#f1f5f9\'" onmouseout="this.style.opacity=0.65;this.style.background=\'none\'"';

                    for (var i = 0; i < data.suggestions.length; i++) {
                        var s = data.suggestions[i];
                        var badgeLabel = s.match_type === 'exact' ? exactLabel : (s.match_type === 'word' ? wordLabel : partialLabel);
                        var badgeColor = s.match_type === 'exact' ? 'background:#ECFDF5;color:#10B981;' : 'background:#FEF3C7;color:#F59E0B;';
                        var badge = '<span style="' + badgeColor + 'font-size:9px;padding:1px 6px;border-radius:4px;white-space:nowrap;">' + badgeLabel + '</span>';
                        var shortUrl = s.target_url.replace(location.origin, '');
                        var safeUrl  = $('<span>').text(s.target_url).html();
                        var safeText = $('<span>').text(s.match_text).html();

                        html += '<div style="padding:6px 8px;border:1px solid var(--sf-gray-200,#E2E8F0);border-radius:6px;margin-bottom:5px;font-size:12px;">';
                        html += '<div style="display:flex;justify-content:space-between;align-items:center;gap:6px;">';
                        html += '<div style="min-width:0;flex:1;">';
                        html += '<div style="display:flex;align-items:center;gap:5px;margin-bottom:2px;"><strong style="color:var(--sf-gray-900,#1E293B);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">&ldquo;' + safeText + '&rdquo;</strong>' + badge + '</div>';
                        html += '<div style="color:var(--sf-gray-500,#64748B);font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="' + safeUrl + '">' + $('<span>').text(s.target_title).html() + ' <span style="opacity:0.5;">(' + s.post_type + ')</span></div>';
                        html += '</div>';
                        html += '<div style="display:flex;gap:1px;flex-shrink:0;">';
                        html += '<button type="button" class="sf-find-text" data-text="' + safeText + '" style="' + sfBtnStyle + '" ' + sfBtnHover + ' title="' + findTip + '">&#x1f50d;</button>';
                        html += '<button type="button" class="sf-copy-link" data-url="' + safeUrl + '" style="' + sfBtnStyle + '" ' + sfBtnHover + ' title="' + copyTip + '">&#x1f4cb;</button>';
                        html += '<button type="button" class="sf-insert-link" data-url="' + safeUrl + '" data-text="' + safeText + '" style="' + sfBtnStyle + 'color:#4F46E5;opacity:0.85;" ' + sfBtnHover + ' title="' + linkTip + '">&#x1f517;</button>';
                        html += '</div></div></div>';
                    }
                    $('#seo-fury-suggestions-list').html(html);
                    $suggestions.show();
                } else {
                    $empty.show();
                }

                if (data.orphan_pages && data.orphan_pages.length > 0) {
                    var ohtml = '';
                    for (var j = 0; j < data.orphan_pages.length; j++) {
                        var op = data.orphan_pages[j];
                        ohtml += '<div style="font-size:12px;padding:4px 0;border-bottom:1px solid var(--sf-gray-100,#F1F5F9);">';
                        ohtml += '<a href="' + op.url + '" target="_blank" style="color:var(--sf-primary,#4F46E5);text-decoration:none;">' + $('<span>').text(op.title).html() + '</a>';
                        ohtml += ' <span style="color:var(--sf-gray-400);font-size:10px;">(' + op.post_type + ')</span>';
                        ohtml += '</div>';
                    }
                    $('#seo-fury-orphan-list').html(ohtml);
                    $orphans.show();
                }

                // Broken links section
                if (data.broken_links && data.broken_links.length > 0) {
                    var bhtml = '<div id="seo-fury-broken-links" style="margin-top:12px;padding:10px;background:#FEF2F2;border:1px solid #FECACA;border-radius:6px;">';
                    bhtml += '<h5 style="margin:0 0 6px;font-size:12px;font-weight:600;color:#DC2626;">';
                    bhtml += '<span class="dashicons dashicons-warning" style="font-size:14px;width:14px;height:14px;margin-right:4px;"></span>';
                    bhtml += (seoFuryData.i18n.brokenLinks || 'Broken Links') + ' (' + data.broken_links.length + ')';
                    bhtml += '</h5>';
                    for (var k = 0; k < data.broken_links.length; k++) {
                        bhtml += '<div style="font-size:11px;padding:2px 0;color:#991B1B;">';
                        bhtml += '<span class="dashicons dashicons-no" style="font-size:12px;width:12px;height:12px;color:#EF4444;"></span> ';
                        bhtml += $('<span>').text(data.broken_links[k]).html();
                        bhtml += '</div>';
                    }
                    bhtml += '</div>';
                    $('#tab-links').append(bhtml);
                }
            },
            error: function () {
                $loading.hide();
                $btn.prop('disabled', false).text(seoFuryData.i18n.scanContent);
            }
        });
    });

    // ========================================
    // Highlight / Find text in editor
    // ========================================
    var SF_HL_STYLE = 'background:#FBBF24;color:#1E293B;padding:2px 4px;border-radius:3px;outline:2px solid #F59E0B;';

    function clearHighlights(rootEl) {
        if (!rootEl) return;
        var scope = rootEl.ownerDocument || rootEl;
        var marks = (scope.querySelectorAll ? scope : rootEl).querySelectorAll('mark.sf-link-highlight');
        for (var i = marks.length - 1; i >= 0; i--) {
            var m = marks[i];
            var parent = m.parentNode;
            while (m.firstChild) parent.insertBefore(m.firstChild, m);
            parent.removeChild(m);
            parent.normalize();
        }
    }

    /** Walk up parents to check if node is inside <a> tag. */
    function sfIsInsideLink(el, boundary) {
        var n = el;
        while (n && n !== boundary) {
            if (n.tagName === 'A') return true;
            n = n.parentNode;
        }
        return false;
    }

    /** Unicode-aware word char check. */
    function sfIsWordChar(ch) {
        return ch ? /[\p{L}\p{N}_]/u.test(ch) : false;
    }

    /**
     * Build a flexible regex from search text that tolerates different types
     * of dashes/hyphens/spaces. "Интернет-маркетинг" will match
     * "Интернет–маркетинг", "Интернет маркетинг", "Интернет‑маркетинг" etc.
     */
    function sfBuildFlexiRegex(text) {
        // Split on any dash/hyphen/space characters.
        var parts = text.split(/[\s\u002D\u2010\u2011\u2012\u2013\u2014\u2015\u2212\u00AD\uFE58\uFE63\uFF0D]+/);
        var escaped = [];
        for (var i = 0; i < parts.length; i++) {
            if (parts[i].length > 0) {
                escaped.push(parts[i].replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
            }
        }
        if (escaped.length === 0) return null;
        // Join with flexible separator: any combo of dashes, hyphens, spaces (0 or more).
        var sep = '[\\s\\u002D\\u2010-\\u2015\\u2212\\u00AD\\uFE58\\uFE63\\uFF0D]*';
        return new RegExp(escaped.join(sep), 'iu');
    }

    /**
     * Check if a DOM node lives inside a non-linkable area:
     * nav / breadcrumb / menu / headings (H1-H6).
     * Links should not be placed in headings or navigation.
     */
    function sfIsInNavArea(node, rootEl) {
        var el = node.parentNode;
        while (el && el !== rootEl) {
            if (el.nodeType !== 1) { el = el.parentNode; continue; }
            var tag = el.tagName;
            if (tag === 'NAV' || tag === 'HEADER') return true;
            if (tag === 'H1' || tag === 'H2' || tag === 'H3' || tag === 'H4' || tag === 'H5' || tag === 'H6') return true;
            var cls = (typeof el.className === 'string') ? el.className : '';
            if (/\b(breadcrumb|elementor-widget-nav-menu|elementor-widget-breadcrumbs|nav-menu|main-menu|site-nav|site-header|mega-menu)\b/i.test(cls)) return true;
            var role = el.getAttribute ? el.getAttribute('role') : null;
            if (role === 'navigation' || role === 'banner') return true;
            el = el.parentNode;
        }
        return false;
    }

    /**
     * Search and highlight text inside rootEl.
     * - Collects ALL text nodes into a flat string (handles Elementor nested spans).
     * - Skips text inside nav / breadcrumb / menu areas.
     * - Three-tier matching: exact word-boundary → plain substring → flexible regex.
     * - Wraps matched text node portions in <mark>.
     */
    function highlightTextInElement(rootEl, searchText) {
        if (!rootEl || !searchText) return false;
        var doc = rootEl.ownerDocument || document;
        clearHighlights(rootEl);

        // 1. Collect text nodes (skip SCRIPT/STYLE and nav/breadcrumb areas).
        var walker = doc.createTreeWalker(rootEl, NodeFilter.SHOW_TEXT, null, false);
        var tNodes = [];
        var flat = '';
        while (walker.nextNode()) {
            var n = walker.currentNode;
            var p = n.parentNode;
            if (!p) continue;
            var tag = p.tagName;
            if (tag === 'SCRIPT' || tag === 'STYLE' || tag === 'NOSCRIPT') continue;
            if (sfIsInNavArea(n, rootEl)) continue;
            tNodes.push({ node: n, start: flat.length, length: n.nodeValue.length });
            flat += n.nodeValue;
        }

        if (!flat.length) return false;

        var flatL = flat.toLowerCase();
        var searchL = searchText.toLowerCase().trim();
        if (!searchL.length) return false;

        var matchIdx = -1;
        var matchLen = searchL.length;

        // 2a. Try exact word-boundary match.
        var from = 0;
        while (true) {
            var idx = flatL.indexOf(searchL, from);
            if (idx === -1) break;
            var bOk = (idx === 0) || !sfIsWordChar(flatL.charAt(idx - 1));
            var aPos = idx + searchL.length;
            var aOk = (aPos >= flatL.length) || !sfIsWordChar(flatL.charAt(aPos));
            if (bOk && aOk) { matchIdx = idx; matchLen = searchL.length; break; }
            from = idx + 1;
        }

        // 2b. Fallback: plain substring.
        if (matchIdx === -1) {
            var plainIdx = flatL.indexOf(searchL);
            if (plainIdx !== -1) { matchIdx = plainIdx; matchLen = searchL.length; }
        }

        // 2c. Flexible regex: handles different dashes/hyphens/spaces.
        //     e.g. "Интернет-маркетинг" matches "Интернет–маркетинг" or "Интернет маркетинг".
        if (matchIdx === -1) {
            var flexi = sfBuildFlexiRegex(searchL);
            if (flexi) {
                var m = flexi.exec(flatL);
                if (m) { matchIdx = m.index; matchLen = m[0].length; }
            }
        }

        if (matchIdx === -1) return false;

        var matchEnd = matchIdx + matchLen;
        var firstMark = null;

        // 3. Highlight overlapping text node portions.
        for (var i = 0; i < tNodes.length; i++) {
            var tn = tNodes[i];
            var tnEnd = tn.start + tn.length;
            var os = Math.max(tn.start, matchIdx);
            var oe = Math.min(tnEnd, matchEnd);
            if (os >= oe) continue;

            var ns = os - tn.start;
            var ne = oe - tn.start;
            var val = tn.node.nodeValue;

            var mark = doc.createElement('mark');
            mark.className = 'sf-link-highlight';
            mark.style.cssText = SF_HL_STYLE;
            mark.textContent = val.substring(ns, ne);

            var par = tn.node.parentNode;
            var after = val.substring(ne);
            if (after) par.insertBefore(doc.createTextNode(after), tn.node.nextSibling);
            par.insertBefore(mark, tn.node.nextSibling);
            tn.node.nodeValue = val.substring(0, ns);

            if (!firstMark) firstMark = mark;
            if (oe >= matchEnd) break;
        }

        if (firstMark) {
            firstMark.scrollIntoView({ behavior: 'smooth', block: 'center' });
            setTimeout(function () { clearHighlights(rootEl); }, 6000);
            return true;
        }
        return false;
    }

    /**
     * Get Elementor preview iframe document (multiple strategies).
     * Returns the document object, not the iframe element.
     */
    function getElPreviewDoc() {
        var iframe = null;
        // Strategy 1: Elementor JS API.
        if (typeof elementor !== 'undefined' && elementor.$preview && elementor.$preview.length) {
            iframe = elementor.$preview[0];
        }
        // Strategy 2: ID lookup.
        if (!iframe) iframe = document.getElementById('elementor-preview-iframe');
        // Strategy 3: querySelector.
        if (!iframe) iframe = document.querySelector('#elementor-preview-responsive-wrapper iframe, .elementor-preview-responsive-wrapper iframe');
        if (!iframe) return null;
        try {
            var doc = iframe.contentDocument || (iframe.contentWindow ? iframe.contentWindow.document : null);
            return doc || null;
        } catch (e) {
            return null;
        }
    }

    /**
     * Get ALL possible content containers in Elementor preview, ordered by priority.
     * Returns an array — we try each one until text is found.
     * This handles Theme Builder setups where the post's Elementor widgets (.elementor-{postId})
     * and the WP post body (inside .elementor-widget-theme-post-content) are different containers.
     */
    function getElContentContainers(elDoc) {
        var containers = [];
        var postId = (typeof seoFuryData !== 'undefined') ? (seoFuryData.postId || seoFuryData.post_id || '') : '';
        var seen = [];

        function add(el) {
            if (el && seen.indexOf(el) === -1) { containers.push(el); seen.push(el); }
        }
        function addAll(nodeList) {
            for (var i = 0; i < nodeList.length; i++) add(nodeList[i]);
        }

        // 1. Post's own Elementor container.
        if (postId) {
            add(elDoc.querySelector('.elementor-' + postId));
            add(elDoc.querySelector('[data-elementor-id="' + postId + '"]'));
        }

        // 2. Post Content widgets from ALL major theme builders / addons.
        //    Each addon uses its own widget class. Cover them all.
        var postContentSels = [
            // Elementor Pro
            '.elementor-widget-theme-post-content',
            // JetThemeCore / JetEngine (Crocoblock)
            '.elementor-widget-jet-post-content',
            '.elementor-widget-jet-single-content',
            '.elementor-widget-jet-listing-dynamic-field',
            // Older Elementor / third-party
            '.elementor-widget-post-content',
            '.elementor-widget-theme-builder-content',
            // Generic: any widget with "post-content" in data-widget_type
            '[data-widget_type*="post-content"]',
            '[data-widget_type*="single-content"]',
            '[data-widget_type*="the_content"]'
        ];
        for (var s = 0; s < postContentSels.length; s++) {
            // Try both the widget itself and .elementor-widget-container inside it.
            var widgets = elDoc.querySelectorAll(postContentSels[s]);
            for (var w = 0; w < widgets.length; w++) {
                var inner = widgets[w].querySelector('.elementor-widget-container');
                add(inner || widgets[w]);
            }
        }

        // 3. Content-type Elementor sections.
        var mainSels = [
            '.elementor[data-elementor-type="wp-post"]',
            '.elementor[data-elementor-type="wp-page"]',
            '.elementor[data-elementor-type="single"]',
            '.elementor[data-elementor-type="single-post"]',
            '.elementor[data-elementor-type="single-page"]',
            '.elementor[data-elementor-type="page"]',
            '.elementor[data-elementor-type="product"]'
        ];
        add(elDoc.querySelector(mainSels.join(',')));

        // 4. Any .elementor except non-content types (header/footer/popup).
        var skip = ['header','footer','popup','loop-item','archive','search-results','error-404'];
        var all = elDoc.querySelectorAll('.elementor[data-elementor-type]');
        for (var j = 0; j < all.length; j++) {
            if (skip.indexOf(all[j].getAttribute('data-elementor-type')) === -1) add(all[j]);
        }

        // 5. Standard WP content selectors.
        add(elDoc.querySelector('.entry-content, .post-content, article.post, article.page, .site-content, #content'));

        // 6. Elementor edit area.
        add(elDoc.querySelector('.elementor-edit-area'));

        // 7. LAST RESORT: entire body — only reached if no specific container has the text.
        //    This covers cases where the Post Content widget uses an unknown class name.
        add(elDoc.body);

        return containers;
    }

    /**
     * Attempt to highlight text in the currently active editor.
     * Tries MULTIPLE containers in Elementor (post widgets + Post Content widget).
     */
    function findTextInEditor(searchText) {
        // 1. Elementor preview — try all content containers.
        var elDoc = getElPreviewDoc();
        if (elDoc) {
            try {
                var containers = getElContentContainers(elDoc);
                for (var ci = 0; ci < containers.length; ci++) {
                    if (highlightTextInElement(containers[ci], searchText)) return true;
                }
            } catch (e) { console.warn('SEO Fury: el-highlight err', e); }
        }
        // 2. Gutenberg iframe (WP 5.9+).
        var gbIframe = document.querySelector('iframe[name="editor-canvas"]');
        if (gbIframe) {
            try {
                var gbDoc = gbIframe.contentDocument || gbIframe.contentWindow.document;
                var gbRoot = gbDoc.querySelector('.editor-styles-wrapper') || gbDoc.body;
                if (gbRoot && highlightTextInElement(gbRoot, searchText)) return true;
            } catch (e) { console.warn('SEO Fury: gb-highlight err', e); }
        }
        // 3. Gutenberg without iframe.
        var gbEditor = document.querySelector('.editor-styles-wrapper');
        if (gbEditor && highlightTextInElement(gbEditor, searchText)) return true;
        // 4. TinyMCE.
        if (typeof tinymce !== 'undefined' && tinymce.activeEditor && !tinymce.activeEditor.isHidden()) {
            var tinyDoc = tinymce.activeEditor.getDoc();
            if (tinyDoc && tinyDoc.body && highlightTextInElement(tinyDoc.body, searchText)) return true;
        }
        return false;
    }

    /**
     * Find-in-text button click — with retry mechanism for iframe timing.
     */
    $(document).on('click', '.sf-find-text', function () {
        var text = $(this).data('text');
        var $btn = $(this);
        // Immediate attempt.
        var found = findTextInEditor(text);
        if (found) {
            $btn.html('&#x2705;');
            setTimeout(function () { $btn.html('&#x1f50d;'); }, 3000);
            return;
        }
        // Retry chain: 300ms, 800ms, 1500ms — iframe may still be rendering.
        $btn.html('&#x23f3;'); // hourglass
        var delays = [300, 800, 1500];
        var attempt = 0;
        function tryAgain() {
            if (attempt >= delays.length) {
                $btn.html('&#x274c;');
                setTimeout(function () { $btn.html('&#x1f50d;'); }, 2500);
                return;
            }
            setTimeout(function () {
                var ok = findTextInEditor(text);
                if (ok) {
                    $btn.html('&#x2705;');
                    setTimeout(function () { $btn.html('&#x1f50d;'); }, 3000);
                } else {
                    attempt++;
                    tryAgain();
                }
            }, delays[attempt]);
        }
        tryAgain();
    });

    // Copy link handler
    $(document).on('click', '.sf-copy-link', function () {
        var url = $(this).data('url');
        var $btn = $(this);
        if (navigator.clipboard) {
            navigator.clipboard.writeText(url).then(function () {
                $btn.html('&#x2713;').css('color', '#22C55E');
                setTimeout(function () { $btn.html('&#x1f4cb;').css('color', ''); }, 1500);
            });
        }
    });

    /* ========================================
       SCHEMA CUSTOM BUILDER — Bridge to schema-builder.js
    ======================================== */

    // Expose callbacks so schema-builder.js can interact with the schema manager
    window._seoFurySchemaCallbacks = {
        storeActiveSchemaData: storeActiveSchemaData,
        pushItem: function (item) { schemaItems.push(item); },
        setActiveSchemaId: setActiveSchemaId,
        applyActiveSchemaItem: applyActiveSchemaItem,
        saveSchemaItems: saveSchemaItems,
        renderSchemaLists: renderSchemaLists,
        setSchemaMode: setSchemaMode,
        getBuilderEditingId: function () { return builderEditingId; },
        setBuilderEditingId: function (id) { builderEditingId = id; },
        updateBuilderItem: function (id, newItemData) {
            var idx = schemaItems.findIndex(function (i) { return i.id === id; });
            if (idx !== -1) {
                newItemData.id = id;
                schemaItems[idx] = newItemData;
            }
        }
    };

    // Initialize the external schema builder module
    if (window.SeoFurySchemaBuilder) {
        window.SeoFurySchemaBuilder.init();
    }

    /**
     * Insert a link into Elementor's in-memory Backbone models (client-side only).
     * The change stays in memory until the user presses Save/Publish.
     * Returns true if link was inserted, false otherwise.
     */
    function insertLinkInElementorModels(linkUrl, anchorText) {
        if (typeof elementor === 'undefined' || !elementor.elements) return false;

        var escaped = anchorText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
        var regex = new RegExp('(' + escaped + ')(?![^<]*<\\/a>)', 'iu');
        var found = false;
        var fields = ['editor', 'title', 'description', 'text', 'content', 'html'];
        var skipWidgets = ['nav-menu','wp-menu','navigation','mega-menu','breadcrumbs',
            'site-logo','site-title','sitemap','menu-anchor','sidebar',
            'wp-widget-nav_menu','global','template','theme-post-navigation',
            'post-navigation','share-buttons','social-icons'];

        function walk(collection) {
            if (found || !collection || !collection.each) return;
            collection.each(function (model) {
                if (found) return;
                var wt = model.get('widgetType');
                if (wt && skipWidgets.indexOf(wt) !== -1) return;
                var settings = model.get('settings');
                if (settings) {
                    for (var i = 0; i < fields.length; i++) {
                        var val = settings.get(fields[i]);
                        if (typeof val === 'string' && regex.test(val)) {
                            settings.set(fields[i], val.replace(regex, '<a href="' + linkUrl + '">$1</a>'));
                            found = true;
                            return;
                        }
                    }
                    var tabs = settings.get('tabs');
                    if (tabs && tabs.models) {
                        tabs.each(function (tabModel) {
                            if (found) return;
                            var tc = tabModel.get('tab_content');
                            if (typeof tc === 'string' && regex.test(tc)) {
                                tabModel.set('tab_content', tc.replace(regex, '<a href="' + linkUrl + '">$1</a>'));
                                found = true;
                            }
                        });
                    }
                }
                if (!found) {
                    var children = model.get('elements');
                    if (children) walk(children);
                }
            });
        }

        try {
            walk(elementor.elements);
        } catch (e) {
        }
        return found;
    }

    /**
     * AJAX fallback: insert link server-side (for non-Elementor editors
     * that still have page builder data, e.g. regular WP editor with Elementor page).
     */
    function insertLinkViaAjax(url, text, $btn, showSuccess, showNotFound) {
        var postId = typeof seoFuryData !== 'undefined' ? (seoFuryData.post_id || seoFuryData.postId) : 0;
        if (!postId) { showNotFound(); return; }

        var nonce = '';
        if (typeof seoFuryData !== 'undefined') {
            nonce = seoFuryData.postMetaNonce || seoFuryData.nonce || '';
        }

        $btn.text('...').prop('disabled', true);

        var ajaxUrl = (typeof ajaxurl !== 'undefined' && ajaxurl) ? ajaxurl :
                      (typeof seoFuryData !== 'undefined' ? seoFuryData.ajaxUrl : '');

        $.post(ajaxUrl, {
            action: 'seo_fury_elementor_insert_link',
            nonce: nonce,
            post_id: postId,
            link_url: url,
            anchor_text: text
        }, function (r) {
            if (r.success) {
                showSuccess();
            } else {
                $btn.prop('disabled', false);
                showNotFound();
            }
        }).fail(function (xhr) {
            $btn.prop('disabled', false);
            showNotFound();
        });
    }

    $(document).on('click', '.sf-insert-link', function () {
        var url = $(this).data('url');
        var text = $(this).data('text');
        var $btn = $(this);

        // Helper: show success / error on button
        function showSuccess() {
            $btn.text(seoFuryData.i18n.linked).css('color', '#10B981').prop('disabled', true);
        }
        function showNotFound() {
            $btn.text(seoFuryData.i18n.notFound).css('color', '#d63638');
            setTimeout(function () { $btn.text(seoFuryData.i18n.insertLink).css('color', '').prop('disabled', false); }, 2000);
        }

        // 1. Elementor EDITOR active — client-side only (no DB save, no reload)
        //    The link persists in memory and saves when user clicks Publish/Update.
        var isElementorEditor = typeof elementor !== 'undefined' && elementor.elements;
        if (isElementorEditor) {
            var inserted = insertLinkInElementorModels(url, text);
            inserted ? showSuccess() : showNotFound();
            return;
        }

        // 2. Page has builder data but NOT in Elementor editor — use AJAX
        var hasPageBuilder = typeof seoFuryData !== 'undefined' &&
            (seoFuryData.has_builder || seoFuryData.isElementor);
        if (hasPageBuilder) {
            insertLinkViaAjax(url, text, $btn, showSuccess, showNotFound);
            return;
        }

        // 3. Gutenberg Block Editor
        if (typeof wp !== 'undefined' && wp.data && wp.data.select('core/block-editor')) {
            var blocks = wp.data.select('core/block-editor').getBlocks();
            var updated = false;
            blocks.forEach(function (block) {
                if (updated) return;
                if (block.attributes && block.attributes.content) {
                    var content = block.attributes.content;
                    var regex = new RegExp('(?<!<a[^>]*>)(' + text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + ')(?![^<]*</a>)', 'i');
                    if (regex.test(content)) {
                        var newContent = content.replace(regex, '<a href="' + url + '">$1</a>');
                        wp.data.dispatch('core/block-editor').updateBlockAttributes(block.clientId, { content: newContent });
                        updated = true;
                    }
                }
            });
            updated ? showSuccess() : showNotFound();
        }
        // 3. TinyMCE Classic Editor
        else if (typeof tinymce !== 'undefined' && tinymce.activeEditor && !tinymce.activeEditor.isHidden()) {
            var editor = tinymce.activeEditor;
            var content = editor.getContent();
            var regex = new RegExp('(?<!<a[^>]*>)(' + text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + ')(?![^<]*</a>)', 'i');
            if (regex.test(content)) {
                content = content.replace(regex, '<a href="' + url + '">$1</a>');
                editor.setContent(content);
                showSuccess();
            } else {
                showNotFound();
            }
        }
        // 4. Fallback — no supported editor found
        else {
            showNotFound();
        }
    });

    // ========================================
    // Secondary Keywords Tag Input
    // ========================================
    (function() {
        var $container = $('#seo-fury-keywords-tags');
        var $input = $('#seo_fury_secondary_keyword_input');
        var $hidden = $('#seo_fury_secondary_keywords');
        var MAX_KEYWORDS = 10;

        if (!$container.length || !$input.length) return;

        function getKeywords() {
            var val = $hidden.val().trim();
            if (!val) return [];
            return val.split(',').map(function(k) { return k.trim(); }).filter(Boolean);
        }

        function setKeywords(arr) {
            $hidden.val(arr.join(', '));
        }

        function addKeyword(keyword) {
            keyword = keyword.trim();
            if (!keyword) return;
            var keywords = getKeywords();
            // Prevent duplicates (case-insensitive)
            var lowerKw = keyword.toLowerCase();
            for (var i = 0; i < keywords.length; i++) {
                if (keywords[i].toLowerCase() === lowerKw) return;
            }
            // Also check against primary keyword
            var primary = $('#seo_fury_focus_keyword').val().trim().toLowerCase();
            if (primary && primary === lowerKw) return;
            if (keywords.length >= MAX_KEYWORDS) return;

            keywords.push(keyword);
            setKeywords(keywords);

            var $tag = $('<span class="seo-fury-keyword-tag">')
                .text(keyword)
                .append($('<button type="button" class="seo-fury-keyword-remove">&times;</button>').attr('data-keyword', keyword));
            $tag.insertBefore($input);
            $input.val('');
            triggerSecondaryAnalysis();
        }

        function removeKeyword(keyword) {
            var keywords = getKeywords().filter(function(k) {
                return k.toLowerCase() !== keyword.toLowerCase();
            });
            setKeywords(keywords);
            triggerSecondaryAnalysis();
        }

        // Click on container focuses input
        $container.on('click', function(e) {
            if (e.target === $container[0]) $input.focus();
        });

        // Enter or comma adds keyword
        $input.on('keydown', function(e) {
            if (e.key === 'Enter' || e.key === ',') {
                e.preventDefault();
                var val = $input.val().replace(/,/g, '').trim();
                addKeyword(val);
            }
            // Backspace on empty input removes last tag
            if (e.key === 'Backspace' && !$input.val()) {
                var keywords = getKeywords();
                if (keywords.length) {
                    var removed = keywords.pop();
                    setKeywords(keywords);
                    $container.find('.seo-fury-keyword-tag').last().remove();
                    triggerSecondaryAnalysis();
                }
            }
        });

        // Also add on blur if there's text
        $input.on('blur', function() {
            var val = $input.val().replace(/,/g, '').trim();
            if (val) addKeyword(val);
        });

        // Remove tag
        $container.on('click', '.seo-fury-keyword-remove', function(e) {
            e.stopPropagation();
            var keyword = $(this).data('keyword');
            $(this).closest('.seo-fury-keyword-tag').remove();
            removeKeyword(keyword);
        });

        // Secondary keywords analysis
        function triggerSecondaryAnalysis() {
            var keywords = getKeywords();
            var $list = $('#seo-fury-secondary-kw-analysis');

            // Create the analysis section if it doesn't exist
            if (!$list.length) {
                var $section = $('<div class="seo-fury-section seo-fury-secondary-kw-section" id="seo-fury-secondary-kw-section">' +
                    '<div class="seo-fury-analysis-header"><h4>' + (seoFuryData.i18n.secondaryKeywords || 'Secondary Keywords Analysis') + '</h4></div>' +
                    '<ul class="seo-fury-analysis-list" id="seo-fury-secondary-kw-analysis"></ul></div>');
                // Insert after the Extra SEO section
                var $extraSeo = $('.seo-fury-extra-seo');
                if ($extraSeo.length) {
                    $extraSeo.after($section);
                }
                $list = $('#seo-fury-secondary-kw-analysis');
            }

            if (!keywords.length) {
                $('#seo-fury-secondary-kw-section').hide();
                return;
            }

            $('#seo-fury-secondary-kw-section').show();
            $list.empty();

            var content = '';
            if (typeof wp !== 'undefined' && wp.data && wp.data.select && wp.data.select('core/editor')) {
                content = wp.data.select('core/editor').getEditedPostContent() || '';
            } else if (typeof tinymce !== 'undefined' && tinymce.get('content')) {
                content = tinymce.get('content').getContent({format: 'text'}) || '';
            }
            content = content.replace(/<[^>]+>/g, ' ').toLowerCase();

            var title = ($('#seo_fury_title').val() || '').toLowerCase();
            var desc = ($('#seo_fury_description').val() || '').toLowerCase();

            keywords.forEach(function(kw) {
                var kwLower = kw.toLowerCase();
                var inTitle = title.indexOf(kwLower) !== -1;
                var inDesc = desc.indexOf(kwLower) !== -1;
                var inContent = content.indexOf(kwLower) !== -1;

                // Count occurrences
                var count = 0;
                var idx = content.indexOf(kwLower);
                while (idx !== -1) {
                    count++;
                    idx = content.indexOf(kwLower, idx + kwLower.length);
                }

                var status = (inTitle || inDesc) && inContent ? 'good' : (inContent ? 'ok' : 'bad');
                var icon = status === 'good' ? '✓' : (status === 'ok' ? '△' : '✕');
                var color = status === 'good' ? '#10B981' : (status === 'ok' ? '#F59E0B' : '#EF4444');

                var details = [];
                details.push(inTitle ? '✓ title' : '✕ title');
                details.push(inDesc ? '✓ description' : '✕ description');
                details.push(count > 0 ? '✓ content (' + count + 'x)' : '✕ content');

                $list.append(
                    '<li style="padding:6px 0;border-bottom:1px solid #f1f5f9;display:flex;align-items:center;gap:8px;">' +
                    '<span style="color:' + color + ';font-weight:600;font-size:14px;width:16px;text-align:center;">' + icon + '</span>' +
                    '<span style="font-weight:500;">' + $('<span>').text(kw).html() + '</span>' +
                    '<span style="color:#94A3B8;font-size:12px;">' + details.join(' · ') + '</span>' +
                    '</li>'
                );
            });
        }

        // Re-run analysis when content changes
        $('#seo_fury_title, #seo_fury_description').on('input', function() {
            if (getKeywords().length) triggerSecondaryAnalysis();
        });

        // Initial run
        if (getKeywords().length) {
            setTimeout(triggerSecondaryAnalysis, 500);
        }
    })();

    // =============================================
    // Schema Templates Feature
    // =============================================

    // Load saved templates via AJAX and render them
    function loadSavedTemplates() {
        var $container = $('#seo-fury-saved-templates-list');
        $container.html('<p class="description">' + (seoFuryData.i18n.loadingTemplates || 'Loading...') + '</p>');

        $.post(seoFuryData.ajaxUrl, {
            action: 'seo_fury_get_schema_templates',
            nonce: seoFuryData.schemaTemplatesNonce
        }, function (response) {
            if (!response.success || !response.data.templates.length) {
                $container.html('<p class="description">' + (seoFuryData.i18n.noSavedTemplates || 'No saved templates yet.') + '</p>');
                return;
            }

            var labelMap = {};
            $('#seo_fury_schema_type option').each(function () {
                labelMap[$(this).val()] = $(this).text();
            });

            var html = '';
            response.data.templates.forEach(function (tpl) {
                var modeIcons = { generator: 'dashicons-media-document', custom: 'dashicons-media-code', custom_builder: 'dashicons-screenoptions' };
                var icon = modeIcons[tpl.mode] || 'dashicons-media-code';
                var typeLabel = (tpl.mode === 'custom' || tpl.mode === 'custom_builder')
                    ? (seoFuryData.i18n.customJsonLd || 'Custom JSON-LD')
                    : (labelMap[tpl.type] || tpl.type || '');
                html +=
                    '<div class="seo-fury-schema-card seo-fury-saved-template-card" data-template-id="' + tpl.id + '">' +
                        '<div class="seo-fury-schema-label">' +
                            '<span class="seo-fury-schema-icon dashicons ' + icon + '" aria-hidden="true"></span>' +
                            '<div class="seo-fury-schema-label-text">' +
                                '<span class="seo-fury-schema-badge">' + $('<span>').text(tpl.name).html() + '</span>' +
                                (typeLabel ? '<span style="color:#888;font-size:11px;display:block;">' + $('<span>').text(typeLabel).html() + '</span>' : '') +
                            '</div>' +
                        '</div>' +
                        '<div class="seo-fury-schema-actions">' +
                            '<button type="button" class="button button-small seo-fury-apply-saved-template" data-template-id="' + tpl.id + '">' +
                                (seoFuryData.i18n.applyTemplate || 'Apply') +
                            '</button>' +
                            '<a href="#" class="seo-fury-icon-link seo-fury-delete-saved-template" data-template-id="' + tpl.id + '" title="Delete">' +
                                '<span class="dashicons dashicons-trash"></span>' +
                            '</a>' +
                        '</div>' +
                    '</div>';
            });

            $container.html(html);
            // Cache templates data on container for apply handler
            $container.data('templates', response.data.templates);
        });
    }

    // Load templates when Saved Templates tab is activated
    $(document).on('click', '.seo-fury-schema-modal-tab[data-schema-tab="saved"]', function () {
        loadSavedTemplates();
    });

    // Apply saved template to the current post
    $(document).on('click', '.seo-fury-apply-saved-template', function () {
        var templateId = parseInt($(this).data('template-id'), 10);
        var $container = $('#seo-fury-saved-templates-list');
        var templates = $container.data('templates') || [];
        var tpl = null;
        for (var i = 0; i < templates.length; i++) {
            if (templates[i].id === templateId) { tpl = templates[i]; break; }
        }
        if (!tpl) return;

        storeActiveSchemaData();

        var newItem = {
            id: 'schema_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9),
            type: tpl.type || 'none',
            mode: tpl.mode || 'generator',
            label: tpl.name,
            data: {},
            custom: tpl.custom || ''
        };

        if (tpl.data && tpl.data !== '{}') {
            try { newItem.data = typeof tpl.data === 'string' ? JSON.parse(tpl.data) : tpl.data; } catch (e) {}
        }
        if (tpl.builder_data) {
            try { newItem.builder_data = typeof tpl.builder_data === 'string' ? JSON.parse(tpl.builder_data) : tpl.builder_data; } catch (e) {}
        }

        schemaItems.push(newItem);
        setActiveSchemaId(newItem.id);
        applyActiveSchemaItem(newItem);
        saveSchemaItems();
        renderSchemaLists();
        updateSchemaUI();

        // Close the schema generator modal
        $('.seo-fury-schema-modal-close').first().trigger('click');
    });

    // Delete a saved template
    $(document).on('click', '.seo-fury-delete-saved-template', function (e) {
        e.preventDefault();
        var templateId = $(this).data('template-id');
        if (!confirm(seoFuryData.i18n.confirmDeleteTemplate || 'Delete this saved template?')) return;

        $.post(seoFuryData.ajaxUrl, {
            action: 'seo_fury_delete_schema_template',
            nonce: seoFuryData.schemaTemplatesNonce,
            template_id: templateId
        }, function (response) {
            if (response.success) {
                loadSavedTemplates();
            }
        });
    });

    // Save current schema in editor as a named template
    $(document).on('click', '.schema-editor-save-template', function () {
        // Collect the latest data from editor fields first
        collectEditorFieldData();

        var name = prompt(seoFuryData.i18n.enterTemplateName || 'Enter a name for this template:');
        if (!name || !name.trim()) return;
        name = name.trim();

        var item = editingSchemaData;
        if (!item || !item.id) return;

        var dataStr = '{}';
        if (item.mode === 'generator' && item.data) {
            try { dataStr = JSON.stringify(item.data); } catch (e) {}
        }
        var builderStr = '';
        if (item.builder_data) {
            try { builderStr = JSON.stringify(item.builder_data); } catch (e) {}
        }

        $.post(seoFuryData.ajaxUrl, {
            action: 'seo_fury_save_schema_template',
            nonce: seoFuryData.schemaTemplatesNonce,
            name: name,
            mode: item.mode || 'generator',
            type: item.type || 'none',
            data: dataStr,
            custom: item.custom || '',
            builder_data: builderStr
        }, function (response) {
            if (response.success) {
                alert(seoFuryData.i18n.templateSaved || 'Template saved successfully.');
            } else {
                alert(seoFuryData.i18n.templateSaveError || 'Failed to save template.');
            }
        });
    });

});
