(function () {
    'use strict';

    // Two <details> menus in the navigation can both be open at once, and the
    // popovers overlap. Opening one closes its siblings; a click outside or Esc
    // closes them all. No library, no state -- the elements are the state.
    var navMenus = Array.prototype.slice.call(document.querySelectorAll('.nav-menu'));

    if (navMenus.length > 0) {
        var closeNavMenus = function (except) {
            navMenus.forEach(function (item) {
                if (item !== except) {
                    item.removeAttribute('open');
                }
            });
        };

        navMenus.forEach(function (item) {
            item.addEventListener('toggle', function () {
                if (item.open) {
                    closeNavMenus(item);
                }
            });
        });

        document.addEventListener('click', function (event) {
            if (!navMenus.some(function (item) { return item.contains(event.target); })) {
                closeNavMenus(null);
            }
        });

        document.addEventListener('keydown', function (event) {
            if (event.key === 'Escape') {
                closeNavMenus(null);
            }
        });
    }

    var menuButton = document.querySelector('[data-menu-toggle]');
    var menu = document.querySelector('[data-menu]');

    if (menuButton && menu) {
        var setMenuState = function (isOpen) {
            menu.hidden = !isOpen;
            menuButton.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
            menuButton.setAttribute(
                'aria-label',
                isOpen ? (menuButton.dataset.closeLabel || '') : (menuButton.dataset.openLabel || '')
            );
        };

        var mobileQuery = window.matchMedia ? window.matchMedia('(max-width: 56rem)') : null;
        setMenuState(!(mobileQuery && mobileQuery.matches));

        menuButton.addEventListener('click', function () {
            setMenuState(menu.hidden);
        });

        document.addEventListener('keydown', function (event) {
            if (event.key === 'Escape' && !menu.hidden) {
                setMenuState(false);
                menuButton.focus();
            }
        });

        if (mobileQuery && mobileQuery.addEventListener) {
            mobileQuery.addEventListener('change', function (event) {
                setMenuState(!event.matches);
            });
        }
    }

    var rememberLocale = function (locale) {
        try {
            if (locale) {
                window.localStorage.setItem('quaeriva_ui_locale', locale);
            }
        } catch (error) {
            // Storage can be unavailable in private or hardened browser modes.
        }
    };

    rememberLocale(document.documentElement.lang || '');

    document.querySelectorAll('[data-locale-switch-form]').forEach(function (form) {
        form.addEventListener('submit', function () {
            var next = form.querySelector('[data-locale-next]');
            var button = form.querySelector('[data-locale-switch]');
            var select = form.querySelector('[data-locale-select]');
            var targetLocale = select ? select.value : (form.dataset.localeTarget || '');

            if (next) {
                next.value = window.location.pathname + window.location.search + window.location.hash;
            }

            rememberLocale(targetLocale);

            if (button) {
                button.setAttribute('aria-busy', 'true');
            }
        });
    });

    window.addEventListener('pageshow', function (event) {
        if (!event.persisted) {
            return;
        }

        try {
            var expectedLocale = window.localStorage.getItem('quaeriva_ui_locale') || '';
            var renderedLocale = document.documentElement.lang || '';

            if (expectedLocale && renderedLocale && expectedLocale !== renderedLocale) {
                window.location.reload();
            }
        } catch (error) {
            // If storage is unavailable, the server remains the locale authority on the next navigation.
        }
    });

    document.querySelectorAll('form[data-progressive-search]').forEach(function (form) {
        form.addEventListener('submit', function () {
            form.setAttribute('data-submitting', 'true');
        });
    });

    var questionLanguage = document.getElementById('question-language');
    var questionCategory = document.getElementById('question-category');
    var questionTags = document.getElementById('question-tags');
    if (questionLanguage && (questionCategory || questionTags)) {
        var syncQuestionTaxonomyLocale = function () {
            var locale = String(questionLanguage.value || '').toLowerCase();
            if (questionTags) {
                Array.prototype.forEach.call(questionTags.options, function (option) {
                    var optionLocale = String(option.dataset.contentLocale || '').toLowerCase();
                    var allowed = optionLocale === '' || optionLocale === locale;
                    option.disabled = !allowed;
                    option.hidden = !allowed;
                    if (!allowed) option.selected = false;
                });
                var picker = questionTags.closest('[data-tag-picker]');
                if (picker && typeof picker._quaerivaRefresh === 'function') picker._quaerivaRefresh();
            }
        };
        var syncLanguageFromCategory = function () {
            if (!questionCategory) return;
            var selected = questionCategory.options[questionCategory.selectedIndex];
            var categoryLocale = selected ? String(selected.dataset.contentLocale || '').toLowerCase() : '';
            if (categoryLocale && questionLanguage.querySelector('option[value="' + CSS.escape(categoryLocale) + '"]')) {
                questionLanguage.value = categoryLocale;
            }
            syncQuestionTaxonomyLocale();
        };
        questionLanguage.addEventListener('change', function () {
            if (questionCategory && questionCategory.value !== '0') {
                var selected = questionCategory.options[questionCategory.selectedIndex];
                var categoryLocale = selected ? String(selected.dataset.contentLocale || '').toLowerCase() : '';
                if (categoryLocale && categoryLocale !== String(questionLanguage.value || '').toLowerCase()) questionCategory.value = '0';
            }
            syncQuestionTaxonomyLocale();
        });
        if (questionCategory) questionCategory.addEventListener('change', syncLanguageFromCategory);
        syncQuestionTaxonomyLocale();
    }


    document.querySelectorAll('[data-tag-picker]').forEach(function (picker) {
        var source = picker.querySelector('.tag-picker-source');
        var search = picker.querySelector('[data-tag-search]');
        var chips = picker.querySelector('[data-tag-chips]');
        var optionsBox = picker.querySelector('[data-tag-options]');
        var status = picker.querySelector('[data-tag-status]');
        var maxTags = Math.max(1, parseInt(picker.dataset.maxTags || '5', 10) || 5);
        var createUrl = String(picker.dataset.tagCreateUrl || '');
        var csrfToken = String(picker.dataset.csrfToken || '');
        var createFailedText = String(picker.dataset.createFailed || 'Could not create this tag. Try again.');
        var createInFlight = false;
        if (!source || !search || !chips || !optionsBox) return;

        var availableOptions = function () {
            return Array.prototype.filter.call(source.options, function (option) {
                return !option.disabled && !option.hidden;
            });
        };
        var selectedOptions = function () {
            return Array.prototype.filter.call(source.options, function (option) {
                return option.selected && !option.disabled;
            });
        };
        var normalize = function (value) {
            return String(value || '').trim().toLocaleLowerCase(document.documentElement.lang || undefined);
        };
        var renderChips = function () {
            while (chips.firstChild) chips.removeChild(chips.firstChild);
            selectedOptions().forEach(function (option) {
                var chip = document.createElement('button');
                chip.type = 'button';
                chip.className = 'tag-picker-chip';
                chip.textContent = option.textContent + ' ×';
                chip.setAttribute('aria-label', option.textContent);
                chip.addEventListener('click', function () {
                    option.selected = false;
                    renderAll();
                    search.focus();
                });
                chips.appendChild(chip);
            });
            if (status) {
                var count = selectedOptions().length;
                status.textContent = count >= maxTags ? count + ' / ' + maxTags : '';
            }
        };
        var matchingOptions = function () {
            var query = normalize(search.value);
            return availableOptions().filter(function (option) {
                return !option.selected && (query === '' || normalize(option.textContent).indexOf(query) !== -1);
            });
        };
        var selectOption = function (option) {
            if (!option || option.selected || option.disabled) return;
            if (selectedOptions().length >= maxTags) {
                if (status) status.textContent = maxTags + ' / ' + maxTags;
                return;
            }
            option.selected = true;
            search.value = '';
            renderAll();
        };
        var exactMatchingOption = function () {
            var query = normalize(search.value);
            if (query === '') return null;
            return availableOptions().find(function (option) {
                return normalize(option.textContent) === query;
            }) || null;
        };
        var currentLocale = function () {
            var localeSelect = document.getElementById('question-language');
            return localeSelect ? String(localeSelect.value || '').trim().toLowerCase() : String(document.documentElement.lang || '').trim().toLowerCase();
        };
        var createMissingTag = function () {
            var name = String(search.value || '').trim();
            if (name === '' || createInFlight || createUrl === '') return;
            if (selectedOptions().length >= maxTags) {
                if (status) status.textContent = maxTags + ' / ' + maxTags;
                return;
            }
            createInFlight = true;
            search.disabled = true;
            var body = new URLSearchParams();
            body.set('_csrf', csrfToken);
            body.set('name', name);
            body.set('content_locale', currentLocale());
            fetch(createUrl, {
                method: 'POST',
                credentials: 'same-origin',
                headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'Accept': 'application/json' },
                body: body.toString()
            }).then(function (response) {
                return response.json().catch(function () { return {}; }).then(function (payload) {
                    if (!response.ok || !payload || payload.ok !== true || !payload.tag) {
                        throw new Error(payload && payload.message ? payload.message : createFailedText);
                    }
                    return payload.tag;
                });
            }).then(function (tag) {
                var option = Array.prototype.find.call(source.options, function (item) {
                    return String(item.value) === String(tag.id);
                });
                if (!option) {
                    option = document.createElement('option');
                    option.value = String(tag.id);
                    option.textContent = String(tag.name || name);
                    option.dataset.contentLocale = String(tag.content_locale || currentLocale());
                    source.appendChild(option);
                }
                option.selected = true;
                search.value = '';
                if (status) status.textContent = '';
                renderAll();
            }).catch(function (error) {
                if (status) status.textContent = error && error.message ? error.message : createFailedText;
            }).finally(function () {
                createInFlight = false;
                search.disabled = false;
                search.focus();
            });
        };
        var renderOptions = function () {
            while (optionsBox.firstChild) optionsBox.removeChild(optionsBox.firstChild);
            var matches = matchingOptions().slice(0, 10);
            matches.forEach(function (option) {
                var button = document.createElement('button');
                button.type = 'button';
                button.className = 'tag-picker-option';
                button.textContent = option.textContent;
                button.addEventListener('mousedown', function (event) { event.preventDefault(); });
                button.addEventListener('click', function () { selectOption(option); search.focus(); });
                optionsBox.appendChild(button);
            });
            optionsBox.hidden = document.activeElement !== search || matches.length === 0;
        };
        var renderAll = function () {
            renderChips();
            renderOptions();
        };

        picker.classList.add('is-enhanced');
        picker._quaerivaRefresh = renderAll;
        search.addEventListener('input', renderOptions);
        search.addEventListener('focus', renderOptions);
        search.addEventListener('blur', function () {
            window.setTimeout(function () { optionsBox.hidden = true; }, 100);
        });
        search.addEventListener('keydown', function (event) {
            if (event.isComposing || event.keyCode === 229) return;
            if (event.key === 'Enter') {
                event.preventDefault();
                event.stopPropagation();
                var exact = exactMatchingOption();
                if (exact) {
                    selectOption(exact);
                } else {
                    createMissingTag();
                }
            } else if (event.key === 'Escape') {
                optionsBox.hidden = true;
            }
        });
        renderAll();
    });

    var icon = function (name) {
        var paths = {
            'bold': '<path d="M7 5h6.5a4.5 4.5 0 0 1 1.7 8.67A4.5 4.5 0 0 1 13.5 22H7V5Zm3 3v4h3.5a2 2 0 1 0 0-4H10Zm0 7v4h3.5a2 2 0 1 0 0-4H10Z"/>',
            'italic': '<path d="M14 5h6v3h-2.08l-4.84 8H16v3H7v-3h2.08l4.84-8H11V5h3Z"/>',
            'heading': '<path d="M5 5h3v6h8V5h3v14h-3v-5H8v5H5V5Z"/>',
            'bullet-list': '<path d="M5 6.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm0 5.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm0 5.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0ZM8 5h13v3H8V5Zm0 5.5h13v3H8v-3ZM8 16h13v3H8v-3Z"/>',
            'numbered-list': '<path d="M3 5h2v5H3V7H2V5h1Zm-1 8h3v2H2v-1.5l1.5-1.2H2v-1.8h3v1.5L3.5 14H5v2H2v-3ZM2 18h3v2H2v-1.5l1.7-1.4H2V15h3v1.5L3.5 18H5v2H2v-2ZM8 5h13v3H8V5Zm0 5.5h13v3H8v-3ZM8 16h13v3H8v-3Z"/>',
            'quote': '<path d="M6 6h6v6H8.5A3.5 3.5 0 0 0 12 15.5V18a6 6 0 0 1-6-6V6Zm10 0h6v6h-3.5a3.5 3.5 0 0 0 3.5 3.5V18a6 6 0 0 1-6-6V6Z"/>',
            'inline-code': '<path d="m8.7 7.3-5 5 5 5 2.1-2.1-2.9-2.9 2.9-2.9-2.1-2.1Zm6.6 0-2.1 2.1 2.9 2.9-2.9 2.9 2.1 2.1 5-5-5-5Z"/>',
            'code-block': '<path d="m7.7 6.3-6 5.7 6 5.7 2-2.1-3.8-3.6 3.8-3.6-2-2.1Zm8.6 0-2 2.1 3.8 3.6-3.8 3.6 2 2.1 6-5.7-6-5.7Z"/><path d="M12 4h2l-2 16h-2l2-16Z"/>',
            'link': '<path d="M10.6 13.4a4 4 0 0 0 5.66 0l2-2a4 4 0 0 0-5.66-5.66l-1.15 1.15 2.12 2.12 1.15-1.15a1 1 0 1 1 1.42 1.42l-2 2a1 1 0 0 1-1.42 0l-2.12 2.12Zm2.8-2.8a4 4 0 0 0-5.66 0l-2 2a4 4 0 1 0 5.66 5.66l1.15-1.15-2.12-2.12-1.15 1.15a1 1 0 1 1-1.42-1.42l2-2a1 1 0 0 1 1.42 0l2.12-2.12Z"/>',
            'markdown-toggle': '<path d="M3 5h18a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2Zm2 3v8h2v-4l2 3 2-3v4h2V8h-2l-2 3-2-3H5Zm10 0v3h-2v2h2v3h2v-3h2v-2h-2V8h-2Z"/>',
        };
        return '<svg class="icon icon-' + String(name) + '" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">' + (paths[name] || '') + '</svg>';
    };

    document.querySelectorAll('textarea[data-rich-editor]').forEach(function (textarea) {
        if (textarea.dataset.editorReady === '1') return;
        textarea.dataset.editorReady = '1';
        var mode = textarea.dataset.richEditor === 'comment' ? 'comment' : 'full';
        var form = textarea.closest('form');
        var tokenField = form ? form.querySelector('input[name="_csrf"]') : null;
        var previewUrl = textarea.getAttribute('data-preview-url') || '';
        var wrapper = document.createElement('div');
        wrapper.className = 'rich-editor-wrap';
        var toolbar = document.createElement('div');
        toolbar.className = 'rich-editor-toolbar';
        toolbar.setAttribute('role', 'toolbar');

        var label = function (key, fallback) {
            var value = textarea.getAttribute('data-editor-' + key);
            return value ? String(value) : fallback;
        };
        var surface = document.createElement('div');
        surface.className = 'rich-editor-surface rich-text';
        surface.contentEditable = 'true';
        surface.setAttribute('role', 'textbox');
        surface.setAttribute('aria-multiline', 'true');
        surface.setAttribute('dir', textarea.getAttribute('dir') || 'auto');
        surface.setAttribute('data-placeholder', textarea.getAttribute('placeholder') || '');
        surface.innerHTML = textarea.getAttribute('data-initial-html') || '';
        textarea.hidden = true;
        wrapper.appendChild(toolbar);
        wrapper.appendChild(surface);
        var counter = document.createElement('small');
        counter.className = 'rich-editor-counter';
        counter.setAttribute('aria-live', 'polite');
        wrapper.appendChild(counter);
        textarea.parentNode.insertBefore(wrapper, textarea);

        var allowedUrl = function (value) {
            var url = String(value || '').trim();
            if (url === '') return false;
            if (url.charAt(0) === '#') return true;
            if (url.charAt(0) === '/' && url.charAt(1) !== '/') return true;
            if (/^(?:javascript|data|vbscript):/i.test(url)) return false;
            try {
                var parsed = new URL(url, document.baseURI);
                return parsed.protocol === 'http:' || parsed.protocol === 'https:';
            } catch (error) {
                return false;
            }
        };

        var replaceTag = function (element, tagName) {
            var replacement = document.createElement(tagName);
            while (element.firstChild) replacement.appendChild(element.firstChild);
            for (var index = 0; index < element.attributes.length; index += 1) {
                var attribute = element.attributes[index];
                replacement.setAttribute(attribute.name, attribute.value);
            }
            element.replaceWith(replacement);
            return replacement;
        };

        var normalizeNode = function (node) {
            if (!node || node.nodeType !== 1) return;
            var tagName = node.tagName.toLowerCase();
            if (tagName === 'b') node = replaceTag(node, 'strong');
            if (tagName === 'i') node = replaceTag(node, 'em');
            tagName = node.tagName.toLowerCase();
            if (tagName === 'h1' || tagName === 'h2') node = replaceTag(node, 'h3');
            if (tagName === 'div') node = replaceTag(node, 'p');
            tagName = node.tagName.toLowerCase();
            var allowed = ['p', 'br', 'strong', 'em', 'code', 'pre', 'blockquote', 'ul', 'ol', 'li', 'h3', 'h4', 'h5', 'a'];
            if (allowed.indexOf(tagName) === -1) {
                while (node.firstChild) node.parentNode.insertBefore(node.firstChild, node);
                node.remove();
                return;
            }
            if (tagName === 'a') {
                var href = node.getAttribute('href') || '';
                Array.prototype.slice.call(node.attributes).forEach(function (attribute) {
                    if (attribute.name !== 'href') node.removeAttribute(attribute.name);
                });
                if (!allowedUrl(href)) node.removeAttribute('href');
            } else {
                Array.prototype.slice.call(node.attributes).forEach(function (attribute) {
                    node.removeAttribute(attribute.name);
                });
            }
            Array.prototype.slice.call(node.childNodes).forEach(normalizeNode);
            if (tagName === 'pre') {
                var firstElement = node.firstElementChild;
                if (!firstElement || firstElement.tagName.toLowerCase() !== 'code' || node.childNodes.length !== 1) {
                    var code = document.createElement('code');
                    while (node.firstChild) code.appendChild(node.firstChild);
                    node.appendChild(code);
                } else {
                    Array.prototype.slice.call(firstElement.attributes).forEach(function (attribute) {
                        firstElement.removeAttribute(attribute.name);
                    });
                }
            }
            if (['strong', 'em', 'code'].indexOf(tagName) !== -1 && node.childNodes.length === 1 && node.firstElementChild && node.firstElementChild.tagName.toLowerCase() === tagName) {
                var child = node.firstElementChild;
                while (child.firstChild) node.insertBefore(child.firstChild, child);
                child.remove();
            }
        };

        var normalizeSurface = function (root) {
            Array.prototype.slice.call(root.childNodes).forEach(normalizeNode);
        };

        var escapeText = function (value) {
            return String(value).replace(/[\\*`\[\]]/g, function (character) { return '\\' + character; });
        };
        var blockTags = ['p', 'div', 'h3', 'h4', 'h5', 'blockquote', 'ul', 'ol', 'pre'];
        var serializeInlineChildren = function (node) {
            return Array.prototype.map.call(node.childNodes, serializeNode).join('');
        };
        var serializeList = function (node, marker) {
            return Array.prototype.map.call(node.children, function (item) {
                var content = serializeInlineChildren(item);
                return marker + content;
            }).join('\n');
        };
        var serializeNode = function (node) {
            if (node.nodeType === 3) return escapeText(node.nodeValue || '');
            if (node.nodeType !== 1) return '';
            var tagName = node.tagName.toLowerCase();
            if (tagName === 'br') return '\n';
            if (tagName === 'strong') return '**' + serializeInlineChildren(node) + '**';
            if (tagName === 'em') return '*' + serializeInlineChildren(node) + '*';
            if (tagName === 'code' && (!node.parentElement || node.parentElement.tagName.toLowerCase() !== 'pre')) return '`' + serializeInlineChildren(node) + '`';
            if (tagName === 'pre') return '```\n' + (node.textContent || '').replace(/\r\n?/g, '\n') + '\n```';
            if (tagName === 'h3') return '## ' + serializeInlineChildren(node);
            if (tagName === 'h4') return '### ' + serializeInlineChildren(node);
            if (tagName === 'h5') return '#### ' + serializeInlineChildren(node);
            if (tagName === 'blockquote') return serializeInlineChildren(node).split('\n').map(function (line) { return '> ' + line; }).join('\n');
            if (tagName === 'ul') return serializeList(node, '- ');
            if (tagName === 'ol') return serializeList(node, '1. ');
            if (tagName === 'div') return serializeRoot(node);
            if (tagName === 'a') {
                var href = node.getAttribute('href') || '';
                return allowedUrl(href) ? '[' + serializeInlineChildren(node) + '](' + href + ')' : serializeInlineChildren(node);
            }
            return serializeInlineChildren(node);
        };
        var serializeRoot = function (root) {
            var parts = [];
            var inline = '';
            var flushInline = function () {
                if (inline !== '') parts.push(inline);
                inline = '';
            };
            Array.prototype.forEach.call(root.childNodes, function (node) {
                var isBlock = node.nodeType === 1 && blockTags.indexOf(node.tagName.toLowerCase()) !== -1;
                if (isBlock) {
                    flushInline();
                    parts.push(serializeNode(node));
                } else {
                    inline += serializeNode(node);
                }
            });
            flushInline();
            return parts.join('\n\n').replace(/\n{3,}/g, '\n\n').trim();
        };

        var savedRange = null;
        var selectionInSurface = function (selection) {
            return !!selection && selection.rangeCount > 0 && surface.contains(selection.anchorNode);
        };
        var saveSelection = function () {
            var selection = window.getSelection();
            if (selectionInSurface(selection)) savedRange = selection.getRangeAt(0).cloneRange();
        };
        var restoreSelection = function () {
            if (!savedRange) return;
            var selection = window.getSelection();
            selection.removeAllRanges();
            selection.addRange(savedRange);
        };
        var currentElement = function (tagName) {
            var selection = window.getSelection();
            if (!selectionInSurface(selection)) return null;
            var node = selection.anchorNode;
            if (node && node.nodeType !== 1) node = node.parentElement;
            while (node && node !== surface) {
                if (node.tagName && node.tagName.toLowerCase() === tagName) return node;
                node = node.parentElement;
            }
            return null;
        };

        var updateCounter = function () {
            var maximum = parseInt(textarea.getAttribute('maxlength') || '0', 10);
            counter.textContent = maximum > 0 ? String(textarea.value.length) + ' / ' + String(maximum) : String(textarea.value.length);
        };
        var serialize = function () {
            normalizeSurface(surface);
            textarea.value = serializeRoot(surface);
            updateCounter();
        };
        var scheduleSerialize = function () {
            window.clearTimeout(textarea.__editorTimer);
            textarea.__editorTimer = window.setTimeout(serialize, 200);
        };
        var finishCommand = function () {
            normalizeSurface(surface);
            serialize();
            saveSelection();
            updateActiveState();
        };
        var unwrapElement = function (element) {
            if (!element || !element.parentNode) return;
            var parent = element.parentNode;
            var marker = document.createTextNode('');
            parent.insertBefore(marker, element);
            while (element.firstChild) parent.insertBefore(element.firstChild, element);
            element.remove();
            var range = document.createRange();
            range.setStartAfter(marker);
            range.collapse(true);
            marker.remove();
            var selection = window.getSelection();
            selection.removeAllRanges();
            selection.addRange(range);
        };
        var wrapSelection = function (tagName) {
            restoreSelection();
            surface.focus();
            var selection = window.getSelection();
            if (!selection || selection.rangeCount === 0) return null;
            var range = selection.getRangeAt(0);
            var wrapperNode = document.createElement(tagName);
            if (range.collapsed) {
                wrapperNode.appendChild(document.createTextNode('​'));
                range.insertNode(wrapperNode);
            } else {
                wrapperNode.appendChild(range.extractContents());
                range.insertNode(wrapperNode);
            }
            range.selectNodeContents(wrapperNode);
            selection.removeAllRanges();
            selection.addRange(range);
            return wrapperNode;
        };
        var currentBlock = function () {
            var selection = window.getSelection();
            if (!selectionInSurface(selection)) return null;
            var node = selection.anchorNode;
            if (node && node.nodeType !== 1) node = node.parentElement;
            while (node && node !== surface) {
                var tag = node.tagName ? node.tagName.toLowerCase() : '';
                if (['p', 'h3', 'h4', 'h5', 'blockquote', 'pre', 'li'].indexOf(tag) !== -1) return node;
                node = node.parentElement;
            }
            return null;
        };
        var replaceCurrentBlock = function (tagName) {
            restoreSelection();
            surface.focus();
            var block = currentBlock();
            if (!block) {
                var created = document.createElement(tagName === 'pre' ? 'pre' : tagName);
                if (tagName === 'pre') created.appendChild(document.createElement('code'));
                surface.appendChild(created);
                return created;
            }
            if (block.tagName.toLowerCase() === 'li') block = block.parentElement;
            if (!block || block === surface) return null;
            if (tagName === 'pre') {
                var pre = document.createElement('pre');
                var code = document.createElement('code');
                while (block.firstChild) code.appendChild(block.firstChild);
                pre.appendChild(code);
                block.replaceWith(pre);
                return pre;
            }
            if (block.tagName.toLowerCase() === 'pre') {
                var paragraph = document.createElement(tagName);
                var codeChild = block.querySelector('code');
                while ((codeChild || block).firstChild) paragraph.appendChild((codeChild || block).firstChild);
                block.replaceWith(paragraph);
                return paragraph;
            }
            return replaceTag(block, tagName);
        };
        var toggleList = function (tagName) {
            restoreSelection();
            surface.focus();
            var existing = currentElement(tagName);
            if (existing) {
                var fragment = document.createDocumentFragment();
                Array.prototype.slice.call(existing.children).forEach(function (item) {
                    var paragraph = document.createElement('p');
                    while (item.firstChild) paragraph.appendChild(item.firstChild);
                    fragment.appendChild(paragraph);
                });
                existing.replaceWith(fragment);
                return;
            }
            var block = currentBlock();
            if (!block) return;
            if (block.tagName.toLowerCase() === 'li') block = block.parentElement;
            var list = document.createElement(tagName);
            var item = document.createElement('li');
            while (block.firstChild) item.appendChild(block.firstChild);
            list.appendChild(item);
            block.replaceWith(list);
        };
        var linkDialog = document.createElement('dialog');
        linkDialog.className = 'rich-editor-link-dialog';
        var linkForm = document.createElement('form');
        linkForm.method = 'dialog';
        var linkInput = document.createElement('input');
        linkInput.type = 'url';
        linkInput.dir = 'ltr';
        linkInput.placeholder = 'https://';
        linkInput.setAttribute('aria-label', label('link-url-prompt', 'Enter a URL'));
        var linkError = document.createElement('small');
        linkError.className = 'rich-editor-link-error';
        var linkCancel = document.createElement('button');
        linkCancel.type = 'button';
        linkCancel.textContent = label('cancel', 'Cancel');
        var linkSave = document.createElement('button');
        linkSave.type = 'submit';
        linkSave.textContent = label('link', 'Link');
        linkForm.appendChild(linkInput);
        linkForm.appendChild(linkError);
        linkForm.appendChild(linkCancel);
        linkForm.appendChild(linkSave);
        linkDialog.appendChild(linkForm);
        wrapper.appendChild(linkDialog);
        linkCancel.addEventListener('click', function () { linkDialog.close(); surface.focus(); restoreSelection(); });
        linkForm.addEventListener('submit', function (event) {
            event.preventDefault();
            var url = String(linkInput.value || '').trim();
            if (!allowedUrl(url)) {
                linkError.textContent = label('invalid-link', 'This link URL is not allowed.');
                return;
            }
            linkError.textContent = '';
            restoreSelection();
            surface.focus();
            var selection = window.getSelection();
            if (!selection || selection.rangeCount === 0) return;
            var range = selection.getRangeAt(0);
            var existing = currentElement('a');
            if (existing) {
                existing.setAttribute('href', url);
            } else {
                var anchor = document.createElement('a');
                anchor.setAttribute('href', url);
                if (range.collapsed) anchor.appendChild(document.createTextNode(url));
                else anchor.appendChild(range.extractContents());
                range.insertNode(anchor);
                range.selectNodeContents(anchor);
                selection.removeAllRanges();
                selection.addRange(range);
            }
            linkDialog.close();
            finishCommand();
        });
        var toggleInline = function (tagName) {
            var existing = currentElement(tagName);
            if (existing) unwrapElement(existing);
            else wrapSelection(tagName);
        };
        var run = function (command) {
            restoreSelection();
            surface.focus();
            if (command === 'code') {
                toggleInline('code');
            } else if (command === 'link') {
                var existing = currentElement('a');
                linkInput.value = existing ? existing.getAttribute('href') || '' : '';
                linkError.textContent = '';
                if (typeof linkDialog.showModal === 'function') linkDialog.showModal();
                else linkDialog.setAttribute('open', 'open');
                window.setTimeout(function () { linkInput.focus(); linkInput.select(); }, 0);
                return;
            } else if (command === 'heading') {
                var heading = currentElement('h3') || currentElement('h4') || currentElement('h5');
                var next = heading ? ({h3: 'h4', h4: 'h5', h5: 'p'}[heading.tagName.toLowerCase()] || 'h3') : 'h3';
                replaceCurrentBlock(next);
            } else if (command === 'list') {
                toggleList('ul');
            } else if (command === 'numbered-list') {
                toggleList('ol');
            } else if (command === 'quote') {
                var quote = currentElement('blockquote');
                replaceCurrentBlock(quote ? 'p' : 'blockquote');
            } else if (command === 'code-block') {
                var pre = currentElement('pre');
                replaceCurrentBlock(pre ? 'p' : 'pre');
            } else if (command === 'bold') {
                toggleInline('strong');
            } else if (command === 'italic') {
                toggleInline('em');
            }
            finishCommand();
        };

        var commandGroups = mode === 'comment' ? [
            [['bold', 'bold', 'bold'], ['italic', 'italic', 'italic'], ['code', 'inline-code', 'code'], ['link', 'link', 'link']]
        ] : [
            [['bold', 'bold', 'bold'], ['italic', 'italic', 'italic']],
            [['heading', 'heading', 'heading'], ['list', 'bullet-list', 'list'], ['numbered-list', 'numbered-list', 'numbered-list'], ['quote', 'quote', 'quote']],
            [['code', 'inline-code', 'code'], ['code-block', 'code-block', 'code-block'], ['link', 'link', 'link']]
        ];
        commandGroups.forEach(function (groupItems) {
            var group = document.createElement('div');
            group.className = 'rich-editor-group';
            group.setAttribute('role', 'group');
            groupItems.forEach(function (item) {
                var command = item[0];
                var button = document.createElement('button');
                button.type = 'button';
                button.className = 'rich-editor-button';
                button.innerHTML = icon(item[1]);
                button.title = label(item[2], item[2]);
                button.setAttribute('aria-label', label(item[2], item[2]));
                button.setAttribute('aria-pressed', 'false');
                button.dataset.editorCommand = command;
                if (command === 'heading') button.setAttribute('data-editor-heading-level', 'h3');
                button.addEventListener('mousedown', function (event) {
                    event.preventDefault();
                    saveSelection();
                });
                button.addEventListener('click', function () { run(command); });
                group.appendChild(button);
            });
            toolbar.appendChild(group);
        });

        var sourceButton = document.createElement('button');
        sourceButton.type = 'button';
        sourceButton.className = 'rich-editor-source';
        sourceButton.insertAdjacentHTML('afterbegin', icon('markdown-toggle'));
        var sourceLabel = document.createElement('span');
        sourceLabel.className = 'rich-editor-source-label';
        sourceLabel.textContent = label('markdown', 'Markdown');
        sourceButton.appendChild(sourceLabel);
        sourceButton.title = label('markdown', 'Markdown');
        sourceButton.setAttribute('aria-label', label('markdown', 'Markdown'));
        sourceButton.setAttribute('aria-pressed', 'false');
        toolbar.appendChild(sourceButton);

        var updateActiveState = function () {
            var selection = window.getSelection();
            var inside = selectionInSurface(selection);
            Array.prototype.forEach.call(toolbar.querySelectorAll('[data-editor-command]'), function (button) {
                var command = button.dataset.editorCommand;
                var active = false;
                if (inside) {
                    if (command === 'heading') active = !!(currentElement('h3') || currentElement('h4') || currentElement('h5'));
                    else if (command === 'quote') active = !!currentElement('blockquote');
                    else if (command === 'code') active = !!currentElement('code') && !currentElement('pre');
                    else if (command === 'code-block') active = !!currentElement('pre');
                    else if (command === 'list') active = !!currentElement('ul');
                    else if (command === 'numbered-list') active = !!currentElement('ol');
                    else if (command === 'bold') active = !!currentElement('strong');
                    else if (command === 'italic') active = !!currentElement('em');
                    else if (command === 'link') active = !!currentElement('a');
                }
                button.setAttribute('aria-pressed', active ? 'true' : 'false');
            });
        };
        document.addEventListener('selectionchange', function () {
            saveSelection();
            updateActiveState();
        });

        var requestPreview = function () {
            if (!previewUrl || !tokenField || typeof window.fetch !== 'function') return Promise.resolve(null);
            var payload = new FormData();
            payload.append('_csrf', tokenField.value);
            payload.append('mode', mode);
            payload.append('body', textarea.value);
            return window.fetch(previewUrl, {
                method: 'POST',
                body: payload,
                credentials: 'same-origin',
                headers: { 'Accept': 'application/json' }
            }).then(function (response) { return response.ok ? response.json() : null; }).catch(function () { return null; });
        };
        var sourceMode = false;
        sourceButton.addEventListener('click', function () {
            window.clearTimeout(textarea.__editorTimer);
            if (!sourceMode) {
                serialize();
                sourceMode = true;
                sourceButton.setAttribute('aria-pressed', 'true');
                surface.hidden = true;
                textarea.hidden = false;
                textarea.focus();
                return;
            }
            requestPreview().then(function (data) {
                if (data && data.ok === true) surface.innerHTML = data.html || '';
                normalizeSurface(surface);
                sourceMode = false;
                sourceButton.setAttribute('aria-pressed', 'false');
                textarea.hidden = true;
                surface.hidden = false;
                surface.focus();
                saveSelection();
            });
        });

        surface.addEventListener('input', function () {
            normalizeSurface(surface);
            scheduleSerialize();
        });
        surface.addEventListener('keydown', function (event) {
            if (!(event.ctrlKey || event.metaKey) || event.altKey) return;
            var command = event.key.toLowerCase() === 'b' ? 'bold' : event.key.toLowerCase() === 'i' ? 'italic' : event.key.toLowerCase() === 'k' ? 'link' : null;
            if (!command) return;
            event.preventDefault();
            saveSelection();
            run(command);
        });
        var imageUploadUrl = textarea.getAttribute('data-image-upload-url') || '';
        var imageUploadingLabel = textarea.getAttribute('data-editor-image-uploading') || '';
        var imageFailedLabel = textarea.getAttribute('data-editor-image-failed') || '';

        /**
         * Upload one image file and replace the placeholder with its markdown.
         *
         * A placeholder is inserted first so the writer sees the paste land and
         * cannot lose their place while the request is in flight. On failure it
         * is replaced with a short message rather than left behind.
         */
        var uploadImage = function (file) {
            if (!imageUploadUrl || !file || typeof window.fetch !== 'function') return false;
            if (!/^image\//.test(file.type || '')) return false;

            var token = (form && form.querySelector('input[name="_csrf"]'))
                ? form.querySelector('input[name="_csrf"]').value
                : '';
            var placeholder = document.createTextNode(imageUploadingLabel || '…');
            restoreSelection();
            var selection = window.getSelection();
            if (selection && selection.rangeCount > 0) {
                var range = selection.getRangeAt(0);
                range.deleteContents();
                range.insertNode(placeholder);
            } else {
                surface.appendChild(placeholder);
            }
            serialize();

            var payload = new FormData();
            payload.append('image', file);
            payload.append('_csrf', token);

            window.fetch(imageUploadUrl, {
                method: 'POST',
                body: payload,
                credentials: 'same-origin'
            }).then(function (response) {
                return response.json();
            }).then(function (data) {
                var replacement;
                if (data && data.ok && data.url) {
                    // The alt text is the writer's job; the file name is a
                    // starting point they can edit, never a claim about content.
                    var alt = (file.name || '').replace(/\.[^.]+$/, '').replace(/[\[\]()]/g, '');
                    replacement = document.createTextNode('![' + alt + '](' + data.url + ')');
                } else {
                    replacement = document.createTextNode(imageFailedLabel || '');
                }
                if (placeholder.parentNode) {
                    placeholder.parentNode.replaceChild(replacement, placeholder);
                }
                normalizeSurface(surface);
                serialize();
                saveSelection();
            }).catch(function () {
                if (placeholder.parentNode) {
                    placeholder.parentNode.replaceChild(
                        document.createTextNode(imageFailedLabel || ''),
                        placeholder
                    );
                }
                serialize();
            });

            return true;
        };

        surface.addEventListener('drop', function (event) {
            if (!imageUploadUrl || !event.dataTransfer || !event.dataTransfer.files) return;
            var dropped = event.dataTransfer.files[0];
            if (!dropped || !/^image\//.test(dropped.type || '')) return;
            event.preventDefault();
            saveSelection();
            uploadImage(dropped);
        });

        surface.addEventListener('paste', function (event) {
            if (!event.clipboardData) return;
            // An image on the clipboard is handled before the text branches,
            // which would otherwise insert nothing at all for a screenshot.
            if (imageUploadUrl && event.clipboardData.files && event.clipboardData.files.length > 0) {
                var pasted = event.clipboardData.files[0];
                if (pasted && /^image\//.test(pasted.type || '')) {
                    event.preventDefault();
                    saveSelection();
                    if (uploadImage(pasted)) return;
                }
            }
            event.preventDefault();
            saveSelection();
            restoreSelection();
            var selection = window.getSelection();
            if (!selection || selection.rangeCount === 0) return;
            var range = selection.getRangeAt(0);
            var html = event.clipboardData.getData('text/html');
            var plain = event.clipboardData.getData('text/plain');
            if (html) {
                var template = document.createElement('template');
                template.innerHTML = html;
                normalizeSurface(template.content);
                range.deleteContents();
                range.insertNode(template.content);
            } else if (typeof plain === 'string') {
                range.deleteContents();
                var trimmedPlain = plain.trim();
                if (trimmedPlain === plain && allowedUrl(trimmedPlain) && /^(?:https?:\/\/|\/[^\/])/.test(trimmedPlain)) {
                    var anchor = document.createElement('a');
                    anchor.href = trimmedPlain;
                    anchor.textContent = trimmedPlain;
                    range.insertNode(anchor);
                } else {
                    range.insertNode(document.createTextNode(plain));
                }
            }
            normalizeSurface(surface);
            serialize();
            saveSelection();
        });
        if (form) form.addEventListener('submit', function () { serialize(); });
        normalizeSurface(surface);
        serialize();
        updateActiveState();

        if (new URLSearchParams(window.location.search).get('editor-selfcheck') === '1' && !window.__quaerivaEditorSelfCheckRan) {
            window.__quaerivaEditorSelfCheckRan = true;
            var selfChecks = [
                ['strong', function () { var node = document.createElement('p'); var child = document.createElement('strong'); child.textContent = 'bold'; node.appendChild(child); return node; }, '**bold**'],
                ['em', function () { var node = document.createElement('p'); var child = document.createElement('em'); child.textContent = 'italic'; node.appendChild(child); return node; }, '*italic*'],
                ['inline code', function () { var node = document.createElement('p'); var child = document.createElement('code'); child.textContent = 'code'; node.appendChild(child); return node; }, '`code`'],
                ['code block', function () { var node = document.createElement('pre'); var child = document.createElement('code'); child.textContent = 'const x = 1;'; node.appendChild(child); return node; }, '```\nconst x = 1;\n```'],
                ['heading', function () { var node = document.createElement('h3'); node.textContent = 'Heading'; return node; }, '## Heading'],
                ['quote', function () { var node = document.createElement('blockquote'); node.textContent = 'Quoted'; return node; }, '> Quoted'],
                ['unordered list', function () { var node = document.createElement('ul'); var item = document.createElement('li'); item.textContent = 'One'; node.appendChild(item); return node; }, '- One'],
                ['ordered list', function () { var node = document.createElement('ol'); var item = document.createElement('li'); item.textContent = 'One'; node.appendChild(item); return node; }, '1. One'],
                ['link', function () { var node = document.createElement('p'); var child = document.createElement('a'); child.href = 'https://e.com'; child.textContent = 'x'; node.appendChild(child); return node; }, '[x](https://e.com)'],
                ['line break', function () { var node = document.createElement('p'); node.appendChild(document.createTextNode('one')); node.appendChild(document.createElement('br')); node.appendChild(document.createTextNode('two')); return node; }, 'one\ntwo'],
                ['paragraphs', function () { var node = document.createElement('div'); var first = document.createElement('p'); first.textContent = 'one'; var second = document.createElement('p'); second.textContent = 'two'; node.appendChild(first); node.appendChild(second); return node; }, 'one\n\ntwo'],
                ['escaped text', function () { var node = document.createElement('p'); node.textContent = 'literal * and `'; return node; }, 'literal \\* and \\`'],
                ['Arabic English', function () { var node = document.createElement('p'); node.textContent = 'مرحبا Quaeriva'; return node; }, 'مرحبا Quaeriva']
            ];
            var selfCheckPassed = 0;
            selfChecks.forEach(function (check) {
                var root = document.createElement('div');
                root.appendChild(check[1]());
                var actual = serializeRoot(root);
                if (actual === check[2]) selfCheckPassed += 1;
                else console.error('RichEditor self-check failed: ' + check[0] + ' expected ' + check[2] + ' got ' + actual);
            });
            console.log('Quaeriva RichEditor self-check: ' + selfCheckPassed + '/' + selfChecks.length + ' passed');
        }
    });

    var titleInput = document.querySelector('#question-title[data-suggest-url]');
    var suggestionBox = document.getElementById('duplicate-suggestions');
    if (titleInput && suggestionBox) {
        var suggestionList = suggestionBox.querySelector('ul');
        var timer = null;
        var controller = null;
        var renderSuggestions = function (items) {
            while (suggestionList && suggestionList.firstChild) suggestionList.removeChild(suggestionList.firstChild);
            if (!suggestionList || !Array.isArray(items) || items.length === 0) {
                suggestionBox.hidden = true;
                return;
            }
            items.forEach(function (item) {
                if (!item || !item.title || !item.url) return;
                var li = document.createElement('li');
                var link = document.createElement('a');
                link.href = String(item.url);
                link.textContent = String(item.title);
                li.appendChild(link);
                suggestionList.appendChild(li);
            });
            suggestionBox.hidden = suggestionList.children.length === 0;
        };
        titleInput.addEventListener('input', function () {
            window.clearTimeout(timer);
            var query = titleInput.value.trim();
            if (query.length < 3) {
                if (controller) controller.abort();
                renderSuggestions([]);
                return;
            }
            timer = window.setTimeout(function () {
                if (controller) controller.abort();
                controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
                var separator = titleInput.dataset.suggestUrl.indexOf('?') === -1 ? '?' : '&';
                var suggestParams = ['q=' + encodeURIComponent(query)];
                if (questionLanguage && questionLanguage.value) suggestParams.push('content_locale=' + encodeURIComponent(questionLanguage.value));
                if (questionCategory && questionCategory.value && questionCategory.value !== '0') suggestParams.push('category_id=' + encodeURIComponent(questionCategory.value));
                if (questionTags) {
                    Array.prototype.forEach.call(questionTags.selectedOptions || [], function (option) {
                        suggestParams.push('tag_ids[]=' + encodeURIComponent(option.value));
                    });
                }
                fetch(titleInput.dataset.suggestUrl + separator + suggestParams.join('&'), {
                    headers: { 'Accept': 'application/json' },
                    credentials: 'same-origin',
                    signal: controller ? controller.signal : undefined
                }).then(function (response) {
                    return response.ok ? response.json() : { questions: [] };
                }).then(function (payload) {
                    renderSuggestions(payload && payload.questions ? payload.questions : []);
                }).catch(function (error) {
                    if (!error || error.name !== 'AbortError') renderSuggestions([]);
                });
            }, 350);
        });
    }

    document.querySelectorAll('[data-share-question], [data-share-topic]').forEach(function (button) {
        button.addEventListener('click', function () {
            var configured = String(button.dataset.shareUrl || '');
            var shareUrl = configured ? new URL(configured, window.location.href).href : window.location.href;
            if (navigator.share) {
                navigator.share({ title: document.title, url: shareUrl }).catch(function () {});
                return;
            }
            if (navigator.clipboard && navigator.clipboard.writeText) {
                navigator.clipboard.writeText(shareUrl).catch(function () {});
            }
        });
    });

    var initThemeMode = function () {
        var toggle = document.getElementById('quaerivaThemeToggle');
        var icon = toggle ? toggle.querySelector('.theme-mode-icon') : null;
        var updateIcon = function (mode) {
            if (icon) {
                icon.textContent = mode === 'dark' ? '☀️' : '🌙';
            }
        };

        var currentMode = document.documentElement.getAttribute('data-theme-mode') || 'light';
        updateIcon(currentMode);

        if (toggle) {
            toggle.addEventListener('click', function () {
                var newMode = (document.documentElement.getAttribute('data-theme-mode') === 'dark') ? 'light' : 'dark';
                document.documentElement.setAttribute('data-theme-mode', newMode);
                try {
                    localStorage.setItem('quaeriva_theme_mode', newMode);
                } catch (e) {}
                updateIcon(newMode);
            });
        }
    };
    initThemeMode();

    /**
     * Scroll-to-top.
     *
     * A sentinel at the top of the document is watched by an IntersectionObserver,
     * so the button's visibility costs nothing per scroll frame. Without the
     * observer the fallback is simply to leave the button hidden -- the page is
     * fully usable without it.
     */
    var initScrollToTop = function () {
        var button = document.getElementById('scroll-to-top');
        var sentinel = document.getElementById('scroll-top-sentinel');
        if (!button || !sentinel || typeof window.IntersectionObserver !== 'function') {
            return;
        }

        var observer = new window.IntersectionObserver(function (entries) {
            var entry = entries[0];
            if (!entry) {
                return;
            }
            button.hidden = entry.isIntersecting;
        }, { rootMargin: '300px 0px 0px 0px' });
        observer.observe(sentinel);

        button.addEventListener('click', function () {
            var reduceMotion = window.matchMedia
                && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
            window.scrollTo({ top: 0, behavior: reduceMotion ? 'auto' : 'smooth' });
            // Returning focus to the top keeps keyboard order in step with the view.
            var focusTarget = document.querySelector('.brand') || document.body;
            if (focusTarget && typeof focusTarget.focus === 'function') {
                focusTarget.focus({ preventScroll: true });
            }
        });
    };
    initScrollToTop();

    /**
     * Collapsible header search.
     *
     * The button only exists under the narrow breakpoint, so this is inert on a
     * wide screen. Focus moves into the field on open, because a search box that
     * appears without focus makes the reader tap twice.
     */
    var initHeaderSearch = function () {
        var toggle = document.getElementById('header-search-toggle');
        var form = document.getElementById('header-search');
        if (!toggle || !form) {
            return;
        }

        toggle.addEventListener('click', function () {
            var open = form.classList.toggle('is-open');
            toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
            if (open) {
                var field = form.querySelector('input[type="search"]');
                if (field) field.focus();
            }
        });

        form.addEventListener('keydown', function (event) {
            if (event.key !== 'Escape') return;
            form.classList.remove('is-open');
            toggle.setAttribute('aria-expanded', 'false');
            toggle.focus();
        });
    };
    initHeaderSearch();
}());
