; (function ($) {
    $(function () {
        const $document = $(document);
        const $window = $(window);

        // ========================================
        // File Upload Field (wp.media)
        // ========================================
        const EVENT_NAMESPACE = '.nxtfOptions';
        const FILE_SELECTORS = {
            container: '[data-file-upload]',
            input: '[data-file-input]',
            button: '[data-file-button]',
            remove: '[data-file-remove]',
            preview: '[data-file-preview]',
            previewLabel: '.nxtf-file-upload-copy-label',
            previewMeta: '.nxtf-file-upload-copy-meta',
        };

        function isConditionallyDisabled($el) {
            return $el.closest('.nxtf-form-field[data-nxtf-condition-disabled="1"]').length > 0;
        }

        function isFileFieldLocked($container) {
            return $container.hasClass('nxtf-file-upload-locked') || isConditionallyDisabled($container);
        }

        function isFileFieldExplicitlyDisabled($container) {
            return $container.hasClass('nxtf-file-upload-disabled');
        }

        function updateFileButtonState($container) {
            const $button = $container.find(FILE_SELECTORS.button);
            if (!$button.length) return;

            const isLocked = isFileFieldLocked($container);
            $button.prop('disabled', isLocked);
            if (isLocked) {
                $button.css({ opacity: '0.5', pointerEvents: 'none' });
            } else {
                $button.css({ opacity: '', pointerEvents: '' });
            }
        }

        function updateFileInputState($container) {
            const $input = $container.find(FILE_SELECTORS.input);
            if (!$input.length) return;

            $input.prop('disabled', isFileFieldExplicitlyDisabled($container));
        }

        function updateFileRemoveState($container, hasValue) {
            const $remove = $container.find(FILE_SELECTORS.remove);
            if (!$remove.length) return;
            const isLocked = isFileFieldLocked($container);
            $remove.prop('disabled', isLocked || !hasValue);
            if (!isLocked && hasValue) {
                $remove.css({ opacity: '', pointerEvents: '' });
            } else {
                $remove.css({ opacity: '0.5', pointerEvents: 'none' });
            }
        }

        function setFileInputValue($container, value) {
            const normalizedValue = String(value || '');
            const $input = $container.find(FILE_SELECTORS.input);
            if (!$input.length) return $input;

            $input.data('nxtfFileValue', normalizedValue);
            $input.val(normalizedValue).trigger('change');
            return $input;
        }

        function updateFilePreviewState($container, hasValue) {
            $container.attr('data-file-has-preview', hasValue ? '1' : '0');
        }

        function updateFilePreviewKind($container, kind) {
            $container.attr('data-file-preview-kind', kind || 'none');
        }

        function getUrlExtension(url) {
            if (!url) return '';

            const cleanUrl = String(url).split('?')[0].split('#')[0];
            const lastSegment = cleanUrl.split('/').pop() || '';
            if (lastSegment.indexOf('.') === -1) return '';

            return lastSegment.split('.').pop().toLowerCase();
        }

        function isImageExtension(ext) {
            return ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'svg', 'avif', 'ico', 'tif', 'tiff'].indexOf(String(ext || '').toLowerCase()) !== -1;
        }

        function mimeLooksLikeImage(mime) {
            return String(mime || '').toLowerCase().indexOf('image/') === 0;
        }

        function extensionRulesSuggestImage($container) {
            const allowedExtensions = parseCsvList($container.data('file-extensions'));
            if (!allowedExtensions.length) {
                return false;
            }

            return allowedExtensions.every(function (rule) {
                return isImageExtension(String(rule || '').replace(/^\./, ''));
            });
        }

        function rulesSuggestImage($container) {
            const libraryType = String($container.data('file-library') || '').toLowerCase();
            if (libraryType === 'image') {
                return true;
            }

            const mimeRuleSuggestsImage = parseCsvList($container.data('file-mime-types')).some(function (rule) {
                return rule === 'image' || rule.indexOf('image/') === 0;
            });

            return mimeRuleSuggestsImage || extensionRulesSuggestImage($container);
        }

        function getPreviewKindForUrl($container, url) {
            if (!url) {
                return 'none';
            }

            const ext = getUrlExtension(url);
            if (ext) {
                return isImageExtension(ext) ? 'image' : 'file';
            }

            return rulesSuggestImage($container) ? 'image' : 'file';
        }

        function getAttachmentPreviewKind($container, attachment) {
            if (!attachment || typeof attachment !== 'object') {
                return 'none';
            }

            if (attachment.type === 'image') {
                return 'image';
            }

            const mime = getAttachmentMime(attachment);
            if (mimeLooksLikeImage(mime)) {
                return 'image';
            }

            const ext = getAttachmentExtension(attachment);
            if (isImageExtension(ext)) {
                return 'image';
            }

            return getPreviewKindForUrl($container, attachment.url || '');
        }

        function getFileNameFromUrl(url) {
            const cleanUrl = String(url || '').split('?')[0].split('#')[0];
            const lastSegment = cleanUrl.split('/').pop() || '';

            try {
                return decodeURIComponent(lastSegment) || 'File attached';
            } catch (e) {
                return lastSegment || 'File attached';
            }
        }

        function updateFilePreviewCopy($container, url, previewKind) {
            const $label = $container.find(FILE_SELECTORS.previewLabel).first();
            const $meta = $container.find(FILE_SELECTORS.previewMeta).first();
            if (!$label.length || !$meta.length) return;

            const defaultLabel = String($container.data('file-empty-label') || 'File upload');
            const defaultMeta = String($container.data('file-empty-meta') || 'Select a file');
            const maxLabel = String($container.data('file-max-label') || '');

            if (url && previewKind === 'file') {
                $label.text('Selected file');
                $meta.text(getFileNameFromUrl(url));
                return;
            }

            if (url && previewKind === 'image') {
                if (maxLabel) {
                    $label.text('Maximum allowed file size is');
                    $meta.text(maxLabel);
                    return;
                }

                $label.text('File upload');
                $meta.text('Select a file');
                return;
            }

            $label.text(defaultLabel);
            $meta.text(defaultMeta);
        }

        function updateFileField($container, attachment) {
            const url = attachment && attachment.url ? attachment.url : '';
            const previewKind = attachment ? getAttachmentPreviewKind($container, attachment) : 'none';

            setFileInputValue($container, url);

            const $preview = $container.find(FILE_SELECTORS.preview);
            if ($preview.length) {
                $preview.css('background-image', url && previewKind === 'image' ? 'url("' + url.replace(/(["\\])/g, '\\$1') + '")' : 'none');
            }

            updateFilePreviewState($container, !!url);
            updateFilePreviewKind($container, previewKind);
            updateFilePreviewCopy($container, url, previewKind);
            updateFileRemoveState($container, !!url);
        }

        function syncFileFieldState($container) {
            updateFileInputState($container);
            updateFileButtonState($container);

            const $input = $container.find(FILE_SELECTORS.input);
            if ($input.length && typeof $input.data('nxtfFileValue') === 'undefined') {
                $input.data('nxtfFileValue', String($input.val() || ''));
            }
            const value = $input.length ? String($input.val() || '') : '';
            const hasValue = value !== '';
            const previewKind = getPreviewKindForUrl($container, value);
            const $preview = $container.find(FILE_SELECTORS.preview);

            if ($preview.length) {
                $preview.css('background-image', hasValue && previewKind === 'image' ? 'url("' + value.replace(/(["\\])/g, '\\$1') + '")' : 'none');
            }

            updateFilePreviewState($container, hasValue);
            updateFilePreviewKind($container, hasValue ? previewKind : 'none');
            updateFilePreviewCopy($container, value, hasValue ? previewKind : 'none');
            updateFileRemoveState($container, hasValue);
        }

        function clearFileField($container) {
            setFileInputValue($container, '');

            const $preview = $container.find(FILE_SELECTORS.preview);
            if ($preview.length) {
                $preview.css('background-image', '');
            }

            updateFilePreviewState($container, false);
            updateFilePreviewKind($container, 'none');
            updateFilePreviewCopy($container, '', 'none');
            updateFileRemoveState($container, false);
        }

        function parseCsvList(value) {
            if (!value) return [];
            return String(value)
                .split(/[,\s|]+/)
                .map(function (item) { return item.trim().toLowerCase(); })
                .filter(Boolean);
        }

        function getAttachmentExtension(attachment) {
            if (!attachment || typeof attachment !== 'object') return '';

            const filename = attachment.filename || '';
            if (filename && filename.indexOf('.') !== -1) {
                return filename.split('.').pop().toLowerCase();
            }

            const url = attachment.url || '';
            if (!url) return '';
            const cleanUrl = String(url).split('?')[0].split('#')[0];
            const lastSegment = cleanUrl.split('/').pop() || '';
            if (lastSegment.indexOf('.') === -1) return '';
            return lastSegment.split('.').pop().toLowerCase();
        }

        function getAttachmentMime(attachment) {
            if (!attachment || typeof attachment !== 'object') return '';

            if (attachment.mime) {
                return String(attachment.mime).toLowerCase();
            }

            if (attachment.type && attachment.subtype) {
                return (String(attachment.type) + '/' + String(attachment.subtype)).toLowerCase();
            }

            return '';
        }

        function mimeMatchesRule(mime, rule) {
            if (!mime || !rule) return false;

            const normalizedMime = String(mime).toLowerCase();
            const normalizedRule = String(rule).toLowerCase();

            if (normalizedRule.indexOf('/*') !== -1) {
                const prefix = normalizedRule.replace('/*', '/');
                return normalizedMime.indexOf(prefix) === 0;
            }

            if (normalizedRule.indexOf('/') === -1) {
                return normalizedMime.indexOf(normalizedRule + '/') === 0;
            }

            return normalizedMime === normalizedRule;
        }

        function getFileMaxBytes($container) {
            const rawValue = $container.data('file-max-bytes');
            const numericValue = Number(rawValue);
            return Number.isFinite(numericValue) && numericValue > 0 ? Math.floor(numericValue) : 0;
        }

        function getAttachmentSizeBytes(attachment) {
            if (!attachment || typeof attachment !== 'object') return null;

            const directValue = Number(attachment.filesizeInBytes);
            if (Number.isFinite(directValue) && directValue >= 0) {
                return Math.floor(directValue);
            }

            return null;
        }

        function formatBytes(bytes) {
            const normalized = Number(bytes);
            if (!Number.isFinite(normalized) || normalized <= 0) {
                return '0 bytes';
            }

            const units = ['bytes', 'KB', 'MB', 'GB', 'TB'];
            let value = normalized;
            let index = 0;

            while (value >= 1024 && index < units.length - 1) {
                value /= 1024;
                index += 1;
            }

            const precision = index === 0 ? 0 : (value >= 10 ? 0 : 1);
            return value.toFixed(precision).replace(/\.0$/, '') + ' ' + units[index];
        }

        function attachmentMatchesFormatRules($container, attachment) {
            const allowedExtensions = parseCsvList($container.data('file-extensions'));
            const allowedMimeTypes = parseCsvList($container.data('file-mime-types'));

            if (!allowedExtensions.length && !allowedMimeTypes.length) {
                return true;
            }

            if (allowedExtensions.length) {
                const ext = getAttachmentExtension(attachment).replace(/^\./, '');
                if (!ext || allowedExtensions.indexOf(ext) === -1) {
                    return false;
                }
            }

            if (allowedMimeTypes.length) {
                const mime = getAttachmentMime(attachment);
                if (!mime) {
                    return false;
                }

                const matchesMime = allowedMimeTypes.some(function (rule) {
                    return mimeMatchesRule(mime, rule);
                });

                if (!matchesMime) {
                    return false;
                }
            }

            return true;
        }

        function attachmentMatchesSizeRules($container, attachment) {
            const maxBytes = getFileMaxBytes($container);
            if (!maxBytes) {
                return true;
            }

            const sizeBytes = getAttachmentSizeBytes(attachment);
            if (sizeBytes === null) {
                return true;
            }

            return sizeBytes <= maxBytes;
        }

        function buildInvalidFormatMessage($container) {
            const customMessage = $container.data('file-invalid-message');
            if (customMessage) return String(customMessage);

            const parts = [];
            const allowedExtensions = parseCsvList($container.data('file-extensions'));
            const allowedMimeTypes = parseCsvList($container.data('file-mime-types'));

            if (allowedExtensions.length) {
                parts.push('extensions: ' + allowedExtensions.join(', '));
            }

            if (allowedMimeTypes.length) {
                parts.push('types: ' + allowedMimeTypes.join(', '));
            }

            return parts.length
                ? 'Selected file format is not allowed (' + parts.join(' | ') + ').'
                : 'Selected file format is not allowed.';
        }

        function buildInvalidSizeMessage($container) {
            const customMessage = $container.data('file-invalid-size-message');
            if (customMessage) return String(customMessage);

            const maxBytes = getFileMaxBytes($container);
            const maxLabel = $container.data('file-max-label');
            const label = maxLabel ? String(maxLabel) : formatBytes(maxBytes);

            return 'Selected file exceeds the maximum allowed size (' + label + ').';
        }

        function getMediaFrame($container) {
            let frame = $container.data('nxtfMediaFrame');
            if (frame) return frame;

            const title = $container.data('file-title') || 'Select a file';
            const buttonText = $container.data('file-button-text') || 'Use this file';
            const libraryType = $container.data('file-library');
            const allowedMimeTypes = parseCsvList($container.data('file-mime-types'));

            const frameOptions = {
                title: title,
                button: { text: buttonText },
                multiple: false,
            };

            if (libraryType) {
                frameOptions.library = { type: libraryType };
            } else if (allowedMimeTypes.length) {
                frameOptions.library = { type: allowedMimeTypes };
            }

            frame = wp.media(frameOptions);
            frame.on('select', function () {
                if (isFileFieldLocked($container)) {
                    return;
                }

                const selection = frame.state().get('selection');
                const attachment = selection && selection.first ? selection.first().toJSON() : null;
                if (attachment) {
                    if (!attachmentMatchesFormatRules($container, attachment)) {
                        window.alert(buildInvalidFormatMessage($container));
                        return;
                    }
                    if (!attachmentMatchesSizeRules($container, attachment)) {
                        window.alert(buildInvalidSizeMessage($container));
                        return;
                    }
                    updateFileField($container, attachment);
                }
            });

            $container.data('nxtfMediaFrame', frame);
            return frame;
        }

        function destroyMediaFrame($container) {
            const frame = $container.data('nxtfMediaFrame');
            if (frame) {
                frame.off('select');
                frame.dispose && frame.dispose();
                $container.removeData('nxtfMediaFrame');
            }
        }

        $document.on('click' + EVENT_NAMESPACE, FILE_SELECTORS.button, function (e) {
            e.preventDefault();
            const $container = $(this).closest(FILE_SELECTORS.container);
            if (!$container.length) return;
            if ($(this).prop('disabled') || isFileFieldLocked($container)) return;
            if (typeof wp === 'undefined' || !wp.media) return;
            getMediaFrame($container).open();
        });

        $document.on('click' + EVENT_NAMESPACE, FILE_SELECTORS.remove, function (e) {
            e.preventDefault();
            const $container = $(this).closest(FILE_SELECTORS.container);
            if (!$container.length) return;
            if ($(this).prop('disabled') || isFileFieldLocked($container)) return;
            destroyMediaFrame($container);
            clearFileField($container);
        });

        $document.on('keydown' + EVENT_NAMESPACE, FILE_SELECTORS.input, function (e) {
            const key = e.key;
            const $input = $(this);
            const $container = $input.closest(FILE_SELECTORS.container);
            if (!$container.length || $input.prop('disabled')) return;

            if (key === 'Enter' || key === ' ') {
                e.preventDefault();
                if (isFileFieldLocked($container)) return;
                if (typeof wp === 'undefined' || !wp.media) return;
                getMediaFrame($container).open();
                return;
            }

            if (key !== 'Tab' && key !== 'Shift') {
                e.preventDefault();
            }
        });

        $document.on('input' + EVENT_NAMESPACE, FILE_SELECTORS.input, function () {
            const $input = $(this);
            const lockedValue = String($input.data('nxtfFileValue') || '');
            if (String($input.val() || '') !== lockedValue) {
                $input.val(lockedValue);
            }
        });

        $(FILE_SELECTORS.container).each(function () {
            const $container = $(this);
            const value = $container.find(FILE_SELECTORS.input).val();
            $container.find(FILE_SELECTORS.input).data('nxtfFileValue', String(value || ''));
            syncFileFieldState($container);
        });

        // ========================================
        // Select2 Field
        // ========================================
        const SELECT2_SELECTOR = 'select[data-nxtf-select2]';

        function initSelect2($scope) {
            if (!$.fn.select2) return;
            const $targets = $scope && $scope.length ? $scope.find(SELECT2_SELECTOR) : $(SELECT2_SELECTOR);

            $targets.each(function () {
                const $select = $(this);
                if ($select.data('select2-initialized')) return;
                if (!$select.is(':visible')) return;

                const searchEnabled = String($select.data('nxtf-select2-search')) !== '0';
                const placeholder = $select.data('nxtf-select2-placeholder') || '';
                const isMultiple = $select.prop('multiple');

                const config = {
                    width: '100%',
                };

                if (!searchEnabled) {
                    config.minimumResultsForSearch = Infinity;
                }

                if (placeholder) {
                    config.placeholder = placeholder;
                    if (!isMultiple) {
                        config.allowClear = true;
                    }
                }

                $select.select2(config);
                $select.data('select2-initialized', true);
            });
        }

        function destroySelect2($scope) {
            if (!$.fn.select2) return;

            const $targets = $scope && $scope.length
                ? ($scope.is(SELECT2_SELECTOR) ? $scope : $scope.find(SELECT2_SELECTOR))
                : $(SELECT2_SELECTOR);

            $targets.each(function () {
                const $select = $(this);
                if (!$select.data('select2-initialized')) return;

                $select.select2('destroy');
                $select.data('select2-initialized', false);
            });
        }

        function getSelectPlaceholder($select) {
            if ($select.prop('multiple')) return '';

            var select2Placeholder = $select.data('nxtf-select2-placeholder');
            if (typeof select2Placeholder === 'string' && select2Placeholder) {
                return select2Placeholder;
            }

            var nativePlaceholder = $select.attr('data-nxtf-select-placeholder');
            return nativePlaceholder ? String(nativePlaceholder) : '';
        }

        function getDisabledOptionLookup($select) {
            var raw = $select.attr('data-nxtf-disabled-options');
            if (!raw) return {};

            try {
                var parsed = JSON.parse(raw);
                if (!Array.isArray(parsed)) return {};

                var lookup = {};
                parsed.forEach(function (value) {
                    lookup[String(value)] = true;
                });

                return lookup;
            } catch (e) {
                return {};
            }
        }

        function findFirstEnabledOptionKey(options, disabledLookup) {
            var keys = Object.keys(options || {});

            for (var i = 0; i < keys.length; i++) {
                if (!options[keys[i]].disabled && !disabledLookup[String(keys[i])]) {
                    return keys[i];
                }
            }

            return null;
        }

        function isTruthyValue(value) {
            if (typeof value === 'boolean') return value;
            if (typeof value === 'number') return Number(value) === 1;
            if (typeof value !== 'string') return false;

            var normalized = value.trim().toLowerCase();
            return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';
        }

        function normalizeOptionDefinition(key, definition, disabledLookup) {
            var isRichObject = definition && typeof definition === 'object' && !Array.isArray(definition);
            var option = {
                label: isRichObject ? String(key) : (definition == null ? '' : String(definition)),
                disabled: !!disabledLookup[String(key)],
                title: '',
            };

            if (isRichObject) {
                if (definition.label != null) {
                    option.label = String(definition.label);
                }

                if (isTruthyValue(definition.disabled)) {
                    option.disabled = true;
                }

                if (typeof definition.title === 'string' && definition.title) {
                    option.title = definition.title;
                }
            }

            return option;
        }

        function normalizeOptionMap(options, disabledLookup) {
            var normalized = {};

            $.each(options || {}, function (key, definition) {
                normalized[String(key)] = normalizeOptionDefinition(String(key), definition, disabledLookup);
            });

            return normalized;
        }

        // ========================================
        // WYSIWYG Field (wp.editor)
        // ========================================
        const WYSIWYG_SELECTOR = 'textarea[data-wysiwyg]';
        const WYSIWYG_DEFAULTS = {
            tinymce: true,
            quicktags: true,
            mediaButtons: false,
        };
        const WYSIWYG_RETRY_LIMIT = 12;
        const WYSIWYG_RETRY_DELAY = 200;
        let wysiwygRetries = 0;
        let wysiwygRetryTimer = null;

        function parseWysiwygSettings(rawSettings) {
            if (!rawSettings) return {};
            try {
                const parsed = JSON.parse(rawSettings);
                return parsed && typeof parsed === 'object' ? parsed : {};
            } catch (err) {
                return {};
            }
        }

        function initWysiwygEditor($textarea) {
            const id = $textarea.attr('id');
            if (!id) return false;
            if ($textarea.data('wysiwyg-initialized')) return true;
            if (!$textarea.is(':visible')) return true;
            if (typeof wp === 'undefined' || !wp.editor || !wp.editor.initialize) return false;

            const rawSettings = $textarea.attr('data-wysiwyg-settings');
            const settings = $.extend(true, {}, WYSIWYG_DEFAULTS, parseWysiwygSettings(rawSettings));

            const needsTinymce = !!settings.tinymce;
            const needsQuicktags = !!settings.quicktags;

            if (!needsTinymce && !needsQuicktags) {
                $textarea.data('wysiwyg-initialized', true);
                return true;
            }

            if (needsTinymce && typeof window.tinymce === 'undefined') {
                return false;
            }

            if (needsQuicktags && typeof window.quicktags === 'undefined') {
                return false;
            }

            wp.editor.initialize(id, settings);
            $textarea.data('wysiwyg-initialized', true);
            return true;
        }

        function initWysiwygEditors($scope) {
            const $targets = $scope && $scope.length ? $scope.find(WYSIWYG_SELECTOR) : $document.find(WYSIWYG_SELECTOR);
            if (!$targets.length) return true;

            let hasVisiblePendingEditors = false;
            $targets.each(function () {
                const $textarea = $(this);
                if ($textarea.data('wysiwyg-initialized')) return;
                if (!$textarea.is(':visible')) return;
                hasVisiblePendingEditors = true;
                return false;
            });

            if (!hasVisiblePendingEditors) return true;
            if (typeof wp === 'undefined' || !wp.editor || !wp.editor.initialize) return false;

            let allReady = true;
            $targets.each(function () {
                if (!initWysiwygEditor($(this))) {
                    allReady = false;
                }
            });
            return allReady;
        }

        function ensureWysiwygEditors($scope) {
            if (initWysiwygEditors($scope)) {
                if (wysiwygRetryTimer) {
                    clearTimeout(wysiwygRetryTimer);
                    wysiwygRetryTimer = null;
                }
                return;
            }
            if (wysiwygRetries >= WYSIWYG_RETRY_LIMIT || wysiwygRetryTimer) {
                return;
            }
            wysiwygRetryTimer = setTimeout(function () {
                wysiwygRetryTimer = null;
                wysiwygRetries += 1;
                ensureWysiwygEditors($scope);
            }, WYSIWYG_RETRY_DELAY);
        }

        function getInitialEnhancementScope() {
            const $visiblePanelSections = $('.setting-panel .content-section:visible');
            return $visiblePanelSections.length ? $visiblePanelSections : $document;
        }

        const $initialEnhancementScope = getInitialEnhancementScope();
        initSelect2($initialEnhancementScope);
        ensureWysiwygEditors($initialEnhancementScope);

        // ========================================
        // Range Slider Value Display
        // ========================================
        const RANGE_SELECTOR = '[data-range-wrapper] input[type="range"][data-range-input]';

        $document.on('input' + EVENT_NAMESPACE, RANGE_SELECTOR, function () {
            const $input = $(this);
            const $wrapper = $input.closest('[data-range-wrapper]');
            const $value = $wrapper.find('[data-range-value]');
            if ($value.length) {
                $value.text($input.val());
            }
        });

        $(RANGE_SELECTOR).trigger('input');

        // ========================================
        // Color Picker Value Display
        // ========================================
        const COLOR_SELECTOR = 'input[type="color"][data-color-input]';

        $document.on('input' + EVENT_NAMESPACE + ' change' + EVENT_NAMESPACE, COLOR_SELECTOR, function () {
            const $input = $(this);
            const $label = $input.closest('[data-color-wrapper]').find('[data-color-value]').first();
            if ($label.length) {
                $label.text($input.val());
            }
        });

        $(COLOR_SELECTOR).trigger('input');

        // ========================================
        // Repeater Field
        // ========================================
        const REPEATER_SELECTORS = {
            container: '[data-repeater]',
            rows: '[data-repeater-rows]',
            row: '[data-repeater-row]',
            add: '[data-repeater-add]',
            remove: '[data-repeater-remove]',
            handle: '[data-repeater-handle]',
            header: '[data-repeater-header]',
            label: '[data-repeater-label]',
            fields: '[data-repeater-fields]',
            toggle: '[data-repeater-toggle]',
        };

        function isLockedRepeater($repeater) {
            return $repeater.hasClass('nxtf-repeater-locked') || isConditionallyDisabled($repeater);
        }

        function reindexRepeater($repeater) {
            var prefix = $repeater.attr('data-repeater-prefix');
            $repeater.find(REPEATER_SELECTORS.rows).first().children(REPEATER_SELECTORS.row).each(function (newIndex) {
                var $row = $(this);
                var currentIndex = $row.attr('data-repeater-index');

                if (String(currentIndex) !== String(newIndex)) {
                    var oldPrefix = prefix + '[' + currentIndex + ']';
                    var newPrefix = prefix + '[' + newIndex + ']';
                    $row.find('[name]').each(function () {
                        var name = this.getAttribute('name');
                        if (name && name.indexOf(oldPrefix) === 0) {
                            this.setAttribute('name', newPrefix + name.substring(oldPrefix.length));
                        }
                    });

                    // Update nested repeater prefixes
                    $row.find('[data-repeater-prefix]').each(function () {
                        var attr = this.getAttribute('data-repeater-prefix');
                        if (attr && attr.indexOf(oldPrefix) === 0) {
                            this.setAttribute('data-repeater-prefix', newPrefix + attr.substring(oldPrefix.length));
                        }
                    });

                    // Update nested template content
                    $row.find('template[data-repeater-tpl]').each(function () {
                        var tpl = this.innerHTML;
                        if (tpl.indexOf(oldPrefix) !== -1) {
                            this.innerHTML = tpl.split(oldPrefix).join(newPrefix);
                        }
                    });

                    $row.attr('data-repeater-index', newIndex);
                }
            });
        }

        function updateRepeaterAddButton($repeater) {
            var $addBtn = $repeater.children(REPEATER_SELECTORS.add).first();
            if (!$addBtn.length) return;

            if (isLockedRepeater($repeater)) {
                $addBtn.prop('disabled', true);
                $addBtn.css({ opacity: '0.5', pointerEvents: 'none' });
                return;
            }

            var maxRows = parseInt($repeater.attr('data-repeater-max') || '0', 10);
            if (maxRows <= 0) {
                if ($addBtn.prop('disabled') || $addBtn[0].style.opacity !== '' || $addBtn[0].style.pointerEvents !== '') {
                    $addBtn.prop('disabled', false);
                    $addBtn.css({ opacity: '', pointerEvents: '' });
                }
                return;
            }
            var currentCount = $repeater.find(REPEATER_SELECTORS.rows).first().children(REPEATER_SELECTORS.row).length;
            $addBtn.prop('disabled', currentCount >= maxRows);
            if (currentCount >= maxRows) {
                $addBtn.css({ opacity: '0.5', pointerEvents: 'none' });
            } else {
                $addBtn.css({ opacity: '', pointerEvents: '' });
            }
        }

        function initRepeaterRow($row) {
            initSelect2($row);
            ensureWysiwygEditors($row);
            $row.find(COLOR_SELECTOR).trigger('input');
            $row.find(RANGE_SELECTOR).trigger('input');
        }

        function syncRepeaterState($repeater) {
            var $rows = $repeater.find(REPEATER_SELECTORS.rows).first();
            if (!$rows.length) return;

            var locked = isLockedRepeater($repeater);
            var instance = $rows.data('repeater-sortable-instance');

            if (!instance) {
                if (typeof Sortable !== 'undefined' && !locked) {
                    instance = Sortable.create($rows[0], {
                        handle: REPEATER_SELECTORS.handle,
                        animation: 200,
                        ghostClass: 'nxtf-sortable-ghost',
                        chosenClass: 'nxtf-sortable-chosen',
                        onEnd: function () {
                            if (isLockedRepeater($repeater)) {
                                syncRepeaterState($repeater);
                                return;
                            }

                            reindexRepeater($repeater);
                            updateRepeaterAddButton($repeater);
                        }
                    });

                    $rows.data('repeater-sortable-instance', instance);
                }
            } else if (typeof instance.option === 'function') {
                instance.option('disabled', locked);
            }

            updateRepeaterAddButton($repeater);
        }

        function initRepeaterSortable($scope) {
            var $targets = $scope && $scope.length
                ? $scope.find(REPEATER_SELECTORS.container)
                : $(REPEATER_SELECTORS.container);

            if ($scope && $scope.length && $scope.is(REPEATER_SELECTORS.container)) {
                $targets = $targets.add($scope);
            }

            $targets.each(function () {
                syncRepeaterState($(this));
            });
        }

        // Add row
        $document.on('click' + EVENT_NAMESPACE, REPEATER_SELECTORS.add, function (e) {
            e.preventDefault();
            var $repeater = $(this).closest(REPEATER_SELECTORS.container);
            if (!$repeater.length) return;
            if ($(this).prop('disabled') || isLockedRepeater($repeater)) return;

            var $rows = $repeater.find(REPEATER_SELECTORS.rows).first();
            var html = $repeater.children('template[data-repeater-tpl]').html();
            var maxRows = parseInt($repeater.attr('data-repeater-max') || '0', 10);
            var currentCount = $rows.children(REPEATER_SELECTORS.row).length;

            if (maxRows > 0 && currentCount >= maxRows) return;
            if (!html) return;

            // Use monotonic counter for unique IDs; reindex fixes names to sequential order
            var nextIndex = parseInt($repeater.attr('data-repeater-next-index') || '0', 10);
            var depth = parseInt($repeater.attr('data-repeater-depth') || '0', 10);
            if (isNaN(depth)) depth = 0;
            var placeholder = new RegExp('__INDEX_' + depth + '__', 'g');
            var rowKeyPlaceholder = new RegExp('__ROW_KEY_' + depth + '__', 'g');
            var rowKey = 'row_' + depth + '_' + nextIndex + '_' + Date.now() + '_' + Math.floor(Math.random() * 1000);
            html = html.replace(placeholder, String(nextIndex));
            html = html.replace(rowKeyPlaceholder, rowKey);
            $repeater.attr('data-repeater-next-index', nextIndex + 1);

            var $newRow = $(html);
            $rows.append($newRow);
            reindexRepeater($repeater);

            // Accordion: collapse all siblings, expand the new row (no animation — slideDown handles appearance)
            $rows.children(REPEATER_SELECTORS.row).not($newRow).each(function () {
                if ($(this).attr('data-repeater-collapsed') !== '1') {
                    collapseRow($(this), true);
                }
            });
            expandRow($newRow, false);

            $newRow.hide().slideDown(200, function () {
                initRepeaterRow($newRow);
                initDynamicOptions($newRow);
                initConditionalFields($newRow);
                initRepeaterSortable($newRow);
            });

            updateRepeaterAddButton($repeater);
        });

        // Remove row
        $document.on('click' + EVENT_NAMESPACE, REPEATER_SELECTORS.remove, function (e) {
            e.preventDefault();
            var $row = $(this).closest(REPEATER_SELECTORS.row);
            var $repeater = $row.closest(REPEATER_SELECTORS.container);
            if ($(this).prop('disabled') || isLockedRepeater($repeater)) return;

            $row.slideUp(200, function () {
                destroySelect2($row);

                // Destroy WYSIWYG editors before removing the DOM element
                $row.find(WYSIWYG_SELECTOR).each(function () {
                    var id = this.getAttribute('id');
                    if (id && typeof wp !== 'undefined' && wp.editor && wp.editor.remove) {
                        wp.editor.remove(id);
                    }
                });

                $row.remove();
                reindexRepeater($repeater);
                updateRepeaterAddButton($repeater);
            });
        });

        // Get the toggle icon scoped to the row's own header (avoids nested repeaters)
        function getRowToggleIcon($row) {
            return $row.children(REPEATER_SELECTORS.header).find(REPEATER_SELECTORS.toggle + ' .dashicons');
        }

        // Collapse a single row (no animation or with animation)
        function collapseRow($row, animate) {
            var $fields = $row.children(REPEATER_SELECTORS.fields);
            if (!$fields.length) return;

            if (animate) {
                $fields.css('overflow', 'hidden');
                $fields.stop().animate({ height: 0 }, 200, function () {
                    $fields[0].style.setProperty('display', 'none', 'important');
                    $fields.css({ overflow: '', height: '' });
                });
            } else {
                $fields[0].style.setProperty('display', 'none', 'important');
            }
            $row.attr('data-repeater-collapsed', '1');
            getRowToggleIcon($row).css('transform', 'rotate(180deg)');
        }

        // Expand a single row
        function expandRow($row, animate) {
            var $fields = $row.children(REPEATER_SELECTORS.fields);
            if (!$fields.length) return;

            if (animate) {
                $fields.css({ overflow: 'hidden', height: 0 });
                $fields[0].style.removeProperty('display');
                var targetHeight = $fields.prop('scrollHeight');
                $fields.stop().animate({ height: targetHeight }, 200, function () {
                    $fields.css({ overflow: '', height: '' });
                });
            } else {
                $fields[0].style.removeProperty('display');
            }
            $row.removeAttr('data-repeater-collapsed');
            getRowToggleIcon($row).css('transform', '');
        }

        // Accordion toggle logic for a row
        function toggleRepeaterRow($row) {
            var $fields = $row.children(REPEATER_SELECTORS.fields);
            if (!$fields.length) return;
            var isCollapsed = $row.attr('data-repeater-collapsed') === '1';

            if (isCollapsed) {
                // Collapse all sibling rows, then expand this one
                $row.closest(REPEATER_SELECTORS.rows).children(REPEATER_SELECTORS.row).not($row).each(function () {
                    if ($(this).attr('data-repeater-collapsed') !== '1') {
                        collapseRow($(this), true);
                    }
                });
                expandRow($row, true);
            } else {
                collapseRow($row, true);
            }
        }

        // Toggle via toggle button
        $document.on('click' + EVENT_NAMESPACE, REPEATER_SELECTORS.toggle, function (e) {
            e.preventDefault();
            toggleRepeaterRow($(this).closest(REPEATER_SELECTORS.row));
        });

        // Toggle via row header click (skip drag handle, remove, and toggle button)
        $document.on('click' + EVENT_NAMESPACE, REPEATER_SELECTORS.header, function (e) {
            var $target = $(e.target);
            if ($target.closest(REPEATER_SELECTORS.handle).length ||
                $target.closest(REPEATER_SELECTORS.remove).length ||
                $target.closest(REPEATER_SELECTORS.toggle).length) {
                return;
            }
            e.preventDefault();
            toggleRepeaterRow($(this).closest(REPEATER_SELECTORS.row));
        });

        initRepeaterSortable($initialEnhancementScope);

        // Accordion init: expand first row, collapse rest (on page load)
        $(REPEATER_SELECTORS.container).each(function () {
            $(this).children(REPEATER_SELECTORS.rows).children(REPEATER_SELECTORS.row).each(function (i) {
                if (i === 0) {
                    expandRow($(this), false);
                } else {
                    collapseRow($(this), false);
                }
            });
        });

        // Block label: update row label from referenced sub-field value
        $document.on('input' + EVENT_NAMESPACE + ' change' + EVENT_NAMESPACE, REPEATER_SELECTORS.row + ' [name]', function () {
            var $input = $(this);
            var $row = $input.closest(REPEATER_SELECTORS.row);
            var $repeater = $row.closest(REPEATER_SELECTORS.container);
            var blockLabelField = $repeater.attr('data-repeater-block-label');
            if (!blockLabelField) return;

            var name = $input.attr('name') || '';
            if (name.indexOf('[' + blockLabelField + ']') === -1) return;

            $row.find(REPEATER_SELECTORS.label).first().text($input.val() || '');
        });

        // ========================================
        // Dynamic Options (options_when)
        // ========================================
        const DYNAMIC_OPTIONS_SELECTOR = '[data-options-when]';
        var pendingDynOpts = [];

        function bindDynOptsController(controllerFieldId, $ctrlScope, applyFn) {
            var $controller = findController(controllerFieldId, $ctrlScope);
            if (!$controller.length) return false;

            var inputType = ($controller.attr('type') || '').toLowerCase();
            var elName = $controller.attr('name') || '';

            if (inputType === 'radio' && elName) {
                $('input[name="' + elName + '"]').on('change' + EVENT_NAMESPACE + '.dynOpts', applyFn);
            } else if (inputType === 'checkbox' && elName.slice(-2) === '[]') {
                $('input[name="' + elName + '"]').on('change' + EVENT_NAMESPACE + '.dynOpts', applyFn);
            } else {
                var events = inputType === 'checkbox'
                    ? 'change' + EVENT_NAMESPACE + '.dynOpts'
                    : 'change' + EVENT_NAMESPACE + '.dynOpts input' + EVENT_NAMESPACE + '.dynOpts';
                $controller.on(events, applyFn);
            }
            return true;
        }

        function retryPendingDynOpts() {
            if (!pendingDynOpts.length) return;

            var stillPending = [];
            pendingDynOpts.forEach(function (entry) {
                if (!$.contains(document, entry.$fieldItem[0])) return;
                if (bindDynOptsController(entry.controllerFieldId, entry.$ctrlScope, entry.applyFn)) {
                    entry.applyFn();
                } else {
                    stillPending.push(entry);
                }
            });
            pendingDynOpts = stillPending;
        }

        function initDynamicOptions($scope) {
            retryPendingDynOpts();

            var $targets = $scope && $scope.length
                ? $scope.find(DYNAMIC_OPTIONS_SELECTOR)
                : $(DYNAMIC_OPTIONS_SELECTOR);

            if (!$targets.length) return;

            $targets.each(function () {
                var $fieldItem = $(this);
                if ($fieldItem.data('nxtf-dynamic-options-bound')) return;
                $fieldItem.data('nxtf-dynamic-options-bound', true);

                var config = $fieldItem.data('options-when');
                if (typeof config === 'string') {
                    try { config = JSON.parse(config); } catch (e) { return; }
                }
                if (!config || !config.field || !config.map) return;

                // Scope to repeater row if inside one
                var $rowScope = $fieldItem.closest(REPEATER_SELECTORS.row);
                var $ctrlScope = $rowScope.length ? $rowScope : null;

                // Collect static options from the select as fallback
                var $select = $fieldItem.find('select').first();
                if (!$select.length) return;

                var controllerFieldId = config.field;
                var map = config.map;
                var staticOptions = (config.options && typeof config.options === 'object') ? config.options : {};
                if (!Object.keys(staticOptions).length) {
                    $select.find('option').each(function () {
                        var $opt = $(this);
                        var val = $opt.val();
                        staticOptions[val] = $opt.text();
                    });
                }
                // Server-saved value for first run (may not exist in static options)
                var savedValue = config.value != null && config.value !== '' ? config.value : null;
                var disabledOptionLookup = getDisabledOptionLookup($select);

                function applyDynamicOptions() {
                    var controllerValue = getFieldValue(controllerFieldId, $ctrlScope);
                    var rawOptions = (controllerValue !== undefined && map[controllerValue]) ? map[controllerValue] : staticOptions;
                    var newOptions = normalizeOptionMap(rawOptions, disabledOptionLookup);
                    // On first run, use server-saved value; thereafter use live select value
                    var currentVal = savedValue !== null ? savedValue : $select.val();
                    savedValue = null;
                    var isSelect2 = $select.data('select2-initialized');

                    // Destroy Select2 before modifying options
                    if (isSelect2) {
                        destroySelect2($select);
                    }

                    // Clear and rebuild options (empty-value placeholder first)
                    $select.empty();
                    var placeholder = getSelectPlaceholder($select);
                    if (newOptions.hasOwnProperty('') && newOptions[''] !== undefined) {
                        $select.append($('<option>', {
                            value: '',
                            text: newOptions[''].label,
                            disabled: !!newOptions[''].disabled,
                            title: newOptions[''].title || undefined,
                        }));
                    } else if (placeholder) {
                        $select.append($('<option>', {
                            value: '',
                            text: placeholder,
                            disabled: !!disabledOptionLookup[''],
                        }));
                    }
                    $.each(newOptions, function (key, option) {
                        if (key === '') return;
                        $select.append($('<option>', {
                            value: key,
                            text: option.label,
                            disabled: !!option.disabled,
                            title: option.title || undefined,
                        }));
                    });

                    // Preserve value if still valid, otherwise select first
                    if (Array.isArray(currentVal)) {
                        var validVals = currentVal.filter(function (v) {
                            return newOptions.hasOwnProperty(v);
                        });
                        $select.val(validVals.length ? validVals : []);
                    } else if (currentVal === '' && placeholder) {
                        $select.val('');
                    } else if (newOptions.hasOwnProperty(currentVal)) {
                        $select.val(currentVal);
                    } else {
                        var firstKey = findFirstEnabledOptionKey(newOptions, disabledOptionLookup);
                        if (firstKey !== null) {
                            $select.val(firstKey);
                        } else {
                            $select.val(null);
                        }
                    }

                    // Reinit Select2 if it was active
                    if (isSelect2) {
                        initSelect2($fieldItem);
                    }

                    // Trigger change so conditional fields that depend on this select re-evaluate
                    $select.trigger('change');
                }

                // Bind to controller; defer if not yet in DOM
                if (bindDynOptsController(controllerFieldId, $ctrlScope, applyDynamicOptions)) {
                    applyDynamicOptions();
                } else {
                    pendingDynOpts.push({
                        $fieldItem: $fieldItem,
                        controllerFieldId: controllerFieldId,
                        $ctrlScope: $ctrlScope,
                        applyFn: applyDynamicOptions
                    });
                    // Apply with static options for now
                    applyDynamicOptions();
                }
            });
        }

        initDynamicOptions($initialEnhancementScope);

        // ========================================
        // Conditional Field Visibility
        // ========================================
        const CONDITION_ALL_SELECTOR = '[data-show-when],[data-hide-when],[data-disable-when]';
        const CONDITION_NAMESPACE = '.nxtfCondition';
        const controllerDependents = {};
        var cascadeDepth = 0;
        const MAX_CASCADE_DEPTH = 10;
        var pendingFields = [];

        /**
         * Find the controller element for a given field ID.
         * Handles radio groups and multi-checkboxes which lack an id attribute
         * by falling back to the data-field-id wrapper lookup.
         * When $scope is provided (e.g. a repeater row), limit lookup to that
         * row and ignore nested repeater rows with duplicate field IDs.
         */
        function findController(fieldId, $scope) {
            // Scoped search first (e.g. within a repeater row)
            if ($scope && $scope.length) {
                var scopeRow = $scope[0];
                var $scopeWrapper = $scope.find('.nxtf-form-field[data-field-id="' + fieldId + '"]').filter(function () {
                    var $closestRow = $(this).closest(REPEATER_SELECTORS.row);
                    return !$closestRow.length || $closestRow[0] === scopeRow;
                }).first();
                if ($scopeWrapper.length) {
                    // Prefer non-hidden inputs (checkbox/switch fields have a hidden _nxtf_rendered_* before the real input)
                    var $scopeInput = $scopeWrapper.find('input:not([type="hidden"]),select,textarea').first();
                    if (!$scopeInput.length) {
                        $scopeInput = $scopeWrapper.find('input,select,textarea').first();
                    }
                    if ($scopeInput.length) return $scopeInput;
                }
            }

            // Global fallback
            var $el = $('#' + fieldId);
            if ($el.length) return $el;

            var $wrapper = $('.nxtf-form-field[data-field-id="' + fieldId + '"]');
            if ($wrapper.length) {
                // Prefer non-hidden inputs (checkbox/switch fields have a hidden _nxtf_rendered_* before the real input)
                var $input = $wrapper.find('input:not([type="hidden"]),select,textarea').first();
                if (!$input.length) {
                    $input = $wrapper.find('input,select,textarea').first();
                }
                if ($input.length) return $input;
            }

            return $();
        }

        function getFieldValue(fieldId, $scope) {
            var $el = findController(fieldId, $scope);
            if (!$el.length) return undefined;

            // If the controller's own wrapper was hidden by a condition, treat as unavailable
            var $wrapper = $el.closest('.nxtf-form-field[data-field-id]');
            if ($wrapper.length && $wrapper.data('nxtf-visible') === false) return undefined;

            var inputType = ($el.attr('type') || '').toLowerCase();

            // Radio group: value of the checked radio sharing the same name
            if (inputType === 'radio') {
                return $('input[name="' + $el.attr('name') + '"]:checked').val() || '';
            }

            // Multi-checkbox (name ends with []): return array of checked values
            var elName = $el.attr('name') || '';
            if (inputType === 'checkbox' && elName.slice(-2) === '[]') {
                var checked = [];
                $('input[name="' + elName + '"]:checked').each(function () {
                    checked.push($(this).val());
                });
                return checked;
            }

            // Single checkbox / switch
            if (inputType === 'checkbox') {
                return $el.is(':checked') ? '1' : '0';
            }

            return $el.val() || '';
        }

        function conditionMatches(condition, controllerValue) {
            var operator = condition.operator || 'is';
            var targetValue = condition.value;

            // When controller is a multi-checkbox (array), check overlap
            if (Array.isArray(controllerValue)) {
                var cvStrings = controllerValue.map(String);
                switch (operator) {
                    case 'is':
                        if (Array.isArray(targetValue)) {
                            return targetValue.some(function (v) {
                                return cvStrings.indexOf(String(v)) !== -1;
                            });
                        }
                        return cvStrings.indexOf(String(targetValue)) !== -1;
                    case 'is_not':
                        if (Array.isArray(targetValue)) {
                            return targetValue.every(function (v) {
                                return cvStrings.indexOf(String(v)) === -1;
                            });
                        }
                        return cvStrings.indexOf(String(targetValue)) === -1;
                    case 'empty':
                        return cvStrings.length === 0;
                    case 'not_empty':
                        return cvStrings.length > 0;
                    case 'contains':
                        var joined = cvStrings.join(',');
                        if (Array.isArray(targetValue)) {
                            return targetValue.some(function (v) {
                                return joined.indexOf(String(v)) !== -1;
                            });
                        }
                        return joined.indexOf(String(targetValue)) !== -1;
                    default:
                        return false;
                }
            }

            switch (operator) {
                case 'is':
                    if (Array.isArray(targetValue)) {
                        return targetValue.map(String).indexOf(String(controllerValue)) !== -1;
                    }
                    return String(controllerValue) === String(targetValue);

                case 'is_not':
                    if (Array.isArray(targetValue)) {
                        return targetValue.map(String).indexOf(String(controllerValue)) === -1;
                    }
                    return String(controllerValue) !== String(targetValue);

                case 'empty':
                    return !controllerValue || String(controllerValue).trim() === '';

                case 'not_empty':
                    return !!controllerValue && String(controllerValue).trim() !== '';

                case 'contains':
                    if (Array.isArray(targetValue)) {
                        return targetValue.some(function (v) {
                            return String(controllerValue).indexOf(String(v)) !== -1;
                        });
                    }
                    return String(controllerValue).indexOf(String(targetValue)) !== -1;

                default:
                    return false;
            }
        }

        function normalizeConditions(raw) {
            if (typeof raw === 'string') {
                try {
                    raw = JSON.parse(raw);
                } catch (e) {
                    return [];
                }
            }
            if (Array.isArray(raw) && raw.length && typeof raw[0] === 'object' && raw[0].field) {
                return raw;
            }
            return (raw && typeof raw === 'object') ? [raw] : [];
        }

        function evaluateConditions(parsed, $scope) {
            return parsed.every(function (condition) {
                var value = getFieldValue(condition.field, $scope);
                return value !== undefined && conditionMatches(condition, value);
            });
        }

        function cascadeDependents($fieldItem) {
            if (cascadeDepth >= MAX_CASCADE_DEPTH) return;
            var fieldId = $fieldItem.attr('data-field-id');
            if (!fieldId || !controllerDependents[fieldId]) return;

            cascadeDepth++;
            var deps = controllerDependents[fieldId];
            var $itemRow = $fieldItem.closest(REPEATER_SELECTORS.row);
            var inRow = $itemRow.length > 0;

            for (var i = deps.length - 1; i >= 0; i--) {
                if (!$.contains(document, deps[i][0])) {
                    deps.splice(i, 1);
                    continue;
                }

                // When inside a repeater row, only cascade to dependents in the same row
                if (inRow) {
                    var $depScope = deps[i].data('nxtf-condition-scope');
                    if ($depScope && $depScope.length && $depScope[0] !== $itemRow[0]) {
                        continue;
                    }
                }

                scheduleApply(deps[i], true);
            }
            cascadeDepth--;
        }

        /**
         * Debounce applyConditions per field item using requestAnimationFrame.
         * Multiple calls in the same tick are collapsed into one.
         */
        function scheduleApply($fieldItem, animate) {
            var node = $fieldItem[0];
            if (node._nxtfApplyScheduled) return;
            node._nxtfApplyScheduled = true;
            requestAnimationFrame(function () {
                node._nxtfApplyScheduled = false;
                if (!$.contains(document, node)) return;
                applyConditions($fieldItem, animate);
            });
        }

        function applyConditions($fieldItem, animate) {
            var parsed = $fieldItem.data('nxtf-parsed-conditions');
            var $scope = $fieldItem.data('nxtf-condition-scope') || null;
            var shouldShow = true;
            var visibilityChanged = false;

            // Evaluate show/hide
            if (parsed.show) {
                shouldShow = evaluateConditions(parsed.show, $scope);
            }

            if (parsed.hide && evaluateConditions(parsed.hide, $scope)) {
                shouldShow = false;
            }

            // Store intended visibility synchronously (before animation and cascade)
            $fieldItem.data('nxtf-visible', shouldShow);

            var wasSelfHidden = $fieldItem[0] && $fieldItem[0].style.display === 'none';

            if (shouldShow && wasSelfHidden) {
                visibilityChanged = true;
                if (animate) {
                    $fieldItem.stop(true, true).slideDown(200, function () {
                        initSelect2($fieldItem);
                        ensureWysiwygEditors($fieldItem);
                    });
                } else {
                    $fieldItem.show();
                    initSelect2($fieldItem);
                    ensureWysiwygEditors($fieldItem);
                }
            } else if (!shouldShow && !wasSelfHidden) {
                visibilityChanged = true;
                if (animate && $fieldItem.is(':visible')) {
                    $fieldItem.stop(true, true).slideUp(200);
                } else {
                    $fieldItem.hide();
                }
            }

            // Evaluate disable
            if (parsed.disable) {
                var shouldDisable = evaluateConditions(parsed.disable, $scope);
                if (shouldDisable) {
                    $fieldItem.attr('data-nxtf-condition-disabled', '1');
                    $fieldItem.addClass('nxtf-condition-disabled');
                    $fieldItem.css({ opacity: '0.5', pointerEvents: 'none' });
                } else {
                    $fieldItem.removeAttr('data-nxtf-condition-disabled');
                    $fieldItem.removeClass('nxtf-condition-disabled');
                    $fieldItem.css({ opacity: '', pointerEvents: '' });
                }

                $fieldItem.find(FILE_SELECTORS.container).each(function () {
                    syncFileFieldState($(this));
                });

                $fieldItem.find(REPEATER_SELECTORS.container).each(function () {
                    syncRepeaterState($(this));
                });
            }

            // Cascade to dependents
            if (visibilityChanged) {
                cascadeDependents($fieldItem);
            }
        }

        /**
         * Bind change events on a controller for a dependent field item.
         * Returns true if the controller was found and bound, false otherwise.
         */
        function bindController(fieldId, $fieldItem) {
            var $scope = $fieldItem.data('nxtf-condition-scope') || null;
            var $controller = findController(fieldId, $scope);
            if (!$controller.length) return false;

            var inputType = ($controller.attr('type') || '').toLowerCase();
            var elName = $controller.attr('name') || '';

            // Radio group: bind all radios sharing the name
            if (inputType === 'radio') {
                $('input[name="' + elName + '"]')
                    .on('change' + CONDITION_NAMESPACE, function () {
                        if (!$.contains(document, $fieldItem[0])) return;
                        scheduleApply($fieldItem, true);
                    });
                return true;
            }

            // Multi-checkbox group: bind all checkboxes sharing the name
            if (inputType === 'checkbox' && elName.slice(-2) === '[]') {
                $('input[name="' + elName + '"]')
                    .on('change' + CONDITION_NAMESPACE, function () {
                        if (!$.contains(document, $fieldItem[0])) return;
                        scheduleApply($fieldItem, true);
                    });
                return true;
            }

            // Single checkbox / switch: only bind change (they don't fire input reliably)
            if (inputType === 'checkbox') {
                $controller.on('change' + CONDITION_NAMESPACE, function () {
                    if (!$.contains(document, $fieldItem[0])) return;
                    scheduleApply($fieldItem, true);
                });
                return true;
            }

            // Select, text, textarea, etc.: bind change + input
            $controller.on('change' + CONDITION_NAMESPACE + ' input' + CONDITION_NAMESPACE, function () {
                if (!$.contains(document, $fieldItem[0])) return;
                scheduleApply($fieldItem, true);
            });
            return true;
        }

        function retryPendingBindings() {
            if (!pendingFields.length) return;

            var stillPending = [];
            var resolvedItems = [];
            pendingFields.forEach(function (entry) {
                if (!$.contains(document, entry.$fieldItem[0])) return;
                var remaining = [];
                entry.ids.forEach(function (fieldId) {
                    if (entry.$fieldItem.data('nxtf-ctrl-' + fieldId)) return;
                    if (bindController(fieldId, entry.$fieldItem)) {
                        entry.$fieldItem.data('nxtf-ctrl-' + fieldId, true);
                        if (resolvedItems.indexOf(entry.$fieldItem) === -1) {
                            resolvedItems.push(entry.$fieldItem);
                        }
                    } else {
                        remaining.push(fieldId);
                    }
                });
                if (remaining.length) {
                    stillPending.push({ $fieldItem: entry.$fieldItem, ids: remaining });
                }
            });
            pendingFields = stillPending;

            // Re-evaluate dependents that gained new controller bindings
            if (resolvedItems.length) {
                resolvedItems.forEach(function ($item) {
                    applyConditions($item, false);
                });
            }
        }

        function initConditionalFields($scope) {
            // Always retry pending bindings — the new scope may contain controller fields
            retryPendingBindings();

            var $targets = $scope && $scope.length
                ? $scope.find(CONDITION_ALL_SELECTOR)
                : $(CONDITION_ALL_SELECTOR);

            if (!$targets.length) return;

            $targets.each(function () {
                var $fieldItem = $(this);
                if ($fieldItem.data('nxtf-condition-bound')) return;
                $fieldItem.data('nxtf-condition-bound', true);

                // Scope conditional lookups to the repeater row when inside one
                var $rowScope = $fieldItem.closest(REPEATER_SELECTORS.row);
                if ($rowScope.length) {
                    $fieldItem.data('nxtf-condition-scope', $rowScope);
                }

                // Parse and cache all conditions once
                var showRaw = $fieldItem.data('show-when');
                var hideRaw = $fieldItem.data('hide-when');
                var disableRaw = $fieldItem.data('disable-when');
                var parsed = {
                    show: showRaw ? normalizeConditions(showRaw) : null,
                    hide: hideRaw ? normalizeConditions(hideRaw) : null,
                    disable: disableRaw ? normalizeConditions(disableRaw) : null,
                };
                $fieldItem.data('nxtf-parsed-conditions', parsed);

                // Collect unique controller field IDs
                var uniqueIds = [];
                [parsed.show, parsed.hide, parsed.disable].forEach(function (conditions) {
                    if (!conditions) return;
                    conditions.forEach(function (c) {
                        if (c.field && uniqueIds.indexOf(c.field) === -1) {
                            uniqueIds.push(c.field);
                        }
                    });
                });

                var unresolvedIds = [];
                uniqueIds.forEach(function (fieldId) {
                    if (!controllerDependents[fieldId]) controllerDependents[fieldId] = [];
                    controllerDependents[fieldId].push($fieldItem);

                    if (bindController(fieldId, $fieldItem)) {
                        $fieldItem.data('nxtf-ctrl-' + fieldId, true);
                    } else {
                        unresolvedIds.push(fieldId);
                    }
                });

                // Track only unresolved IDs for deferred binding
                if (unresolvedIds.length) {
                    pendingFields.push({ $fieldItem: $fieldItem, ids: unresolvedIds });
                }

                // Set initial state without animation
                applyConditions($fieldItem, false);
            });
        }

        initConditionalFields($initialEnhancementScope);

        // ========================================
        // Navigation Logic
        // ========================================
        const ROTATE_CLASS = 'nxtf-menu-arrow-rotated';
        const SELECTORS = {
            panel: '.setting-panel',
            panelNav: '[data-panel-nav]',
            navItem: '.nav-item',
            navArrow: '[data-nav-arrow]',
            navIcon: '.dashicons:first-child',
            sublinkItem: '.sublink-item',
            sublinks: '.sublinks',
            contentSection: '.content-section',
            sectionPanels: '[data-section-panels]',
            sectionLoading: '[data-section-loading]',
        };
        const CLASSES = {
            activeSublink: 'active',
            navReset: 'active-parent',
            navInactive: '',
            navIconActive: 'nxtf-nav-icon-active',
            navIconInactive: '',
            navItemActive: 'active-parent',
        };
        const ANIMATION_CLASSES = ['nxtf-section-animate'];
        const SETTINGS_LOAD_ERROR = 'Failed to load settings. Refresh and try again.';
        const AJAX_URL = typeof window.ajaxurl === 'string' ? window.ajaxurl : '';
        const $panels = $(SELECTORS.panel);
        if (!$panels.length) return;
        const panelCount = $panels.length;
        const PANEL_STATE_KEY = 'nxtfOptionsPanelState';
        const panelStates = [];
        let hashChangeBound = false;

        function bindHashChange() {
            if (hashChangeBound) return;
            hashChangeBound = true;
            $window.on('hashchange' + EVENT_NAMESPACE, function () {
                panelStates.forEach(function (state) {
                    state.handleHash(true);
                });
            });
        }

        function setHash(hash, replace = false) {
            if (!hash) return false;
            const currentHash = window.location.hash.substring(1);
            if (currentHash === hash) return false;
            if (replace && window.history && window.history.replaceState) {
                window.history.replaceState(null, '', `#${hash}`);
            } else {
                window.location.hash = hash;
            }
            return true;
        }

        function safeDecode(value) {
            try {
                return decodeURIComponent(value);
            } catch (err) {
                return value;
            }
        }

        function normalizeId(value) {
            return safeDecode(String(value || '')).trim();
        }

        function normalizeHash(rawHash) {
            return normalizeId(rawHash).replace(/^#/, '').replace(/^\/+|\/+$/g, '');
        }

        function parseHash(hash) {
            const normalized = normalizeHash(hash);
            const parts = normalized ? normalized.split('/').filter(Boolean) : [];
            if (parts.length >= 3) {
                return {
                    panelId: normalizeId(parts[0]),
                    tabId: normalizeId(parts[1]),
                    sublinkId: normalizeId(parts[2]),
                    hasPanel: true
                };
            }
            if (parts.length === 2) {
                return {
                    panelId: '',
                    tabId: normalizeId(parts[0]),
                    sublinkId: normalizeId(parts[1]),
                    hasPanel: false
                };
            }
            return { panelId: '', tabId: '', sublinkId: '', hasPanel: false };
        }

        function buildHash(panelId, tabId, sublinkId) {
            const normalizedTab = normalizeId(tabId);
            const normalizedSublink = normalizeId(sublinkId);
            const normalizedPanel = normalizeId(panelId);
            if (!normalizedTab || !normalizedSublink) return '';
            if (normalizedPanel) {
                return [
                    encodeURIComponent(normalizedPanel),
                    encodeURIComponent(normalizedTab),
                    encodeURIComponent(normalizedSublink)
                ].join('/');
            }
            return [
                encodeURIComponent(normalizedTab),
                encodeURIComponent(normalizedSublink)
            ].join('/');
        }

        function findSublinkByHash($links, hashValue) {
            const normalizedHash = normalizeHash(hashValue);
            if (!normalizedHash) return $();
            const normalizedSuffix = normalizedHash.split('/').slice(-2).join('/');
            let $result = $();
            $links.each(function () {
                const linkHash = normalizeHash($(this).attr('href'));
                if (linkHash === normalizedHash
                    || linkHash === normalizedSuffix
                    || linkHash.endsWith(`/${normalizedSuffix}`)) {
                    $result = $(this);
                    return false;
                }
            });
            return $result;
        }

        function initializePanel(index) {
            const $panel = $panels.eq(index);
            const existingState = $panel.data(PANEL_STATE_KEY);
            if (existingState) {
                return existingState;
            }

            const panelId = normalizeId($panel.data('panel-id'));
            const normalizedPanelId = panelId.toLowerCase();
            const isPrimaryPanel = index === 0;
            const $navContainer = $panel.find(SELECTORS.panelNav);
            if (!$navContainer.length) return;

            const $navItems = $navContainer.find(SELECTORS.navItem);
            const $navIcons = $navItems.find(SELECTORS.navIcon);
            const $navArrows = $navItems.find(SELECTORS.navArrow);
            const $sublinkItems = $navContainer.find(SELECTORS.sublinkItem);
            const $allSublinks = $navContainer.find(SELECTORS.sublinks);
            const $contentSections = $panel.find(SELECTORS.contentSection);
            const sectionAction = String($panel.data('section-action') || '').trim();
            const sectionNonce = String($panel.data('section-nonce') || '').trim();
            const sectionRequestCache = new Map();
            let $activeSection = $contentSections.filter('.active:visible').first();

            function getDefaultTarget() {
                const $firstSublink = $sublinkItems.first();
                if (!$firstSublink.length) return null;
                const tabId = normalizeId($firstSublink.closest('[data-tab-id]').attr('data-tab-id'));
                const sublinkId = normalizeId($firstSublink.attr('data-id'));
                if (!tabId || !sublinkId) return null;
                return { tabId: tabId, sublinkId: sublinkId };
            }

            function resetNavState() {
                $sublinkItems.removeClass(CLASSES.activeSublink);
                $navItems.removeClass(CLASSES.navReset).addClass(CLASSES.navInactive);
                $navIcons.removeClass(CLASSES.navIconActive).addClass(CLASSES.navIconInactive);
            }

            const sectionCache = new Map();

            function getSectionElement(tabId, sublinkId) {
                const cacheKey = `${tabId}-${sublinkId}`;
                if (sectionCache.has(cacheKey)) {
                    return sectionCache.get(cacheKey);
                }

                let $section = $contentSections.filter(`[data-section-id="${cacheKey}"]`);
                if (!$section.length && panelId) {
                    const sectionDomId = `section-${panelId}-${cacheKey}`;
                    $section = $contentSections.filter(function () {
                        return this.id === sectionDomId;
                    });
                }

                sectionCache.set(cacheKey, $section);
                return $section;
            }

            function getDeferredSectionErrorHtml() {
                return `<p class="nxtf-text-error" data-section-loading="1">${SETTINGS_LOAD_ERROR}</p>`;
            }

            function loadSectionPanelsIfNeeded($section) {
                if (!$section.length || $section.attr('data-section-loaded') === '1') {
                    return $.Deferred().resolve().promise();
                }

                const sectionKey = normalizeId($section.attr('data-section-id'));
                if (sectionKey && sectionRequestCache.has(sectionKey)) {
                    return sectionRequestCache.get(sectionKey);
                }

                const groupId = normalizeId($section.attr('data-group-id') || sectionKey);
                const $panelContainer = $section.find(SELECTORS.sectionPanels).first();
                const $loadingPlaceholder = $panelContainer.find(SELECTORS.sectionLoading).first();

                if (!groupId || !sectionAction || !sectionNonce || !AJAX_URL) {
                    if ($loadingPlaceholder.length) {
                        $loadingPlaceholder.replaceWith(getDeferredSectionErrorHtml());
                    } else if ($panelContainer.length) {
                        $panelContainer.html(getDeferredSectionErrorHtml());
                    }
                    return $.Deferred().resolve().promise();
                }

                if ($loadingPlaceholder.length) {
                    $loadingPlaceholder.text('Loading settings...');
                } else if ($panelContainer.length && !$panelContainer.children().length) {
                    $panelContainer.html('<p class="nxtf-text-loading" data-section-loading="1">Loading settings...</p>');
                }

                const request = $.ajax({
                    url: AJAX_URL,
                    type: 'POST',
                    dataType: 'json',
                    data: {
                        action: sectionAction,
                        nonce: sectionNonce,
                        group_id: groupId,
                    }
                }).done(function (response) {
                    const html = response && response.success && response.data ? response.data.html : '';
                    if (typeof html !== 'string' || !html.length) {
                        if ($panelContainer.length) {
                            $panelContainer.html(getDeferredSectionErrorHtml());
                        }
                        return;
                    }

                    if ($panelContainer.length) {
                        $panelContainer.html(html);
                    }
                    $section.attr('data-section-loaded', '1');
                }).fail(function () {
                    if ($panelContainer.length) {
                        $panelContainer.html(getDeferredSectionErrorHtml());
                    }
                }).always(function () {
                    if (sectionKey) {
                        sectionRequestCache.delete(sectionKey);
                    }
                });

                if (sectionKey) {
                    sectionRequestCache.set(sectionKey, request);
                }

                return request;
            }

            function showSection(tabId, sublinkId, animate) {
                const $targetSection = getSectionElement(tabId, sublinkId);
                if (!$targetSection.length) return;

                if (!$activeSection.length) {
                    $contentSections
                        .filter('.active')
                        .not($targetSection)
                        .hide()
                        .removeClass('active');
                }

                if ($activeSection.length && !$activeSection.is($targetSection)) {
                    $activeSection.hide().removeClass('active');
                }

                $targetSection
                    .addClass('active')
                    .show();

                if (animate) {
                    $targetSection.addClass(ANIMATION_CLASSES);
                }

                loadSectionPanelsIfNeeded($targetSection).always(function () {
                    initSelect2($targetSection);
                    ensureWysiwygEditors($targetSection);
                    initDynamicOptions($targetSection);
                    initConditionalFields($targetSection);
                    initRepeaterSortable($targetSection);
                });

                $activeSection = $targetSection;
            }

            function activateTab(tabId, sublinkId, animate = true) {
                if (!tabId || !sublinkId) return;
                const $tabEl = $navContainer.find(`[data-tab-id="${tabId}"]`);
                if (!$tabEl.length) return;

                resetNavState();

                const $sublinks = $tabEl.find(SELECTORS.sublinks);
                const $arrow = $tabEl.find(SELECTORS.navArrow);
                const $navItem = $tabEl.find(SELECTORS.navItem);
                const $navItemIcon = $navItem.find(SELECTORS.navIcon);

                $navItem
                    .addClass(CLASSES.navItemActive)
                    .removeClass(CLASSES.navInactive);
                $navItemIcon
                    .addClass(CLASSES.navIconActive)
                    .removeClass(CLASSES.navIconInactive);

                if ($sublinks.length) {
                    const duration = animate ? 250 : 0;
                    $allSublinks.not($sublinks).stop(true, true).slideUp(duration);
                    $navArrows.not($arrow).removeClass(ROTATE_CLASS);
                    if (animate) {
                        $sublinks.stop(true, true).slideDown(duration);
                    } else {
                        $sublinks.stop(true, true).show();
                    }
                    $arrow.addClass(ROTATE_CLASS);
                }

                const $sublinkEl = $tabEl.find(`[data-id="${sublinkId}"]`);
                if ($sublinkEl.length) {
                    $sublinkEl.addClass(CLASSES.activeSublink);
                }

                showSection(tabId, sublinkId, animate);
            }

            function resolveTargetFromHash(hash) {
                if (!hash) return null;
                const $hashLink = findSublinkByHash($sublinkItems, hash);
                if ($hashLink.length) {
                    const $tabContainer = $hashLink.closest('[data-tab-id]');
                    return {
                        tabId: normalizeId($tabContainer.attr('data-tab-id')),
                        sublinkId: normalizeId($hashLink.attr('data-id')),
                        normalized: true
                    };
                }

                const parsedHash = parseHash(hash);
                const isPanelMatch = parsedHash.hasPanel
                    ? parsedHash.panelId.toLowerCase() === normalizedPanelId
                    : panelCount === 1;
                if (!isPanelMatch) {
                    return null;
                }

                const { tabId, sublinkId } = parsedHash;
                if (!tabId || !sublinkId) {
                    return null;
                }

                return { tabId, sublinkId, normalized: parsedHash.hasPanel || !panelId };
            }

            function handleHash(animate = true) {
                const hash = window.location.hash.substring(1);
                if (!hash) {
                    if (!isPrimaryPanel) return;
                    const defaultTarget = getDefaultTarget();
                    if (!defaultTarget) return;
                    const defaultHash = buildHash(panelId, defaultTarget.tabId, defaultTarget.sublinkId);
                    if (defaultHash) {
                        setHash(defaultHash, true);
                    }
                    activateTab(defaultTarget.tabId, defaultTarget.sublinkId, animate);
                    return;
                }

                const target = resolveTargetFromHash(hash);
                if (!target) {
                    if (!isPrimaryPanel) return;
                    const fallback = getDefaultTarget();
                    if (fallback) {
                        const fallbackHash = buildHash(panelId, fallback.tabId, fallback.sublinkId);
                        if (fallbackHash) {
                            setHash(fallbackHash, true);
                        }
                        activateTab(fallback.tabId, fallback.sublinkId, animate);
                    }
                    return;
                }

                const normalizedHash = buildHash(panelId, target.tabId, target.sublinkId);
                if (normalizedHash && normalizeHash(hash) !== normalizeHash(normalizedHash)) {
                    setHash(normalizedHash, true);
                }

                activateTab(target.tabId, target.sublinkId, animate);
            }

            $contentSections.removeClass(ANIMATION_CLASSES);
            $activeSection = $contentSections.filter('.active:visible').first();
            $allSublinks.removeAttr('style').hide();

            handleHash(false);
            $activeSection = $contentSections.filter('.active:visible').first();

            $navContainer.off('.nxtfOptions').on('click.nxtfOptions', SELECTORS.navItem, function (e) {
                e.preventDefault();

                const $container = $(this).closest('[data-tab-id]');
                const tabId = normalizeId($container.attr('data-tab-id'));
                if (!tabId) return;

                const $sublinks = $container.find(SELECTORS.sublinks);
                if (!$sublinks.length) return;

                const $firstSublink = $sublinks.find(SELECTORS.sublinkItem).first();
                if (!$firstSublink.length) return;

                const sublinkId = normalizeId($firstSublink.attr('data-id'));
                const targetHash = buildHash(panelId, tabId, sublinkId);
                if (!setHash(targetHash)) {
                    activateTab(tabId, sublinkId, true);
                }
            });

            $navContainer.on('click.nxtfOptions', SELECTORS.sublinkItem, function (e) {
                e.preventDefault();
                e.stopPropagation();

                const $sublink = $(this);
                const $tabContainer = $sublink.closest('[data-tab-id]');
                const sublinkId = normalizeId($sublink.attr('data-id'));
                const tabId = normalizeId($tabContainer.attr('data-tab-id'));
                if (!tabId || !sublinkId) return;

                const targetHash = buildHash(panelId, tabId, sublinkId);
                if (!setHash(targetHash)) {
                    activateTab(tabId, sublinkId, true);
                }
            });

            const state = {
                panelId: panelId,
                handleHash: handleHash
            };
            $panel.data(PANEL_STATE_KEY, state);
            return state;
        }

        $panels.each(function (index) {
            const state = initializePanel(index);
            if (state && panelStates.indexOf(state) === -1) {
                panelStates.push(state);
            }
        });
        bindHashChange();

        $window.on('load' + EVENT_NAMESPACE, function () {
            const $visibleSections = $panels.find(`${SELECTORS.contentSection}:visible`);
            const $scope = $visibleSections.length ? $visibleSections : $document;
            initSelect2($scope);
            ensureWysiwygEditors($scope);
            initDynamicOptions($scope);
            initConditionalFields($scope);
            initRepeaterSortable($scope);
            panelStates.forEach(function (state) {
                state.handleHash(false);
            });
        });
    });
}(jQuery));
