diff --git a/index.html b/index.html index 06cff1c..88d4c8b 100644 --- a/index.html +++ b/index.html @@ -1001,6 +1001,13 @@ Sovereignty and service always.`; var first = focusable[0]; var last = focusable[focusable.length - 1]; + // If focus escaped outside the overlay (e.g. to body), redirect to first + if (!crisisOverlay.contains(document.activeElement)) { + e.preventDefault(); + first.focus(); + return; + } + if (e.shiftKey) { // Shift+Tab: if on first, wrap to last if (document.activeElement === first) { @@ -1050,38 +1057,55 @@ Sovereignty and service always.`; } }, 1000); - overlayDismissBtn.focus(); + // Focus the first focusable element (call link) — dismiss button is still disabled + var firstFocusable = crisisOverlay.querySelector('a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])'); + if (firstFocusable) { + firstFocusable.focus(); + } } // Register focus trap on document (always listening, gated by class check) document.addEventListener('keydown', trapFocusInOverlay); + function dismissOverlay() { + crisisOverlay.classList.remove('active'); + if (overlayTimer) { + clearInterval(overlayTimer); + overlayTimer = null; + } + + // Re-enable background interaction + var mainApp = document.querySelector('.app'); + if (mainApp) mainApp.removeAttribute('inert'); + var chatSection = document.getElementById('chat'); + if (chatSection) chatSection.removeAttribute('aria-hidden'); + var footerEl = document.querySelector('footer'); + if (footerEl) footerEl.removeAttribute('aria-hidden'); + + // Restore focus to the element that had it before the overlay opened + if (_preOverlayFocusElement && typeof _preOverlayFocusElement.focus === 'function') { + _preOverlayFocusElement.focus(); + } else { + msgInput.focus(); + } + _preOverlayFocusElement = null; + } + overlayDismissBtn.addEventListener('click', function() { if (!overlayDismissBtn.disabled) { - crisisOverlay.classList.remove('active'); - if (overlayTimer) { - clearInterval(overlayTimer); - overlayTimer = null; - } - - // Re-enable background interaction - var mainApp = document.querySelector('.app'); - if (mainApp) mainApp.removeAttribute('inert'); - var chatSection = document.getElementById('chat'); - if (chatSection) chatSection.removeAttribute('aria-hidden'); - var footerEl = document.querySelector('footer'); - if (footerEl) footerEl.removeAttribute('aria-hidden'); - - // Restore focus to the element that had it before the overlay opened - if (_preOverlayFocusElement && typeof _preOverlayFocusElement.focus === 'function') { - _preOverlayFocusElement.focus(); - } else { - msgInput.focus(); - } - _preOverlayFocusElement = null; + dismissOverlay(); } }); + // Escape key closes crisis overlay (only after dismiss button is enabled) + document.addEventListener('keydown', function(e) { + if (e.key !== 'Escape') return; + if (!crisisOverlay.classList.contains('active')) return; + if (overlayDismissBtn.disabled) return; // Don't bypass countdown + e.preventDefault(); + dismissOverlay(); + }); + // ===== MESSAGE RENDERING ===== function addMessage(role, text, skipSave) { var div = document.createElement('div');