
        let currentSearchType = null;
        let searchDebounceIds = null;
        const caseIds = {{ case.id }};
        const currentCaseTitle = {{ (case.titel or '') | tojson }};
        let caseAddPanelInitialized = false;
        let caseAddActiveAction = null;
        let caseAddOriginalShowRelModal = null;
        const CASE_ADD_ACTIONS = [
            { action: 'assign', label: 'Tildel sag', icon: 'bi-person-check', moduleKey: null, relFn: 'openRelAssignModal' },
            { action: 'time', label: 'Tidregistrering', icon: 'bi-clock', moduleKey: 'time', relFn: 'openRelTimeModal' },
            { action: 'note', label: 'Kommentar', icon: 'bi-chat-left-text', moduleKey: 'solution', relFn: 'openRelNoteModal' },
            { action: 'reminder', label: 'Pamindelse', icon: 'bi-bell', moduleKey: 'reminders', relFn: 'openRelReminderModal' },
            { action: 'pipeline', label: 'Salgspipeline', icon: 'bi-graph-up-arrow', moduleKey: 'pipeline', relFn: 'openRelPipelineModal' },
            { action: 'files', label: 'Filer', icon: 'bi-paperclip', moduleKey: 'files', relFn: 'openRelFilesModal' },
            { action: 'hardware', label: 'Hardware', icon: 'bi-cpu', moduleKey: 'hardware', relFn: 'openRelHardwareModal' },
            { action: 'todo', label: 'Opgave', icon: 'bi-check2-square', moduleKey: 'todo-steps', relFn: 'openRelTodoModal' },
            { action: 'solution', label: 'Losning', icon: 'bi-lightbulb', moduleKey: 'solution', relFn: 'openRelSolutionModal' },
            { action: 'sales', label: 'Varekob og salg', icon: 'bi-bag', moduleKey: 'sales', relFn: 'openRelSalesModal' },
            { action: 'subscription', label: 'Abonnement', icon: 'bi-arrow-repeat', moduleKey: 'subscription', relFn: 'openRelSubscriptionModal' },
            { action: 'email', label: 'Send email', icon: 'bi-envelope', moduleKey: 'emails', relFn: 'openRelEmailModal' }
        ];

        async function openCaseModuleAddPanel() {
            if (typeof loadModulePrefs === 'function') {
                await loadModulePrefs();
            }

            const panel = document.getElementById('caseAddSidePanel');
            const backdrop = document.getElementById('caseAddSideBackdrop');
            if (!panel || !backdrop) return;

            backdrop.classList.add('open');
            panel.classList.add('open');
            panel.setAttribute('aria-hidden', 'false');

            if (!caseAddOriginalShowRelModal && typeof window._showRelModal === 'function') {
                caseAddOriginalShowRelModal = window._showRelModal;
            }
            if (typeof caseAddOriginalShowRelModal === 'function') {
                window._showRelModal = renderCaseAddWorkspaceModal;
            }

            renderCaseAddActionList(caseAddActiveAction);
            caseAddPanelInitialized = true;
        }

        function closeCaseModuleAddPanel() {
            const panel = document.getElementById('caseAddSidePanel');
            const backdrop = document.getElementById('caseAddSideBackdrop');
            if (!panel || !backdrop) return;

            panel.classList.remove('open');
            panel.setAttribute('aria-hidden', 'true');
            backdrop.classList.remove('open');

            if (typeof caseAddOriginalShowRelModal === 'function') {
                window._showRelModal = caseAddOriginalShowRelModal;
            }
        }

        function renderCaseAddWorkspaceModal(title, bodyHtml, footerBtns) {
            const workspace = document.getElementById('caseAddSideWorkspace');
            if (!workspace) return;

            workspace.innerHTML = `
                <div id="relQaModalEl" class="d-flex flex-column gap-2">
                    <div class="section-title">${title}</div>
                    <div id="relQaModalBody">${bodyHtml}</div>
                    <div id="relQaModalFooter" class="d-flex justify-content-end gap-2 border-top pt-2">
                        <button class="btn btn-sm btn-outline-secondary" type="button" onclick="closeCaseModuleAddPanel()">Luk</button>
                        ${footerBtns || ''}
                    </div>
                </div>
            `;

            workspace.querySelectorAll('form').forEach((formEl) => {
                formEl.addEventListener('submit', (evt) => evt.preventDefault());
            });

            workspace.querySelectorAll('#relQaModalFooter button').forEach((btnEl) => {
                if (!btnEl.getAttribute('type')) {
                    btnEl.setAttribute('type', 'button');
                }
            });
        }

        function _isCaseAddModuleEnabled(actionConfig) {
            if (!actionConfig?.moduleKey) return true;
            if (actionConfig.moduleKey === 'time') return true;
            return modulePrefs[actionConfig.moduleKey] !== false;
        }

        function _renderCaseAddModuleToggle(actionConfig) {
            if (!actionConfig?.moduleKey) {
                return '<span class="d-inline-block" style="width:1rem; height:1rem;"></span>';
            }

            const isTimeModule = actionConfig.moduleKey === 'time';
            const isChecked = _isCaseAddModuleEnabled(actionConfig);
            return `<input type="checkbox" class="case-add-module-toggle" ${isChecked ? 'checked' : ''} ${isTimeModule ? 'disabled' : ''} onchange="toggleModulePref('${actionConfig.moduleKey}', this.checked)">`;
        }

        function renderCaseAddActionList(preferredAction = null) {
            const listEl = document.getElementById('caseAddModuleList');
            if (!listEl) return;

            const actions = CASE_ADD_ACTIONS;
            if (!actions.length) {
                listEl.innerHTML = '<div class="text-muted small">Ingen aktive moduler fundet.</div>';
                return;
            }

            listEl.innerHTML = actions.map((cfg) => `
                <div class="case-add-module-item">
                    <button type="button" class="case-add-module-btn" id="caseAddAction_${cfg.action}" onclick="openCaseAddAction('${cfg.action}')">
                        <i class="bi ${cfg.icon} me-1"></i>${cfg.label}
                    </button>
                    ${_renderCaseAddModuleToggle(cfg)}
                </div>
            `).join('');

            const fallbackAction = actions[0]?.action || null;
            const nextAction = actions.some((cfg) => cfg.action === preferredAction) ? preferredAction : fallbackAction;
            if (nextAction) {
                openCaseAddAction(nextAction);
            }
        }

        async function openCaseAddAction(actionName) {
            document.querySelectorAll('.case-add-module-btn').forEach((btn) => btn.classList.remove('active'));
            document.getElementById(`caseAddAction_${actionName}`)?.classList.add('active');
            caseAddActiveAction = actionName;

            const action = CASE_ADD_ACTIONS.find((cfg) => cfg.action === actionName);
            const workspace = document.getElementById('caseAddSideWorkspace');
            if (!action || !workspace) return;

            workspace.innerHTML = '<div class="text-muted small"><span class="spinner-border spinner-border-sm me-2"></span>Indlaeser formular...</div>';

            const relFn = window[action.relFn];
            if (typeof relFn !== 'function') {
                workspace.innerHTML = '<div class="text-danger small">Modulformular er ikke tilgaengelig endnu.</div>';
                return;
            }

            const existingRelQaEl = document.getElementById('relQaModalEl');
            if (existingRelQaEl && !workspace.contains(existingRelQaEl)) {
                const existingModalInstance = window.bootstrap?.Modal?.getInstance(existingRelQaEl);
                if (existingModalInstance) {
                    existingModalInstance.hide();
                }
                existingRelQaEl.remove();
            }

            try {
                await Promise.resolve(relFn(caseIds, currentCaseTitle));
            } catch (error) {
                console.error('Could not load module add form', error);
                workspace.innerHTML = '<div class="text-danger small">Kunne ikke indlaese formularen.</div>';
            }
        }

        document.addEventListener('keydown', (event) => {
            if (event.key === 'Escape') {
                const panel = document.getElementById('caseAddSidePanel');
                if (panel && panel.classList.contains('open')) {
                    closeCaseModuleAddPanel();
                }
            }
        });

        function openSearchModal(type) {
            currentSearchType = type;
            const titles = {
                'hardware': 'Tilføj Hardware',
                'location': 'Tilføj Lokation',
                'contact': 'Tilføj Kontakt',
                'customer': 'Tilføj Kunde'
            };
            document.getElementById('entitySearchTitle').textContent = titles[type] || 'Søg';
            document.getElementById('entitySearchInput').value = '';
            document.getElementById('entitySearchResults').innerHTML = '';
            
            const modal = new bootstrap.Modal(document.getElementById('entitySearchModal'));
            modal.show();
            
            setTimeout(() => document.getElementById('entitySearchInput').focus(), 500);
        }

        document.getElementById('entitySearchInput').addEventListener('input', function(e) {
            clearTimeout(searchDebounceIds);
            const query = e.target.value.trim();
            if (query.length < 2) {
                document.getElementById('entitySearchResults').innerHTML = '';
                return;
            }
            
            searchDebounceIds = setTimeout(() => performSearch(query), 300);
        });

        async function performSearch(query) {
            document.getElementById('entitySearchSpinner').classList.remove('d-none');
            document.getElementById('entitySearchResults').classList.add('d-none');
            
            try {
                let url = '';
                if (currentSearchType === 'hardware') url = `/api/v1/search/hardware?q=${encodeURIComponent(query)}`;
                else if (currentSearchType === 'location') url = `/api/v1/search/locations?q=${encodeURIComponent(query)}`;
                else if (currentSearchType === 'contact') url = `/api/v1/search/contacts?q=${encodeURIComponent(query)}`;
                else if (currentSearchType === 'customer') url = `/api/v1/search/customers?q=${encodeURIComponent(query)}`;
                
                const res = await fetch(url);
                if (!res.ok) throw new Error('Search failed');
                const results = await res.json();
                renderResults(results);
            } catch (e) {
                console.error(e);
                document.getElementById('entitySearchResults').innerHTML = '<div class="text-danger text-center p-3">Fejl ved søgning</div>';
            } finally {
                document.getElementById('entitySearchSpinner').classList.add('d-none');
                document.getElementById('entitySearchResults').classList.remove('d-none');
            }
        }

        function renderResults(results) {
            const container = document.getElementById('entitySearchResults');
            if (results.length === 0) {
                container.innerHTML = '<div class="text-muted text-center p-3">Ingen resultater fundet</div>';
                return;
            }
            
            container.innerHTML = results.map(item => {
                let title = '', subtitle = '', icon = '', id = item.id;
                
                if (currentSearchType === 'hardware') {
                    title = `${item.brand} ${item.model}`;
                    subtitle = `SN: ${item.serial_number}`;
                    icon = 'bi-laptop';
                } else if (currentSearchType === 'location') {
                    title = item.name;
                    subtitle = `${item.address_street || ''} ${item.address_city || ''}`;
                    icon = 'bi-geo-alt';
                } else if (currentSearchType === 'contact') {
                    title = `${item.first_name} ${item.last_name}`;
                    subtitle = item.email;
                    icon = 'bi-person';
                } else if (currentSearchType === 'customer') {
                    title = item.name;
                    subtitle = `CVR: ${item.cvr_nummer || 'N/A'}`;
                    icon = 'bi-building';
                }
                
                return `
                    <button type="button" class="list-group-item list-group-item-action d-flex align-items-center" onclick="addEntity(${id})">
                        <div class="me-3 fs-4 text-muted"><i class="bi ${icon}"></i></div>
                        <div>
                            <div class="fw-bold">${title}</div>
                            <small class="text-muted">${subtitle}</small>
                        </div>
                    </button>
                `;
            }).join('');
        }

        async function addEntity(id) {
            let url = '', body = {};
            
            if (currentSearchType === 'hardware') {
                url = `/api/v1/sag/${caseIds}/hardware`;
                body = { hardware_id: id };
            } else if (currentSearchType === 'location') {
                url = `/api/v1/sag/${caseIds}/locations`;
                body = { location_id: id };
            } else if (currentSearchType === 'contact') {
                url = `/api/v1/sag/${caseIds}/contacts`;
                body = { contact_id: id };
            } else if (currentSearchType === 'customer') {
                url = `/api/v1/sag/${caseIds}/customers`;
                body = { customer_id: id };
            }
            
            try {
                const res = await fetch(url, {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify(body)
                });
                
                if (!res.ok) {
                    const err = await res.json();
                    alert("Fejl: " + (err.detail || 'Kunne ikke tilføje'));
                    return;
                }
                
                bootstrap.Modal.getInstance(document.getElementById('entitySearchModal')).hide();
                window.location.reload();
            } catch (e) {
                alert("Fejl: " + e.message);
            }
        }

        async function removeContact(caseId, contactId) {
            if(!confirm("Fjern denne kontakt fra sagen?")) return;
            try {
                const res = await fetch(`/api/v1/sag/${caseId}/contacts/${contactId}`, { method: 'DELETE' });
                if (res.ok) window.location.reload();
                else alert("Fejl ved sletning");
            } catch(e) { alert("Fejl: " + e.message); }
        }

        function openContactRoleModal(contactId, contactName, role, isPrimary) {
            document.getElementById('contactRoleContactId').value = contactId;
            document.getElementById('contactRoleName').textContent = contactName || '-';
            document.getElementById('contactRoleInput').value = role || '';
            document.getElementById('contactRolePrimary').checked = !!isPrimary;

            const modal = new bootstrap.Modal(document.getElementById('contactRoleModal'));
            modal.show();
        }

        async function saveContactRole() {
            const contactId = document.getElementById('contactRoleContactId').value;
            const role = document.getElementById('contactRoleInput').value.trim();
            const isPrimary = document.getElementById('contactRolePrimary').checked;

            try {
                const res = await fetch(`/api/v1/sag/${caseIds}/contacts/${contactId}`, {
                    method: 'PATCH',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({ role, is_primary: isPrimary })
                });

                if (!res.ok) {
                    const err = await res.json();
                    throw new Error(err.detail || 'Kunne ikke opdatere kontakt');
                }

                bootstrap.Modal.getInstance(document.getElementById('contactRoleModal')).hide();
                window.location.reload();
            } catch (e) {
                alert('Fejl: ' + e.message);
            }
        }

        async function removeCustomer(caseId, customerId) {
            if(!confirm("Fjern denne kunde fra sagen?")) return;
            try {
                const res = await fetch(`/api/v1/sag/${caseId}/customers/${customerId}`, { method: 'DELETE' });
                if (res.ok) window.location.reload();
                else alert("Fejl ved sletning");
            } catch(e) { alert("Fejl: " + e.message); }
        }

        async function updateDeferredUntil(value) {
            try {
                const res = await fetch(`/api/v1/sag/${caseIds}`, {
                    method: 'PATCH',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({ deferred_until: value || null })
                });
                if (!res.ok) {
                    const err = await res.json();
                    throw new Error(err.detail || 'Kunne ikke opdatere');
                }
                window.location.reload();
            } catch (e) {
                alert('Fejl: ' + e.message);
            }
        }

        async function updateDeadline(value) {
            try {
                const res = await fetch(`/api/v1/sag/${caseIds}`, {
                    method: 'PATCH',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({ deadline: value || null })
                });
                if (!res.ok) {
                    const err = await res.json();
                    throw new Error(err.detail || 'Kunne ikke opdatere deadline');
                }
                window.location.reload();
            } catch (e) {
                alert('Fejl: ' + e.message);
            }
        }

        function shiftDeadlineDays(days) {
            const input = document.getElementById('deadlineInput');
            const base = input.value ? new Date(input.value) : new Date();
            base.setDate(base.getDate() + days);
            input.value = base.toISOString().slice(0, 10);
            updateDeadline(input.value);
        }

        function shiftDeadlineMonths(months) {
            const input = document.getElementById('deadlineInput');
            const base = input.value ? new Date(input.value) : new Date();
            base.setMonth(base.getMonth() + months);
            input.value = base.toISOString().slice(0, 10);
            updateDeadline(input.value);
        }

        function openDeadlineModal() {
            const modal = new bootstrap.Modal(document.getElementById('deadlineModal'));
            modal.show();
        }

        function saveDeadlineAll() {
            const input = document.getElementById('deadlineInput');
            updateDeadline(input.value || null);
        }

        function clearDeadlineAll() {
            const input = document.getElementById('deadlineInput');
            input.value = '';
            updateDeadline(null);
        }

        function setDeferredFromInput() {
            const input = document.getElementById('deferredUntilInput');
            updateDeferredUntil(input.value || null);
        }

        function shiftDeferredDays(days) {
            const input = document.getElementById('deferredUntilInput');
            const base = input.value ? new Date(input.value) : new Date();
            base.setDate(base.getDate() + days);
            input.value = base.toISOString().slice(0, 10);
            updateDeferredUntil(input.value);
        }

        function shiftDeferredMonths(months) {
            const input = document.getElementById('deferredUntilInput');
            const base = input.value ? new Date(input.value) : new Date();
            base.setMonth(base.getMonth() + months);
            input.value = base.toISOString().slice(0, 10);
            updateDeferredUntil(input.value);
        }

        function clearDeferredUntil() {
            const input = document.getElementById('deferredUntilInput');
            input.value = '';
            updateDeferredUntil(null);
        }

        function openDeferredModal() {
            const modal = new bootstrap.Modal(document.getElementById('deferredModal'));
            modal.show();
        }

        async function updateDeferredCaseAndStatus(caseId, status) {
            try {
                const res = await fetch(`/api/v1/sag/${caseIds}`, {
                    method: 'PATCH',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({
                        deferred_until_case_id: caseId ? parseInt(caseId, 10) : null,
                        deferred_until_status: status || null
                    })
                });
                if (!res.ok) {
                    const err = await res.json();
                    throw new Error(err.detail || 'Kunne ikke opdatere');
                }
                window.location.reload();
            } catch (e) {
                alert('Fejl: ' + e.message);
            }
        }

        function setDeferredCaseFromInputs() {
            const caseSelect = document.getElementById('deferredCaseSelect');
            const statusSelect = document.getElementById('deferredStatusSelect');
            updateDeferredCaseAndStatus(caseSelect.value || null, statusSelect.value || null);
        }

        function clearDeferredCase() {
            const caseSelect = document.getElementById('deferredCaseSelect');
            const statusSelect = document.getElementById('deferredStatusSelect');
            caseSelect.value = '';
            statusSelect.value = '';
            updateDeferredCaseAndStatus(null, null);
        }

        function saveDeferredAll() {
            const input = document.getElementById('deferredUntilInput');
            const caseSelect = document.getElementById('deferredCaseSelect');
            const statusSelect = document.getElementById('deferredStatusSelect');
            updateDeferredUntil(input.value || null);
            updateDeferredCaseAndStatus(caseSelect.value || null, statusSelect.value || null);
        }

        function clearDeferredAll() {
            const input = document.getElementById('deferredUntilInput');
            const caseSelect = document.getElementById('deferredCaseSelect');
            const statusSelect = document.getElementById('deferredStatusSelect');
            input.value = '';
            caseSelect.value = '';
            statusSelect.value = '';
            updateDeferredUntil(null);
            updateDeferredCaseAndStatus(null, null);
        }

        function togglePipelineEdit(forceEdit = null) {
            const view = document.getElementById('pipelineViewMode');
            const edit = document.getElementById('pipelineEditMode');
            const shouldEdit = forceEdit === null ? edit.classList.contains('d-none') : forceEdit;

            if (shouldEdit) {
                view.classList.add('d-none');
                edit.classList.remove('d-none');
            } else {
                view.classList.remove('d-none');
                edit.classList.add('d-none');
            }

            if (shouldEdit) {
                ensurePipelineStagesLoaded();
            }
        }

        async function ensurePipelineStagesLoaded() {
            const select = document.getElementById('pipelineStageSelect');
            if (!select) return;

            if (select.options.length > 1) return;

            try {
                const response = await fetch('/api/v1/pipeline/stages', { credentials: 'include' });
                if (!response.ok) return;

                const stages = await response.json();
                if (!Array.isArray(stages) || stages.length === 0) return;

                const existingValue = select.value || '';
                select.innerHTML = '<option value="">Ikke sat</option>' +
                    stages.map((stage) => `<option value="${stage.id}">${stage.name}</option>`).join('');
                if (existingValue) {
                    select.value = existingValue;
                }
            } catch (error) {
                console.error('Could not load pipeline stages', error);
            }
        }

        async function saveCaseType(newType, newLabel, newIcon, newColor) {
            // Update UI immediately for snappy feel
            const btn = document.getElementById('caseTypeDropdownBtn');
            const lbl = document.getElementById('caseTypeLabel');
            const ico = document.getElementById('caseTypeIcon');
            if (btn) btn.style.setProperty('--tcolor', newColor);
            if (lbl) lbl.textContent = newLabel;
            if (ico) { ico.className = 'bi ' + newIcon; }

            try {
                const resp = await fetch(`/api/v1/sag/${caseId}`, {
                    method: 'PATCH',
                    credentials: 'include',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ type: newType })
                });
                if (!resp.ok) throw new Error('HTTP ' + resp.status);
                // Reload to re-render template vars (color accent on ID chip etc.)
                location.reload();
            } catch (e) {
                console.error('saveCaseType error', e);
                showToast('Kunne ikke gemme sagstype', 'danger');
            }
        }

        async function saveCaseStatusFromTopbar() {
            const select = document.getElementById('topbarStatusSelect');
            if (!select) return;

            try {
                const response = await fetch(`/api/v1/sag/${caseId}`, {
                    method: 'PATCH',
                    credentials: 'include',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ status: select.value || 'åben' })
                });

                if (!response.ok) throw new Error('HTTP ' + response.status);
                location.reload();
            } catch (e) {
                console.error('saveCaseStatusFromTopbar error', e);
                showToast('Kunne ikke gemme status', 'danger');
            }
        }

        async function hydrateTopbarStatusOptions() {
            const select = document.getElementById('topbarStatusSelect');
            if (!select) return;

            const initialValue = String(select.value || '').trim();
            const known = new Map();

            const addStatus = (raw) => {
                const value = String(raw || '').trim();
                if (!value) return;
                const key = value.toLowerCase();
                if (!known.has(key)) {
                    known.set(key, value);
                }
            };

            Array.from(select.options || []).forEach((opt) => addStatus(opt.value));

            try {
                const response = await fetch('/api/v1/sag?include_deferred=true', { credentials: 'include' });
                if (response.ok) {
                    const cases = await response.json();
                    (Array.isArray(cases) ? cases : []).forEach((c) => addStatus(c?.status));
                }
            } catch (error) {
                console.warn('Could not hydrate status options from cases API', error);
            }

            ['åben', 'under behandling', 'afventer', 'løst', 'lukket'].forEach(addStatus);
            addStatus(initialValue);

            const sortedValues = Array.from(known.values()).sort((a, b) =>
                a.localeCompare(b, 'da', { sensitivity: 'base' })
            );

            select.innerHTML = sortedValues.map((value) => {
                const selected = initialValue && value.toLowerCase() === initialValue.toLowerCase();
                return `<option value="${value}" ${selected ? 'selected' : ''}>${value.charAt(0).toUpperCase()}${value.slice(1)}</option>`;
            }).join('');

            if (initialValue) {
                select.value = Array.from(known.values()).find((v) => v.toLowerCase() === initialValue.toLowerCase()) || initialValue;
            }
        }

        function saveCaseTypeFromTopbar() {
            const select = document.getElementById('topbarTypeSelect');
            if (!select) return;

            const typeMeta = {
                ticket: { label: 'Ticket', icon: 'bi-ticket-perforated', color: '#6366f1' },
                pipeline: { label: 'Pipeline', icon: 'bi-graph-up-arrow', color: '#0ea5e9' },
                opgave: { label: 'Opgave', icon: 'bi-puzzle', color: '#f59e0b' },
                ordre: { label: 'Ordre', icon: 'bi-receipt', color: '#10b981' },
                projekt: { label: 'Projekt', icon: 'bi-folder2-open', color: '#8b5cf6' },
                service: { label: 'Service', icon: 'bi-tools', color: '#ef4444' }
            };

            const nextType = (select.value || 'ticket').toLowerCase();
            const meta = typeMeta[nextType] || typeMeta.ticket;
            saveCaseType(nextType, meta.label, meta.icon, meta.color);
        }

        async function saveCasePriorityFromTopbar() {
            const select = document.getElementById('topbarPrioritySelect');
            if (!select) return;

            try {
                const resp = await fetch(`/api/v1/sag/${caseId}`, {
                    method: 'PATCH',
                    credentials: 'include',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ priority: (select.value || 'normal').toLowerCase() })
                });

                if (!resp.ok) throw new Error('HTTP ' + resp.status);
                location.reload();
            } catch (e) {
                console.error('saveCasePriorityFromTopbar error', e);
                showToast('Kunne ikke gemme prioritet', 'danger');
            }
        }

        async function saveCaseStartDateFromTopbar() {
            const input = document.getElementById('topbarStartDateInput');
            if (!input) return;

            try {
                const resp = await fetch(`/api/v1/sag/${caseId}`, {
                    method: 'PATCH',
                    credentials: 'include',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ start_date: input.value || null })
                });

                if (!resp.ok) throw new Error('HTTP ' + resp.status);
                location.reload();
            } catch (e) {
                console.error('saveCaseStartDateFromTopbar error', e);
                showToast('Kunne ikke gemme startdato', 'danger');
            }
        }

        function clearCaseStartDateFromTopbar() {
            const input = document.getElementById('topbarStartDateInput');
            if (!input) return;
            input.value = '';
            saveCaseStartDateFromTopbar();
        }

        async function saveAssignmentFromTabsBar() {
            const topUser = document.getElementById('tabsAssignmentUserSelect');
            const topGroup = document.getElementById('tabsAssignmentGroupSelect');
            const legacyUser = document.getElementById('assignmentUserSelect');
            const legacyGroup = document.getElementById('assignmentGroupSelect');

            if (legacyUser && topUser) {
                legacyUser.value = topUser.value;
            }
            if (legacyGroup && topGroup) {
                legacyGroup.value = topGroup.value;
            }

            await saveAssignment();
        }

        async function saveAssignment() {
            const statusEl = document.getElementById('assignmentStatus');
            const userValue = document.getElementById('assignmentUserSelect')?.value || '';
            const groupValue = document.getElementById('assignmentGroupSelect')?.value || '';

            const payload = {
                ansvarlig_bruger_id: userValue ? parseInt(userValue, 10) : null,
                assigned_group_id: groupValue ? parseInt(groupValue, 10) : null
            };

            if (statusEl) {
                statusEl.textContent = 'Gemmer...';
            }

            try {
                const response = await fetch(`/api/v1/sag/${caseId}`, {
                    method: 'PATCH',
                    credentials: 'include',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(payload)
                });

                if (!response.ok) {
                    let message = 'Kunne ikke gemme tildeling';
                    try {
                        const data = await response.json();
                        message = data.detail || message;
                    } catch (err) {
                        // Keep default message
                    }
                    if (statusEl) {
                        statusEl.textContent = `❌ ${message}`;
                    }
                    return;
                }

                if (statusEl) {
                    statusEl.textContent = '✅ Tildeling gemt';
                }

                const topUser = document.getElementById('tabsAssignmentUserSelect');
                const topGroup = document.getElementById('tabsAssignmentGroupSelect');
                if (topUser) {
                    topUser.value = userValue;
                }
                if (topGroup) {
                    topGroup.value = groupValue;
                }
            } catch (err) {
                if (statusEl) {
                    statusEl.textContent = `❌ ${err.message}`;
                }
            }
        }

        async function savePipeline() {
            const stageValue = document.getElementById('pipelineStageSelect').value;
            const probabilityValue = document.getElementById('pipelineProbabilityInput').value;
            const amountValue = document.getElementById('pipelineAmountInput').value;
            const descriptionValue = document.getElementById('pipelineDescriptionInput').value;

            const payload = {
                stage_id: stageValue ? parseInt(stageValue, 10) : null,
                probability: probabilityValue === '' ? null : parseInt(probabilityValue, 10),
                amount: amountValue === '' ? null : parseFloat(amountValue),
                description: descriptionValue === '' ? null : descriptionValue
            };

            try {
                const response = await fetch(`/api/v1/sag/${caseId}/pipeline`, {
                    method: 'PATCH',
                    credentials: 'include',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(payload)
                });

                if (!response.ok) {
                    let message = 'Kunne ikke opdatere pipeline';
                    try {
                        const err = await response.json();
                        message = err.detail || err.message || message;
                    } catch (_e) {
                        const text = await response.text();
                        if (text) message = text;
                    }
                    throw new Error(`${message} (HTTP ${response.status})`);
                }

                window.location.reload();
            } catch (error) {
                alert(`Fejl: ${error.message}`);
            }
        }

        // ==========================================
        // VIEW CONTROL (Tag-based)
        // ==========================================

        let modulePrefs = {};
        let currentCaseView = 'Sag-detalje';

        function moduleHasContent(el) {
            const attr = el.getAttribute('data-has-content');
            if (attr === 'true') return true;
            if (attr === 'false') return false;
            if (attr === 'unknown') return false;

            if (el.querySelector('.person-card')) return true;
            if (el.querySelector('.list-group-item')) return true;
            return true;
        }

        function setModuleContentState(moduleKey, hasContent) {
            const el = document.querySelector(`[data-module="${moduleKey}"]`);
            if (!el) return;
            el.setAttribute('data-has-content', hasContent ? 'true' : 'false');
            applyViewLayout(currentCaseView);
        }

        function applyViewLayout(viewName) {
            if (!viewName) return;
            currentCaseView = viewName;
            document.body.setAttribute('data-case-view', viewName);

            const viewDefaults = {
                'Pipeline': ['pipeline', 'relations', 'sales', 'time'],
                'Kundevisning': ['customers', 'contacts', 'locations', 'wiki', 'tags'],
                'Sag-detalje': ['pipeline', 'hardware', 'locations', 'contacts', 'customers', 'wiki', 'tags', 'todo-steps', 'relations', 'call-history', 'files', 'emails', 'solution', 'time', 'sales', 'subscription', 'reminders', 'calendar']
            };

            const defaultsByCaseType = caseTypeModuleDefaults[caseTypeKey];
            const standardModules = Array.isArray(defaultsByCaseType) && defaultsByCaseType.length > 0
                ? defaultsByCaseType
                : (viewDefaults[viewName] || []);
            const standardModuleSet = new Set(standardModules);
            standardModuleSet.add('tags');
            standardModuleSet.add('time');

            document.querySelectorAll('[data-module]').forEach((el) => {
                const moduleName = el.getAttribute('data-module');
                const hasContent = moduleHasContent(el);
                const isTimeModule = moduleName === 'time';
                const shouldCompactWhenEmpty = moduleName !== 'wiki' && moduleName !== 'pipeline' && !isTimeModule;
                const pref = modulePrefs[moduleName];
                const tabButton = document.querySelector(`[data-module-tab="${moduleName}"]`);

                // Helper til at skjule eller vise modulet og dets mb-3 indpakning 
                const setVisibility = (visible) => {
                    let wrapper = null;
                    if (el.parentElement) {
                        const isMB3 = el.parentElement.classList.contains('mb-3');
                        const isRowCol12 = el.parentElement.classList.contains('col-12') && el.parentElement.parentElement && el.parentElement.parentElement.classList.contains('row');
                        if (isMB3) wrapper = el.parentElement;
                        else if (isRowCol12) wrapper = el.parentElement.parentElement;
                    }

                    if (visible) {
                        el.classList.remove('d-none');
                        if (wrapper && wrapper.classList.contains('d-none')) {
                            wrapper.classList.remove('d-none');
                        }
                        if (tabButton && tabButton.classList.contains('d-none')) {
                            tabButton.classList.remove('d-none');
                        }
                    } else {
                        el.classList.add('d-none');
                        if (wrapper && !wrapper.classList.contains('d-none')) wrapper.classList.add('d-none');
                        if (tabButton && !tabButton.classList.contains('d-none')) tabButton.classList.add('d-none');
                    }
                };

                // Altid vis time (tid)
                if (isTimeModule) {
                    setVisibility(true);
                    el.classList.remove('module-empty-compact');
                    return;
                }

                // HVIS specifik præference deaktiverer den - Skjul den! Uanset content.
                if (pref === false) {
                    setVisibility(false);
                    el.classList.remove('module-empty-compact');
                    return;
                }

                // HVIS specifik præference aktiverer den (brugervalg)
                if (pref === true) {
                    setVisibility(true);
                    el.classList.toggle('module-empty-compact', shouldCompactWhenEmpty && !hasContent);
                    return;
                }

                // Default logic (ingen brugervalg) - har den content, så vis den
                if (hasContent) {
                    setVisibility(true);
                    el.classList.remove('module-empty-compact');
                    return;
                }
                
                // Default logic - ingen content: se på layout defaults
                if (standardModuleSet.has(moduleName)) {
                    setVisibility(true);
                    el.classList.toggle('module-empty-compact', shouldCompactWhenEmpty);
                } else {
                    setVisibility(false);
                    el.classList.remove('module-empty-compact');
                }
            });

            updateRightColumnVisibility();
            updateInnerColumnVisibility();
        }

        function updateRightColumnVisibility() {
            const rightColumn = document.getElementById('case-right-column');
            const leftColumn = document.getElementById('case-left-column');
            if (!rightColumn || !leftColumn) return;

            const visibleRightModules = rightColumn.querySelectorAll('.right-module-card:not(.d-none)');
            if (visibleRightModules.length === 0) {
                rightColumn.classList.add('d-none');
                rightColumn.classList.remove('col-xl-4');
                rightColumn.classList.remove('col-lg-4');
                leftColumn.classList.remove('col-xl-8');
                leftColumn.classList.remove('col-lg-8');
                leftColumn.classList.add('col-12');
            } else {
                rightColumn.classList.remove('d-none');
                rightColumn.classList.add('col-xl-4');
                rightColumn.classList.add('col-lg-4');
                leftColumn.classList.add('col-xl-8');
                leftColumn.classList.add('col-lg-8');
                leftColumn.classList.remove('col-12');
            }
        }

        function updateInnerColumnVisibility() {
            const leftCol = document.getElementById('inner-left-col');
            const centerCol = document.getElementById('inner-center-col');
            if (!leftCol || !centerCol) return;

            // Tæl synlige moduler i venstre kolonnen (mb-3 wrappers der ikke er skjulte)
            const visibleLeftModules = leftCol.querySelectorAll('.mb-3:not(.d-none) [data-module]');
            const hasVisibleLeft = visibleLeftModules.length > 0;

            if (!hasVisibleLeft) {
                // Ingen synlige moduler i venstre - center forbliver fuld bredde
                leftCol.classList.add('d-none');
                centerCol.classList.add('col-12');
            } else {
                // Begge interne sektioner vises stadig i én kolonne hver
                leftCol.classList.remove('d-none');
                centerCol.classList.add('col-12');
            }
        }

        async function applyViewFromTags() {
            try {
                const res = await fetch(`/api/v1/tags/entity/case/${caseIds}`);
                if (!res.ok) return;
                const tags = await res.json();
                const viewTag = tags.find(t => ['Pipeline', 'Kundevisning', 'Sag-detalje'].includes(t.name));
                applyViewLayout(viewTag ? viewTag.name : 'Sag-detalje');
            } catch (e) {
                console.error('View tag lookup failed', e);
            }
        }

        async function loadModulePrefs() {
            try {
                const res = await fetch(`/api/v1/sag/${caseIds}/modules`);
                if (!res.ok) return;
                const prefs = await res.json();
                modulePrefs = (prefs || []).reduce((acc, p) => {
                    acc[p.module_key] = p.is_enabled;
                    return acc;
                }, {});
                modulePrefs.time = true;
            } catch (e) {
                console.error('Module prefs load failed', e);
            }
        }

        async function loadCaseTypeModuleDefaultsSetting() {
            try {
                const res = await fetch('/api/v1/settings/case_type_module_defaults');
                if (!res.ok) return;
                const setting = await res.json();
                const parsed = JSON.parse(setting.value || '{}');
                if (parsed && typeof parsed === 'object') {
                    caseTypeModuleDefaults = Object.entries(parsed).reduce((acc, [key, value]) => {
                        acc[String(key || '').toLowerCase()] = Array.isArray(value) ? value : [];
                        return acc;
                    }, {});
                } else {
                    caseTypeModuleDefaults = {};
                }
            } catch (e) {
                console.error('Case type module defaults load failed', e);
                caseTypeModuleDefaults = {};
            }
        }

        async function openModuleControlModal() {
            const list = document.getElementById('moduleControlList');
            list.innerHTML = '<div class="text-muted small">Indlæser...</div>';

            const modules = Array.from(document.querySelectorAll('[data-module]')).map(el => {
                const key = el.getAttribute('data-module');
                return { key, label: window.moduleDisplayNames[key] || key };
            });

            list.innerHTML = modules.map(m => {
                const isTimeModule = m.key === 'time';
                const checked = isTimeModule ? true : modulePrefs[m.key] !== false;
                return `
                    <div class="form-check mb-2">
                        <input class="form-check-input" type="checkbox" id="module_${m.key}" ${checked ? 'checked' : ''}
                            ${isTimeModule ? 'disabled' : ''}
                            onchange="toggleModulePref('${m.key}', this.checked)">
                        <label class="form-check-label" for="module_${m.key}">${m.label}${isTimeModule ? ' (altid synlig)' : ''}</label>
                    </div>
                `;
            }).join('');

            const modal = new bootstrap.Modal(document.getElementById('moduleControlModal'));
            modal.show();
        }

        async function toggleModulePref(moduleKey, isEnabled) {
            if (moduleKey === 'time') {
                modulePrefs.time = true;
                applyViewFromTags();
                return;
            }
            try {
                const res = await fetch(`/api/v1/sag/${caseIds}/modules`, {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({ module_key: moduleKey, is_enabled: isEnabled })
                });
                if (!res.ok) {
                    const err = await res.json();
                    throw new Error(err.detail || 'Kunne ikke opdatere modul');
                }
                modulePrefs[moduleKey] = isEnabled;
                applyViewFromTags();
            } catch (e) {
                alert('Fejl: ' + e.message);
            }
        }

        // ==========================================
        // FILES & EMAILS LOGIC
        // ==========================================

        let sagFilesCache = [];
        
        // ---------------- FILES ----------------

        function updateCaseEmailAttachmentOptions(files) {
            const select = document.getElementById('caseEmailAttachmentIds');
            if (!select) return;

            const safeFiles = Array.isArray(files) ? files : [];
            if (!safeFiles.length) {
                select.innerHTML = '<option disabled>Ingen sagsfiler tilgængelige</option>';
                return;
            }

            select.innerHTML = safeFiles.map((file) => {
                const fileId = Number(file.id);
                const filename = escapeHtml(file.filename || `Fil ${fileId}`);
                const date = file.created_at ? new Date(file.created_at).toLocaleDateString('da-DK') : '-';
                return `<option value="${fileId}">${filename} (${date})</option>`;
            }).join('');
        }
        
        async function loadSagFiles() {
            const container = document.getElementById('files-list');
            if (container) {
                container.innerHTML = '<div class="p-3 text-center text-muted"><span class="spinner-border spinner-border-sm"></span> Henter filer...</div>';
            }
            
            try {
                const res = await fetch(`/api/v1/sag/${caseIds}/files`);
                if(res.ok) {
                    const files = await res.json();
                    sagFilesCache = Array.isArray(files) ? files : [];
                    updateCaseEmailAttachmentOptions(sagFilesCache);
                    renderFiles(files);
                } else {
                    sagFilesCache = [];
                    updateCaseEmailAttachmentOptions(sagFilesCache);
                    if (container) {
                        container.innerHTML = '<div class="p-3 text-center text-danger">Fejl ved hentning af filer</div>';
                    }
                    setModuleContentState('files', true);
                }
            } catch(e) {
                console.error(e);
                sagFilesCache = [];
                updateCaseEmailAttachmentOptions(sagFilesCache);
                if (container) {
                    container.innerHTML = '<div class="p-3 text-center text-danger">Fejl ved hentning af filer</div>';
                }
                setModuleContentState('files', true);
            }
        }

        function renderFiles(files) {
            const container = document.getElementById('files-list');
            sagFilesCache = Array.isArray(files) ? files : [];
            updateCaseEmailAttachmentOptions(sagFilesCache);

            if (!container) {
                return;
            }

            if(!files || files.length === 0) {
                container.innerHTML = '<div class="p-3 text-center text-muted">Ingen filer fundet...</div>';
                setModuleContentState('files', false);
                return;
            }
            setModuleContentState('files', true);
            container.innerHTML = files.map(f => {
                const size = (f.size_bytes / 1024 / 1024).toFixed(2) + ' MB';
                return `
                    <div class="list-group-item d-flex justify-content-between align-items-center">
                        <div class="ms-2 me-auto">
                            <div class="fw-bold text-truncate" style="max-width: 250px;">
                                <a href="javascript:void(0);" onclick="previewFile(${f.id}, '${f.filename.replace(/'/g, "\\'")}', '${f.content_type || ''}')" class="text-decoration-none text-dark">
                                    <i class="bi bi-file-earmark me-1"></i> ${f.filename}
                                </a>
                            </div>
                            <small class="text-muted">${size} • ${new Date(f.created_at).toLocaleDateString()}</small>
                        </div>
                        <div class="d-flex gap-1">
                            <a href="${f.download_url}?download=true" class="btn btn-sm btn-outline-primary border-0" title="Download">
                                <i class="bi bi-download"></i>
                            </a>
                            <button class="btn btn-sm btn-outline-danger border-0" onclick="deleteFile(${f.id})" title="Slet">
                                <i class="bi bi-x-lg"></i>
                            </button>
                        </div>
                    </div>
                `;
            }).join('');
        }

        async function handleFileUpload(fileList) {
            if(!fileList || fileList.length === 0) return;
            const formData = new FormData();
            for (let i = 0; i < fileList.length; i++) {
                formData.append("files", fileList[i]);
            }
            
            // Show loading
            document.getElementById('files-list').innerHTML += '<div class="p-2 text-center text-muted fst-italic">Uploader...</div>';
            
            try {
                const res = await fetch(`/api/v1/sag/${caseIds}/files`, {
                    method: 'POST',
                    body: formData
                });
                if(res.ok) {
                    loadSagFiles();
                } else {
                    alert('Upload fejlede');
                    loadSagFiles(); // Reload to clear loading state
                }
            } catch(e) {
                alert('Upload fejl: ' + e);
                loadSagFiles();
            }
        }

        async function deleteFile(fileId) {
            if(!confirm("Slet denne fil?")) return;
            try {
                const res = await fetch(`/api/v1/sag/${caseIds}/files/${fileId}`, { method: 'DELETE' });
                if(res.ok) loadSagFiles();
                else alert("Kunne ikke slette fil");
            } catch(e) { alert("Fejl: " + e); }
        }

        // File Preview
        function previewFile(fileId, filename, contentType) {
            const modal = new bootstrap.Modal(document.getElementById('filePreviewModal'));
            const previewContent = document.getElementById('previewContent');
            const fileNameEl = document.getElementById('previewFileName');
            const downloadBtn = document.getElementById('previewDownloadBtn');
            
            // Set filename and download link
            fileNameEl.textContent = filename;
            const fileUrl = `/api/v1/sag/${caseIds}/files/${fileId}`;
            downloadBtn.href = `${fileUrl}?download=true`;
            downloadBtn.download = filename;
            
            // Show loading spinner
            previewContent.innerHTML = `
                <div class="spinner-border text-primary" role="status">
                    <span class="visually-hidden">Indlæser...</span>
                </div>
            `;
            
            modal.show();
            
            // Determine file type and render preview
            const ext = filename.split('.').pop().toLowerCase();
            
            if (['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp', 'bmp'].includes(ext)) {
                // Image preview
                previewContent.innerHTML = `<img src="${fileUrl}" class="img-fluid" style="max-height: 80vh;" alt="${filename}">`;
            } else if (ext === 'pdf') {
                // PDF preview using iframe
                previewContent.innerHTML = `<iframe src="${fileUrl}" class="w-100 h-100 border-0" style="min-height: 60vh;"></iframe>`;
            } else if (['txt', 'log', 'md', 'json', 'xml', 'csv', 'html', 'css', 'js', 'py', 'sql'].includes(ext)) {
                // Text file preview
                fetch(fileUrl)
                    .then(res => res.text())
                    .then(text => {
                        previewContent.innerHTML = `<pre class="p-3 m-0 overflow-auto h-100" style="max-height: 80vh;"><code>${escapeHtml(text)}</code></pre>`;
                    })
                    .catch(err => {
                        previewContent.innerHTML = `<div class="alert alert-danger m-4">Kunne ikke indlæse fil: ${err}</div>`;
                    });
            } else if (['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'].includes(ext)) {
                // Office documents - use Google Docs Viewer
                const encodedUrl = encodeURIComponent(window.location.origin + fileUrl);
                previewContent.innerHTML = `<iframe src="https://docs.google.com/viewer?url=${encodedUrl}&embedded=true" class="w-100 h-100 border-0" style="min-height: 60vh;"></iframe>`;
            } else if (['mp4', 'webm', 'ogg'].includes(ext)) {
                // Video preview
                previewContent.innerHTML = `
                    <video controls class="w-100" style="max-height: 80vh;">
                        <source src="${fileUrl}" type="video/${ext}">
                        Din browser understøtter ikke video afspilning.
                    </video>
                `;
            } else if (['mp3', 'wav', 'ogg', 'm4a'].includes(ext)) {
                // Audio preview
                previewContent.innerHTML = `
                    <div class="p-5 text-center">
                        <i class="bi bi-music-note-beamed display-1 text-muted mb-4"></i>
                        <h5>${filename}</h5>
                        <audio controls class="w-100 mt-3">
                            <source src="${fileUrl}" type="audio/${ext}">
                            Din browser understøtter ikke audio afspilning.
                        </audio>
                    </div>
                `;
            } else {
                // Unsupported file type
                previewContent.innerHTML = `
                    <div class="p-5 text-center">
                        <i class="bi bi-file-earmark-x display-1 text-muted mb-4"></i>
                        <h5>Kan ikke vise forhåndsvisning for denne filtype</h5>
                        <p class="text-muted">${filename}</p>
                        <a href="${fileUrl}?download=true" class="btn btn-primary mt-3" download="${filename}">
                            <i class="bi bi-download me-2"></i> Download fil
                        </a>
                    </div>
                `;
            }
        }

        function escapeHtml(text) {
            const div = document.createElement('div');
            div.textContent = text;
            return div.innerHTML;
        }

        // File Drag & Drop
        const fileDropZone = document.getElementById('fileDropZone');
        if(fileDropZone) {
            fileDropZone.addEventListener('dragover', e => { e.preventDefault(); fileDropZone.classList.add('bg-light-subtle'); });
            fileDropZone.addEventListener('dragleave', e => { e.preventDefault(); fileDropZone.classList.remove('bg-light-subtle'); });
            fileDropZone.addEventListener('drop', e => {
                e.preventDefault();
                fileDropZone.classList.remove('bg-light-subtle');
                if(e.dataTransfer.files.length) handleFileUpload(e.dataTransfer.files);
            });
        }
        
        // ---------------- EMAILS ----------------

        let linkedEmailsCache = [];
        let filteredLinkedEmailsCache = [];
        let selectedLinkedEmailId = null;
        let selectedLinkedEmailDetail = null;
        let selectedEmailThreadKey = null;

        function parseEmailField(value) {
            return String(value || '')
                .split(/[\n,;]+/)
                .map((email) => email.trim())
                .filter(Boolean);
        }

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

        let rewriteReviewState = null;

        function extractRewriteBody(rawText, context) {
            const text = String(rawText || '').trim();
            if (!text) return '';

            if (context === 'email') {
                const bodyMatch = text.match(/(?:^|\n)Besked:\s*\n([\s\S]*)$/i);
                if (bodyMatch?.[1]) return bodyMatch[1].trim();
                return text;
            }

            if (context === 'case') {
                const descMatch = text.match(/(?:^|\n)Beskrivelse:\s*\n([\s\S]*)$/i);
                if (descMatch?.[1]) return descMatch[1].trim();
                return text;
            }

            return text;
        }

        function buildLineDiff(originalText, rewrittenText) {
            const originalLines = String(originalText || '').split('\n');
            const rewrittenLines = String(rewrittenText || '').split('\n');
            const maxLen = Math.max(originalLines.length, rewrittenLines.length);
            const changes = [];

            for (let idx = 0; idx < maxLen; idx += 1) {
                const before = originalLines[idx] ?? '';
                const after = rewrittenLines[idx] ?? '';
                if (before !== after) {
                    changes.push({ index: idx, before, after });
                }
            }

            return { changes, originalLines, rewrittenLines };
        }

        function updateRewriteSelectionInfo() {
            const infoEl = document.getElementById('rewritePreviewSelectionInfo');
            const selectedCount = document.querySelectorAll('.rewrite-change-check:checked').length;
            const totalCount = rewriteReviewState?.changes?.length || 0;
            if (!infoEl) return;
            infoEl.textContent = `${selectedCount} af ${totalCount} ændringer valgt`;
        }

        function renderRewritePreview(changes) {
            const listEl = document.getElementById('rewritePreviewList');
            const noChangesEl = document.getElementById('rewritePreviewNoChanges');
            if (!listEl || !noChangesEl) return;

            if (!changes.length) {
                listEl.innerHTML = '';
                noChangesEl.classList.remove('d-none');
                return;
            }

            noChangesEl.classList.add('d-none');
            listEl.innerHTML = changes.map((change, i) => `
                <div class="card border-0 shadow-sm">
                    <div class="card-body py-2 px-3">
                        <div class="d-flex justify-content-between align-items-center mb-2">
                            <div class="form-check">
                                <input class="form-check-input rewrite-change-check" type="checkbox" value="${change.index}" id="rewriteChange_${change.index}" checked>
                                <label class="form-check-label small fw-semibold" for="rewriteChange_${change.index}">
                                    Ændring ${i + 1} (linje ${change.index + 1})
                                </label>
                            </div>
                        </div>
                        <div class="row g-2">
                            <div class="col-md-6">
                                <div class="small text-muted mb-1">Før</div>
                                <div class="border rounded p-2 bg-light" style="white-space: pre-wrap; min-height: 44px;">${escapeHtml(change.before) || '<span class="text-muted fst-italic">(tom)</span>'}</div>
                            </div>
                            <div class="col-md-6">
                                <div class="small text-muted mb-1">Efter</div>
                                <div class="border rounded p-2" style="white-space: pre-wrap; min-height: 44px; background: rgba(15,76,117,0.08);">${escapeHtml(change.after) || '<span class="text-muted fst-italic">(tom)</span>'}</div>
                            </div>
                        </div>
                    </div>
                </div>
            `).join('');

            listEl.querySelectorAll('.rewrite-change-check').forEach((input) => {
                input.addEventListener('change', updateRewriteSelectionInfo);
            });
            updateRewriteSelectionInfo();
        }

        function applyRewriteChanges(mode) {
            if (!rewriteReviewState) return;

            const { originalLines, rewrittenLines, applyToTarget } = rewriteReviewState;
            if (mode === 'all') {
                applyToTarget(rewrittenLines.join('\n'));
                return;
            }

            const selectedIndexes = new Set(
                Array.from(document.querySelectorAll('.rewrite-change-check:checked'))
                    .map((el) => Number(el.value))
                    .filter((val) => Number.isInteger(val) && val >= 0)
            );

            const merged = [...originalLines];
            for (let idx = 0; idx < rewrittenLines.length; idx += 1) {
                if (selectedIndexes.has(idx)) {
                    merged[idx] = rewrittenLines[idx] ?? '';
                }
            }
            applyToTarget(merged.join('\n'));
        }

        function openRewriteReviewModal({ title, originalText, rewrittenText, applyToTarget }) {
            const summaryEl = document.getElementById('rewritePreviewSummary');
            const applyAllBtn = document.getElementById('rewriteApplyAllBtn');
            const applySelectedBtn = document.getElementById('rewriteApplySelectedBtn');
            const modalEl = document.getElementById('rewritePreviewModal');
            if (!summaryEl || !applyAllBtn || !applySelectedBtn || !modalEl) return;

            const diff = buildLineDiff(originalText, rewrittenText);
            rewriteReviewState = {
                ...diff,
                applyToTarget,
            };

            summaryEl.textContent = `${title}: ${diff.changes.length} foreslaaede ændringer.`;
            renderRewritePreview(diff.changes);

            applyAllBtn.disabled = !diff.changes.length;
            applySelectedBtn.disabled = !diff.changes.length;

            const modal = bootstrap.Modal.getOrCreateInstance(modalEl);
            modal.show();
        }

        async function requestRewriteSuggestion(endpoint, text, context) {
            const response = await fetch(endpoint, {
                method: 'POST',
                credentials: 'include',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ text, context })
            });
            if (!response.ok) {
                let detail = `HTTP ${response.status}`;
                try {
                    const err = await response.json();
                    if (err?.detail) detail = err.detail;
                } catch (_) {}
                throw new Error(detail);
            }
            return response.json();
        }

        window.rewriteCaseEmailWithApproval = async function () {
            const bodyInput = document.getElementById('caseEmailBody');
            const btn = document.getElementById('caseEmailRewriteBtn');
            if (!bodyInput) return;

            const source = (bodyInput.value || '').trim();
            if (!source) {
                alert('Skriv en besked først.');
                return;
            }

            const originalHtml = btn?.innerHTML || '';
            if (btn) {
                btn.disabled = true;
                btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Renskriver...';
            }

            try {
                const payload = await requestRewriteSuggestion('/api/v1/emails/rewrite-text', source, 'email');
                const rewritten = extractRewriteBody(payload?.rewritten_text || '', 'email');
                openRewriteReviewModal({
                    title: 'Email-tekst',
                    originalText: source,
                    rewrittenText: rewritten,
                    applyToTarget: (nextText) => {
                        bodyInput.value = nextText;
                        bootstrap.Modal.getOrCreateInstance(document.getElementById('rewritePreviewModal')).hide();
                    }
                });
            } catch (error) {
                console.error(error);
                alert(`Kunne ikke renskrive email: ${error.message || 'Ukendt fejl'}`);
            } finally {
                if (btn) {
                    btn.disabled = false;
                    btn.innerHTML = originalHtml;
                }
            }
        };

        function getDefaultCaseRecipient() {
            const primaryContact = document.querySelector('.contact-row[data-is-primary="true"][data-email]');
            if (primaryContact?.dataset?.email) {
                return primaryContact.dataset.email.trim();
            }

            const anyContact = document.querySelector('.contact-row[data-email]');
            if (anyContact?.dataset?.email) {
                return anyContact.dataset.email.trim();
            }

            const customerSmall = document.querySelector('.customer-row small');
            if (customerSmall) {
                const text = customerSmall.textContent || '';
                const match = text.match(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/i);
                if (match) {
                    return match[0].trim();
                }
            }

            return '';
        }

        function prefillCaseEmailCompose() {
            const toInput = document.getElementById('caseEmailTo');
            const subjectInput = document.getElementById('caseEmailSubject');

            if (toInput && !toInput.value.trim()) {
                const recipient = getDefaultCaseRecipient();
                if (recipient) {
                    toInput.value = recipient;
                }
            }

            if (subjectInput && !subjectInput.value.trim()) {
                subjectInput.value = escapeHtmlForInput(`Sag #${caseIds}: `);
            }
        }

        function openReplyToLinkedEmail() {
            const composeModalEl = document.getElementById('caseEmailComposeModal');
            if (!composeModalEl || !selectedLinkedEmailId || !selectedLinkedEmailDetail) {
                return;
            }

            const toInput = document.getElementById('caseEmailTo');
            const subjectInput = document.getElementById('caseEmailSubject');
            const bodyInput = document.getElementById('caseEmailBody');

            const senderEmail = (selectedLinkedEmailDetail.sender_email || '').trim();
            const originalSubject = (selectedLinkedEmailDetail.subject || '').trim();

            if (toInput && !toInput.value.trim() && senderEmail) {
                toInput.value = senderEmail;
            }

            if (subjectInput && !subjectInput.value.trim()) {
                const replySubject = /^re:\s*/i.test(originalSubject)
                    ? originalSubject
                    : `Re: ${originalSubject || `Sag #${caseIds}`}`;
                subjectInput.value = escapeHtmlForInput(replySubject);
            }

            if (bodyInput && !bodyInput.value.trim()) {
                const received = selectedLinkedEmailDetail.received_date
                    ? new Date(selectedLinkedEmailDetail.received_date).toLocaleString('da-DK')
                    : '-';
                const senderName = selectedLinkedEmailDetail.sender_name || senderEmail || 'Ukendt';
                bodyInput.value = `\n\n---\nFra: ${senderName}\nDato: ${received}\nEmne: ${originalSubject || '(Ingen emne)'}\n`;
            }

            bootstrap.Modal.getOrCreateInstance(composeModalEl).show();
        }

        async function sendCaseEmail() {
            const toInput = document.getElementById('caseEmailTo');
            const ccInput = document.getElementById('caseEmailCc');
            const bccInput = document.getElementById('caseEmailBcc');
            const subjectInput = document.getElementById('caseEmailSubject');
            const bodyInput = document.getElementById('caseEmailBody');
            const attachmentSelect = document.getElementById('caseEmailAttachmentIds');
            const sendBtn = document.getElementById('caseEmailSendBtn');
            const statusEl = document.getElementById('caseEmailSendStatus');

            if (!toInput || !subjectInput || !bodyInput || !sendBtn || !statusEl) {
                return;
            }

            const to = parseEmailField(toInput.value);
            const cc = parseEmailField(ccInput?.value || '');
            const bcc = parseEmailField(bccInput?.value || '');
            const subject = (subjectInput.value || '').trim();
            const bodyText = (bodyInput.value || '').trim();
            const attachmentFileIds = Array.from(attachmentSelect?.selectedOptions || [])
                .map((opt) => Number(opt.value))
                .filter((id) => Number.isInteger(id) && id > 0);

            if (!to.length) {
                alert('Udfyld mindst én modtager i Til-feltet.');
                return;
            }

            if (!subject) {
                alert('Udfyld emne før afsendelse.');
                return;
            }

            if (!bodyText) {
                alert('Udfyld besked før afsendelse.');
                return;
            }

            sendBtn.disabled = true;
            statusEl.className = 'text-muted';
            statusEl.textContent = 'Sender e-mail...';

            try {
                const res = await fetch(`/api/v1/sag/${caseIds}/emails/send`, {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({
                        to,
                        cc,
                        bcc,
                        subject,
                        body_text: bodyText,
                        attachment_file_ids: attachmentFileIds,
                        thread_email_id: selectedLinkedEmailId || null,
                        thread_key: (
                            linkedEmailsCache.find((entry) => Number(entry.id) === Number(selectedLinkedEmailId))?.thread_key
                            || linkedEmailsCache.find((entry) => Number(entry.id) === Number(selectedLinkedEmailId))?.resolved_thread_key
                            || null
                        )
                    })
                });

                if (!res.ok) {
                    let message = `HTTP ${res.status} ${res.statusText || 'Send failed'}`;
                    try {
                        const responseText = await res.text();
                        if (responseText) {
                            try {
                                const err = JSON.parse(responseText);
                                if (err?.detail) {
                                    message = err.detail;
                                } else if (err?.message) {
                                    message = err.message;
                                }
                            } catch (_) {
                                message = responseText.slice(0, 500);
                            }
                        }
                    } catch (_) {
                    }
                    throw new Error(message);
                }

                if (subjectInput) subjectInput.value = '';
                if (bodyInput) bodyInput.value = '';
                if (ccInput) ccInput.value = '';
                if (bccInput) bccInput.value = '';
                if (attachmentSelect) {
                    Array.from(attachmentSelect.options).forEach((option) => {
                        option.selected = false;
                    });
                }

                statusEl.className = 'text-success';
                statusEl.textContent = 'E-mail sendt.';
                loadLinkedEmails();

                const composeModalEl = document.getElementById('caseEmailComposeModal');
                const composeModal = composeModalEl ? bootstrap.Modal.getInstance(composeModalEl) : null;
                if (composeModal) {
                    composeModal.hide();
                }
            } catch (error) {
                statusEl.className = 'text-danger';
                statusEl.textContent = error?.message || 'Email send failed (ukendt fejl)';
            } finally {
                sendBtn.disabled = false;
            }
        }

        function openCaseEmailTab() {
            const trigger = document.getElementById('emails-tab');
            if (!trigger) return;
            const instance = bootstrap.Tab.getOrCreateInstance(trigger);
            instance.show();
        }

        window.quickReplyToEmailFromComment = async function(emailId) {
            const parsedId = Number(emailId);
            if (!Number.isFinite(parsedId)) return;

            openCaseEmailTab();

            try {
                await loadLinkedEmails();
                await loadLinkedEmailDetail(parsedId);
                openReplyToLinkedEmail();
            } catch (error) {
                console.error('Kunne ikke starte quick svar fra kommentar:', error);
            }
        }

        async function loadLinkedEmails() {
            const container = document.getElementById('linked-emails-list');
            const threadContainer = document.getElementById('email-threads-list');
            if (!container || !threadContainer) return;
            
            try {
                const res = await fetch(`/api/v1/sag/${caseIds}/email-links`);
                if(res.ok) {
                    linkedEmailsCache = await res.json();
                    await applyLinkedEmailFilters(true);
                } else {
                    container.innerHTML = '<div class="p-3 text-center text-danger">Fejl ved hentning af emails</div>';
                    threadContainer.innerHTML = '<div class="p-3 text-center text-danger">Fejl ved hentning af tråde</div>';
                    setModuleContentState('emails', true);
                }
            } catch(e) {
                console.error(e);
                container.innerHTML = '<div class="p-3 text-center text-danger">Fejl ved hentning af emails</div>';
                threadContainer.innerHTML = '<div class="p-3 text-center text-danger">Fejl ved hentning af tråde</div>';
                setModuleContentState('emails', true);
            }
        }

        function getFilteredLinkedEmails() {
            const textFilter = (document.getElementById('emailFilterInput')?.value || '').trim().toLowerCase();
            const attachmentFilter = document.getElementById('emailAttachmentFilter')?.value || 'all';
            const readFilter = document.getElementById('emailReadFilter')?.value || 'all';

            return linkedEmailsCache.filter((email) => {
                if (textFilter) {
                    const haystack = [
                        email.subject,
                        email.sender_email,
                        email.sender_name,
                        email.body_text,
                        email.body_html
                    ].join(' ').toLowerCase();
                    if (!haystack.includes(textFilter)) return false;
                }

                const hasAttachments = Boolean(email.has_attachments) || Number(email.attachment_count || 0) > 0;
                if (attachmentFilter === 'with' && !hasAttachments) return false;
                if (attachmentFilter === 'without' && hasAttachments) return false;

                const isRead = Boolean(email.is_read);
                if (readFilter === 'read' && !isRead) return false;
                if (readFilter === 'unread' && isRead) return false;

                return true;
            });
        }

        function getThreadKey(email) {
            return (email?.resolved_thread_key || email?.thread_key || `email-${email?.id || 'unknown'}`).toString();
        }

        function isOutgoingEmail(email) {
            if (typeof email?.is_outgoing === 'boolean') {
                return email.is_outgoing;
            }
            const folder = (email?.folder || '').toString().toLowerCase();
            const status = (email?.status || '').toString().toLowerCase();
            return folder.startsWith('sent') || status === 'sent';
        }

        function buildThreadGroups(emails) {
            const map = new Map();

            emails.forEach((email) => {
                const threadKey = getThreadKey(email);
                const existing = map.get(threadKey);
                const receivedDateMs = email.received_date ? new Date(email.received_date).getTime() : 0;

                if (!existing) {
                    map.set(threadKey, {
                        threadKey,
                        lastDateMs: receivedDateMs,
                        latestEmail: email,
                        emails: [email]
                    });
                    return;
                }

                existing.emails.push(email);
                if (receivedDateMs > existing.lastDateMs) {
                    existing.lastDateMs = receivedDateMs;
                    existing.latestEmail = email;
                }
            });

            return Array.from(map.values())
                .map((group) => {
                    group.emails.sort((a, b) => {
                        const aDate = a.received_date ? new Date(a.received_date).getTime() : 0;
                        const bDate = b.received_date ? new Date(b.received_date).getTime() : 0;
                        return bDate - aDate;
                    });
                    return group;
                })
                .sort((a, b) => b.lastDateMs - a.lastDateMs);
        }

        function getCurrentThreadEmails() {
            if (!selectedEmailThreadKey) return [];
            return filteredLinkedEmailsCache
                .filter((email) => getThreadKey(email) === selectedEmailThreadKey)
                .sort((a, b) => {
                    const aDate = a.received_date ? new Date(a.received_date).getTime() : 0;
                    const bDate = b.received_date ? new Date(b.received_date).getTime() : 0;
                    return bDate - aDate;
                });
        }

        function renderEmailThreads(threadGroups) {
            const container = document.getElementById('email-threads-list');
            if (!container) return;

            if (!threadGroups.length) {
                container.innerHTML = '<div class="p-3 text-center text-muted">Ingen tråde fundet...</div>';
                const counter = document.getElementById('linkedEmailThreadsCount');
                if (counter) counter.textContent = '0';
                return;
            }

            const counter = document.getElementById('linkedEmailThreadsCount');
            if (counter) counter.textContent = String(threadGroups.length);

            container.innerHTML = threadGroups.map((group) => {
                const latest = group.latestEmail || {};
                const isSelected = selectedEmailThreadKey === group.threadKey;
                const receivedDate = latest.received_date ? new Date(latest.received_date).toLocaleString('da-DK') : '-';
                const sender = latest.sender_name || latest.sender_email || '-';
                const subject = latest.subject || '(Ingen emne)';
                const unreadCount = group.emails.filter((item) => !item.is_read).length;

                return `
                <button type="button" class="list-group-item list-group-item-action border-0 border-bottom text-start ${isSelected ? 'active' : ''}" onclick='selectEmailThread(${JSON.stringify(group.threadKey)})'>
                    <div class="d-flex justify-content-between align-items-start gap-2">
                        <div class="flex-grow-1 overflow-hidden">
                            <div class="fw-semibold text-truncate">${escapeHtml(subject)}</div>
                            <div class="small ${isSelected ? 'text-white-50' : 'text-muted'} text-truncate">${escapeHtml(sender)}</div>
                            <div class="small ${isSelected ? 'text-white-50' : 'text-muted'} text-truncate">${escapeHtml(receivedDate)}</div>
                        </div>
                        <div class="d-flex flex-column align-items-end gap-1">
                            <span class="badge ${isSelected ? 'bg-light text-dark' : 'bg-secondary'}">${group.emails.length}</span>
                            ${unreadCount > 0 ? `<span class="badge bg-warning text-dark">${unreadCount} ulæst</span>` : ''}
                        </div>
                    </div>
                </button>
                `;
            }).join('');
        }

        function selectEmailThread(threadKey) {
            selectedEmailThreadKey = String(threadKey || '');

            const threadGroups = buildThreadGroups(filteredLinkedEmailsCache);
            renderEmailThreads(threadGroups);

            const threadEmails = getCurrentThreadEmails();
            renderLinkedEmails(threadEmails);

            if (!threadEmails.length) {
                selectedLinkedEmailId = null;
                renderEmailPreviewEmpty();
                return;
            }

            const hasCurrentSelected = threadEmails.some((item) => Number(item.id) === Number(selectedLinkedEmailId));
            if (!hasCurrentSelected) {
                selectedLinkedEmailId = Number(threadEmails[0].id);
            }

            loadLinkedEmailDetail(selectedLinkedEmailId, true);
        }

        async function applyLinkedEmailFilters(loadDetail = false) {
            filteredLinkedEmailsCache = getFilteredLinkedEmails();
            const threadGroups = buildThreadGroups(filteredLinkedEmailsCache);

            renderEmailThreads(threadGroups);

            if (!threadGroups.length) {
                selectedEmailThreadKey = null;
                selectedLinkedEmailId = null;
                renderLinkedEmails([]);
                const threadCounter = document.getElementById('threadEmailsCount');
                if (threadCounter) threadCounter.textContent = '0';
                renderEmailPreviewEmpty();
                setModuleContentState('emails', false);
                return;
            }

            const selectedThreadExists = threadGroups.some((group) => group.threadKey === selectedEmailThreadKey);
            if (!selectedThreadExists) {
                selectedEmailThreadKey = threadGroups[0].threadKey;
            }

            const threadEmails = getCurrentThreadEmails();
            renderLinkedEmails(threadEmails);

            const threadCounter = document.getElementById('threadEmailsCount');
            if (threadCounter) threadCounter.textContent = String(threadEmails.length);

            if (!threadEmails.length) {
                selectedLinkedEmailId = null;
                renderEmailPreviewEmpty();
                return;
            }

            const selectedEmailExists = threadEmails.some((item) => Number(item.id) === Number(selectedLinkedEmailId));
            if (!selectedEmailExists) {
                selectedLinkedEmailId = Number(threadEmails[0].id);
            }

            if (loadDetail && selectedLinkedEmailId) {
                await loadLinkedEmailDetail(selectedLinkedEmailId, true);
            }

            setModuleContentState('emails', true);
        }

        function renderLinkedEmails(emails) {
            const container = document.getElementById('linked-emails-list');
            if (!container) return;
            if(!emails || emails.length === 0) {
                container.innerHTML = '<div class="p-3 text-center text-muted">Ingen linkede e-mails...</div>';
                return;
            }

            container.innerHTML = emails.map(e => {
                const isSelected = Number(selectedLinkedEmailId) === Number(e.id);
                const receivedDate = e.received_date ? new Date(e.received_date).toLocaleString('da-DK') : '-';
                const sender = e.sender_name || e.sender_email || '-';
                const subject = e.subject || '(Ingen emne)';
                const isOutgoing = isOutgoingEmail(e);
                const snippetSource = e.body_text || e.body_html || '';
                const snippet = snippetSource.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 130);
                const hasAttachments = Boolean(e.has_attachments) || Number(e.attachment_count || 0) > 0;

                return `
                <button type="button" class="list-group-item list-group-item-action border-0 border-bottom text-start ${isSelected ? 'active' : ''}" onclick="loadLinkedEmailDetail(${e.id})">
                    <div class="d-flex justify-content-between align-items-start gap-2">
                        <div class="flex-grow-1 overflow-hidden">
                            <div class="fw-semibold text-truncate">${escapeHtml(subject)}</div>
                            <div class="small ${isSelected ? 'text-white-50' : 'text-muted'} text-truncate">${escapeHtml(sender)}</div>
                            <div class="small mt-1">${isOutgoing
                                ? '<span class="badge bg-primary-subtle text-primary-emphasis">Udgående</span>'
                                : '<span class="badge bg-success-subtle text-success-emphasis">Indgående</span>'
                            }</div>
                            <div class="small ${isSelected ? 'text-white-50' : 'text-muted'} text-truncate">${escapeHtml(snippet || 'Ingen preview')}</div>
                        </div>
                        <div class="d-flex flex-column align-items-end gap-1">
                            <div class="small ${isSelected ? 'text-white-50' : 'text-muted'}">${escapeHtml(receivedDate)}</div>
                            ${hasAttachments ? '<span class="badge bg-info-subtle text-info-emphasis">📎</span>' : ''}
                            ${!e.is_read ? '<span class="badge bg-warning text-dark">Ulæst</span>' : ''}
                            <span class="btn btn-sm btn-link p-0 ${isSelected ? 'text-white' : 'text-danger'}" onclick="event.stopPropagation(); unlinkEmail(${e.id});" title="Fjern link">
                                <i class="bi bi-link-45deg" style="text-decoration: line-through;"></i>
                            </span>
                        </div>
                    </div>
                </button>
                `;
            }).join('');
        }

        function renderEmailPreviewEmpty() {
            const panel = document.getElementById('email-preview-panel');
            if (!panel) return;
            selectedLinkedEmailDetail = null;
            panel.innerHTML = `
                <div class="p-3 text-center text-muted d-flex align-items-center justify-content-center flex-grow-1">
                    Vælg en e-mail i listen for at se indhold og vedhæftninger
                </div>
            `;
        }

        async function loadLinkedEmailDetail(emailId, skipRefresh = false) {
            selectedLinkedEmailId = Number(emailId);
            const panel = document.getElementById('email-preview-panel');
            if (!panel) return;

            panel.innerHTML = `
                <div class="p-4 text-center text-muted">
                    <div class="spinner-border spinner-border-sm me-2" role="status"></div>
                    Henter e-mail...
                </div>
            `;

            if (!skipRefresh) {
                const threadEmails = getCurrentThreadEmails();
                renderLinkedEmails(threadEmails);
            }

            try {
                const res = await fetch(`/api/v1/emails/${emailId}`);
                if (!res.ok) {
                    panel.innerHTML = '<div class="p-3 text-danger">Kunne ikke hente e-mail detaljer.</div>';
                    return;
                }

                const email = await res.json();
                const subject = email.subject || '(Ingen emne)';
                const sender = email.sender_name || email.sender_email || '-';
                const received = email.received_date ? new Date(email.received_date).toLocaleString('da-DK') : '-';
                const attachments = Array.isArray(email.attachments) ? email.attachments : [];
                const bodyText = email.body_text || '';
                const bodyHtml = email.body_html || '';
                selectedLinkedEmailDetail = email;

                panel.innerHTML = `
                    <div class="border-bottom p-3">
                        <div class="fw-bold mb-1">${escapeHtml(subject)}</div>
                        <div class="small text-muted">Fra: ${escapeHtml(sender)}</div>
                        <div class="small text-muted">Dato: ${escapeHtml(received)}</div>
                        <div class="mt-2 d-flex gap-2">
                            <button type="button" class="btn btn-sm btn-primary" onclick="openReplyToLinkedEmail()">
                                <i class="bi bi-reply me-1"></i>Svar i tråd
                            </button>
                        </div>
                    </div>
                    <div class="p-3 border-bottom">
                        <div class="small fw-semibold mb-2">Vedhæftninger (${attachments.length})</div>
                        <div id="email-attachments-list" class="d-flex flex-wrap gap-2"></div>
                    </div>
                    <div class="p-3 overflow-auto" style="max-height: 45vh; white-space: normal;">
                        ${bodyText ? `<pre class="mb-0" style="white-space: pre-wrap; font-family: inherit;">${escapeHtml(bodyText)}</pre>` : (bodyHtml ? bodyHtml : '<div class="text-muted">Ingen indhold</div>')}
                    </div>
                `;

                const attachmentContainer = document.getElementById('email-attachments-list');
                if (attachmentContainer) {
                    if (!attachments.length) {
                        attachmentContainer.innerHTML = '<span class="text-muted small">Ingen vedhæftninger</span>';
                    } else {
                        attachmentContainer.innerHTML = attachments.map(att => {
                            const attachmentName = att.filename || `Vedhæftning ${att.id}`;
                            const url = `/api/v1/emails/${email.id}/attachments/${att.id}`;
                            return `<a class="btn btn-sm btn-outline-secondary" href="${url}"><i class="bi bi-download me-1"></i>${escapeHtml(attachmentName)}</a>`;
                        }).join('');
                    }
                }

                const cacheIdx = linkedEmailsCache.findIndex((item) => Number(item.id) === Number(email.id));
                if (cacheIdx >= 0) {
                    linkedEmailsCache[cacheIdx].is_read = true;
                }

                const filteredIdx = filteredLinkedEmailsCache.findIndex((item) => Number(item.id) === Number(email.id));
                if (filteredIdx >= 0) {
                    filteredLinkedEmailsCache[filteredIdx].is_read = true;
                }

                if (!skipRefresh) {
                    const threadEmails = getCurrentThreadEmails();
                    renderLinkedEmails(threadEmails);
                    renderEmailThreads(buildThreadGroups(filteredLinkedEmailsCache));
                }
            } catch (e) {
                console.error(e);
                selectedLinkedEmailDetail = null;
                panel.innerHTML = '<div class="p-3 text-danger">Fejl ved hentning af e-mail detaljer.</div>';
            }
        }
        
        async function unlinkEmail(emailId) {
            if(!confirm("Fjern link til denne email?")) return;
            try {
                const res = await fetch(`/api/v1/sag/${caseIds}/email-links/${emailId}`, { method: 'DELETE' });
                if(res.ok) {
                    if (Number(selectedLinkedEmailId) === Number(emailId)) {
                        selectedLinkedEmailId = null;
                        renderEmailPreviewEmpty();
                    }
                    loadLinkedEmails();
                }
            } catch(e) { alert(e); }
        }

        // Email Search
        const emailSearchInput = document.getElementById('emailSearchInput');
        const emailSearchResults = document.getElementById('emailSearchResults');
        let emailDebounce = null;

        if(emailSearchInput) {
            emailSearchInput.addEventListener('input', e => {
                clearTimeout(emailDebounce);
                const q = e.target.value.trim();
                if(q.length < 2) {
                    emailSearchResults.style.display = 'none'; 
                    return; 
                }
                emailDebounce = setTimeout(() => searchEmails(q), 300);
            });
            
            // Hide on outside click
            document.addEventListener('click', e => {
                if(!emailSearchInput.contains(e.target) && !emailSearchResults.contains(e.target)) {
                    emailSearchResults.style.display = 'none';
                }
            });
        }

        ['emailFilterInput', 'emailAttachmentFilter', 'emailReadFilter'].forEach((id) => {
            const el = document.getElementById(id);
            if (!el) return;
            const eventName = id === 'emailFilterInput' ? 'input' : 'change';
            el.addEventListener(eventName, () => {
                applyLinkedEmailFilters(true);
            });
        });

        async function searchEmails(query) {
            try {
                const res = await fetch(`/api/v1/emails?q=${encodeURIComponent(query)}&limit=5`);
                if(res.ok) {
                    const emails = await res.json();
                    renderEmailSuggestions(emails);
                    emailSearchResults.style.display = 'block';
                }
            } catch(e) { console.error(e); }
        }

        function renderEmailSuggestions(emails) {
             if(!emails.length) {
                 emailSearchResults.innerHTML = '<div class="list-group-item text-muted">Ingen fundet</div>';
                 return;
             }
             emailSearchResults.innerHTML = emails.map(e => `
                 <button class="list-group-item list-group-item-action" onclick="linkEmail(${e.id})">
                     <div class="fw-bold text-truncate">${e.subject}</div>
                     <div class="small text-muted">${e.sender_email}</div>
                 </button>
             `).join('');
        }

        async function linkEmail(emailId) {
             emailSearchInput.value = '';
             emailSearchResults.style.display = 'none';
             try {
                 const res = await fetch(`/api/v1/sag/${caseIds}/email-links`, {
                     method: 'POST',
                     headers: {'Content-Type': 'application/json'},
                     body: JSON.stringify({email_id: emailId})
                 });
                 if(res.ok) loadLinkedEmails();
                 else alert("Kunne ikke linke email");
             } catch(e) { alert(e); }
        }

        // Email Import Drag & Drop (.msg / .eml)
        const emailDropZone = document.getElementById('emailDropZone');
        if(emailDropZone) {
            emailDropZone.addEventListener('dragover', e => { e.preventDefault(); emailDropZone.classList.add('bg-warning-subtle'); });
            emailDropZone.addEventListener('dragleave', e => { e.preventDefault(); emailDropZone.classList.remove('bg-warning-subtle'); });
            emailDropZone.addEventListener('drop', e => {
                e.preventDefault();
                emailDropZone.classList.remove('bg-warning-subtle');
                const files = e.dataTransfer.files;
                if(files.length) uploadEmailFile(files[0]);
            });
        }
        
        async function uploadEmailFile(file) {
            if (!file) return;
            const lowerName = String(file.name || '').toLowerCase();
            if (!(lowerName.endsWith('.eml') || lowerName.endsWith('.msg'))) {
                alert('Kun .eml og .msg filer understøttes');
                return;
            }

            const formData = new FormData();
            formData.append('file', file);
            
            // Show busy indicator
            emailDropZone.style.opacity = '0.5';
            
            try {
                const res = await fetch(`/api/v1/sag/${caseIds}/upload-email`, {
                    method: 'POST',
                    body: formData
                });
                if(res.ok) {
                    loadLinkedEmails();
                } else {
                    alert('Import fejlede');
                }
            } catch(e) { alert(e); }
            finally {
                emailDropZone.style.opacity = '1';
            }
        }

        // Load content on start
        document.addEventListener('DOMContentLoaded', () => {
            const caseEmailSendBtn = document.getElementById('caseEmailSendBtn');
            if (caseEmailSendBtn) {
                caseEmailSendBtn.addEventListener('click', sendCaseEmail);
            }

            const caseEmailRewriteBtn = document.getElementById('caseEmailRewriteBtn');
            if (caseEmailRewriteBtn) {
                caseEmailRewriteBtn.addEventListener('click', rewriteCaseEmailWithApproval);
            }

            const rewriteApplyAllBtn = document.getElementById('rewriteApplyAllBtn');
            if (rewriteApplyAllBtn) {
                rewriteApplyAllBtn.addEventListener('click', () => applyRewriteChanges('all'));
            }

            const rewriteApplySelectedBtn = document.getElementById('rewriteApplySelectedBtn');
            if (rewriteApplySelectedBtn) {
                rewriteApplySelectedBtn.addEventListener('click', () => applyRewriteChanges('selected'));
            }

            const caseEmailComposeModal = document.getElementById('caseEmailComposeModal');
            if (caseEmailComposeModal) {
                caseEmailComposeModal.addEventListener('show.bs.modal', () => {
                    const statusEl = document.getElementById('caseEmailSendStatus');
                    if (statusEl) {
                        statusEl.className = 'text-muted';
                        statusEl.textContent = '';
                    }
                    prefillCaseEmailCompose();
                    updateCaseEmailAttachmentOptions(sagFilesCache);
                });
            }

            prefillCaseEmailCompose();
            updateCaseEmailAttachmentOptions(sagFilesCache);
            loadSagFiles();
            loadLinkedEmails();
        });

    