← Accessible Components results

Accessible Components

ling 3.0 Flash · full

Initial view of Accessible Components

Reviews & generation details

Reviews

No human review yet. Unreviewed does not mean unsuccessful.

Generation context

Imported historical artifact. Its exact original instructions and lineage are not known.

Generation prompt · exact text unavailable

See the current prompt for reference. It may differ from the instructions used to generate this artifact.

Inspect original source 60,599 bytes · SHA-256 13816f7a741f
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Accessible Component Showcase</title>
<style>
/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   DESIGN SYSTEM CONSTANTS
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
:root {
  --color-bg: #f8f7f5;
  --color-surface: #ffffff;
  --color-border: #d4d0c8;
  --color-text: #1a1a1a;
  --color-text-secondary: #5a5650;
  --color-text-muted: #8a8480;
  --color-accent: #2b6cb0;
  --color-accent-hover: #1e4f8a;
  --color-accent-light: #e8f0fa;
  --color-focus: #2b6cb0;
  --color-focus-ring: rgba(43,108,176,0.45);
  --color-danger: #c0392b;
  --color-success: #278040;
  --radius: 8px;
  --radius-sm: 4px;
  --shadow-sm: 0 1px 3px rgba(0,0,0,0.06);
  --shadow-md: 0 4px 12px rgba(0,0,0,0.08);
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", Arial, sans-serif;
  --font-mono: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", Menlo, Courier, monospace;
  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 24px; --space-6: 32px; --space-8: 48px;
  --anim-fast: 150ms; --anim-normal: 250ms; --anim-slow: 350ms;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   RESET & BASE
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { font-size: 16px; scroll-behavior: smooth; }
body {
  font-family: var(--font-sans);
  background: var(--color-bg);
  color: var(--color-text);
  line-height: 1.6;
  min-height: 100vh;
  display: flex;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   SKIP LINK
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
.skip-link {
  position: absolute;
  top: -100px;
  left: var(--space-4);
  background: var(--color-accent);
  color: #fff;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  z-index: 1000;
  font-weight: 600;
  text-decoration: none;
  transition: top var(--anim-fast);
}
.skip-link:focus {
  top: var(--space-4);
  outline: 3px solid var(--color-focus);
  outline-offset: 2px;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   LAYOUT
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
.sidebar {
  position: fixed;
  top: 0; left: 0;
  width: 220px;
  height: 100vh;
  background: var(--color-surface);
  border-right: 1px solid var(--color-border);
  padding: var(--space-5) var(--space-4);
  overflow-y: auto;
  flex-shrink: 0;
}
.sidebar h1 {
  font-size: 1.1rem;
  margin-bottom: var(--space-4);
  color: var(--color-accent);
  letter-spacing: -0.01em;
}
.sidebar nav a {
  display: block;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--color-text-secondary);
  text-decoration: none;
  font-size: 0.9rem;
  margin-bottom: 2px;
  transition: background var(--anim-fast), color var(--anim-fast);
}
.sidebar nav a:hover,
.sidebar nav a:focus {
  background: var(--color-accent-light);
  color: var(--color-accent);
  outline: none;
}
.sidebar nav a:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

.main {
  margin-left: 220px;
  padding: var(--space-8) var(--space-8) var(--space-8) calc(220px + var(--space-8));
  max-width: 900px;
  flex: 1;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   FOCUS INDICATORS (global)
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
a:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   COMPONENT SECTIONS
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
.component-section {
  margin-bottom: var(--space-8);
  padding: var(--space-6);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
}
.component-section h2 {
  font-size: 1.35rem;
  margin-bottom: var(--space-2);
  color: var(--color-text);
}
.component-section .caption {
  color: var(--color-text-secondary);
  font-size: 0.85rem;
  margin-bottom: var(--space-5);
  line-height: 1.5;
}
.component-section .caption kbd {
  background: #eee;
  border: 1px solid #ccc;
  border-radius: 3px;
  padding: 1px 5px;
  font-family: var(--font-mono);
  font-size: 0.8rem;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   MODAL
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 900;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--anim-normal), visibility var(--anim-normal);
}
.modal-overlay.open {
  opacity: 1;
  visibility: visible;
}
.modal-dialog {
  background: var(--color-surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  padding: var(--space-6);
  max-width: 480px;
  width: 90%;
  max-height: 80vh;
  overflow-y: auto;
  transform: translateY(16px);
  transition: transform var(--anim-normal);
}
.modal-overlay.open .modal-dialog {
  transform: translateY(0);
}
.modal-dialog h3 {
  margin-bottom: var(--space-4);
  font-size: 1.15rem;
}
.modal-dialog p {
  margin-bottom: var(--space-4);
  color: var(--color-text-secondary);
}
.modal-dialog .btn-row {
  display: flex;
  gap: var(--space-3);
  justify-content: flex-end;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   BUTTONS
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text);
  font-size: 0.9rem;
  cursor: pointer;
  transition: background var(--anim-fast), border-color var(--anim-fast);
  font-family: var(--font-sans);
}
.btn:hover {
  background: #f0f0f0;
}
.btn-primary {
  background: var(--color-accent);
  color: #fff;
  border-color: var(--color-accent);
}
.btn-primary:hover {
  background: var(--color-accent-hover);
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   DROPDOWN MENU
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
.dropdown-wrapper {
  position: relative;
  display: inline-block;
}
.dropdown-menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  min-width: 180px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  padding: var(--space-2) 0;
  z-index: 100;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity var(--anim-fast), transform var(--anim-fast), visibility var(--anim-fast);
}
.dropdown-menu.open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.dropdown-menu [role="menuitem"] {
  display: block;
  width: 100%;
  text-align: left;
  padding: var(--space-2) var(--space-4);
  background: none;
  border: none;
  cursor: pointer;
  font-size: 0.9rem;
  color: var(--color-text);
  transition: background var(--anim-fast);
}
.dropdown-menu [role="menuitem"]:hover,
.dropdown-menu [role="menuitem"]:focus {
  background: var(--color-accent-light);
}
.dropdown-menu [role="menuitem"].highlighted {
  background: var(--color-accent);
  color: #fff;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   TABS
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
.tabs-nav {
  display: flex;
  gap: 2px;
  border-bottom: 2px solid var(--color-border);
  margin-bottom: var(--space-4);
}
.tabs-nav [role="tab"] {
  padding: var(--space-2) var(--space-4);
  border: none;
  background: none;
  cursor: pointer;
  font-size: 0.9rem;
  color: var(--color-text-secondary);
  border-bottom: 2px solid transparent;
  margin-bottom: -2px;
  transition: color var(--anim-fast), border-color var(--anim-fast);
  font-family: var(--font-sans);
}
.tabs-nav [role="tab"]:hover {
  color: var(--color-text);
}
.tabs-nav [role="tab"][aria-selected="true"] {
  color: var(--color-accent);
  border-bottom-color: var(--color-accent);
  font-weight: 600;
}
.tabs-panel {
  display: none;
}
.tabs-panel.visible {
  display: block;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   COMBOBOX
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
.combobox-wrapper {
  position: relative;
  width: 300px;
}
.combobox-wrapper input {
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-size: 0.95rem;
  font-family: var(--font-sans);
}
.combobox-listbox {
  position: absolute;
  top: calc(100% + 2px);
  left: 0;
  right: 0;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  max-height: 200px;
  overflow-y: auto;
  z-index: 100;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity var(--anim-fast), transform var(--anim-fast), visibility var(--anim-fast);
}
.combobox-listbox.open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.combobox-listbox [role="option"] {
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
  font-size: 0.9rem;
  transition: background var(--anim-fast);
}
.combobox-listbox [role="option"]:hover,
.combobox-listbox [role="option"].highlighted {
  background: var(--color-accent-light);
}
.combobox-listbox [role="option"].highlighted {
  background: var(--color-accent);
  color: #fff;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   ACCORDION
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
.accordion-item {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-2);
  overflow: hidden;
}
.accordion-header {
  width: 100%;
  text-align: left;
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface);
  border: none;
  cursor: pointer;
  font-size: 0.95rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-family: var(--font-sans);
}
.accordion-header:hover {
  background: #f5f5f5;
}
.accordion-header .arrow {
  transition: transform var(--anim-normal);
  font-size: 0.8rem;
}
.accordion-header[aria-expanded="true"] .arrow {
  transform: rotate(90deg);
}
.accordion-panel {
  max-height: 0;
  overflow: hidden;
  transition: max-height var(--anim-slow);
}
.accordion-panel[hidden] {
  display: none;
}
.accordion-panel .inner {
  padding: 0 var(--space-4) var(--space-4);
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   REDUCED MOTION
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   RESPONSIVE
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 700px) {
  .sidebar {
    display: none;
  }
  .main {
    margin-left: 0;
    padding: var(--space-4);
  }
}
</style>
</head>
<body>

<!-- SKIP LINK -->
<a class="skip-link" href="#main-content">Skip to main content</a>

<main id="main-content" class="main">

<!-- ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   MODAL SECTION
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ -->
<section class="component-section" id="sec-modal" aria-labelledby="heading-modal">
  <h2 id="heading-modal">Modal Dialog</h2>
  <p class="caption">Opens a focused dialog; <kbd>Tab</kbd>/<kbd>Shift+Tab</kbd> cycle inside · <kbd>Esc</kbd> or click backdrop to close · focus returns to trigger</p>
  <button class="btn btn-primary" id="modal-trigger">Open Modal</button>
</section>

<!-- Modal overlay -->
<div class="modal-overlay" id="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="modal-title" aria-hidden="true">
  <div class="modal-dialog" tabindex="-1">
    <h3 id="modal-title">Modal Dialog</h3>
    <p>This is a modal dialog. Focus is trapped inside while it is open. Press <kbd>Esc</kbd>, click the backdrop, or use the close button to dismiss it.</p>
    <div class="btn-row">
      <button class="btn" id="modal-close">Close</button>
    </div>
  </div>
</div>

<!-- ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   DROPDOWN MENU SECTION
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ -->
<section class="component-section" id="sec-dropdown" aria-labelledby="heading-dropdown">
  <h2 id="heading-dropdown">Dropdown Menu</h2>
  <p class="caption"><kbd>Click</kbd> or <kbd>Enter</kbd>/<kbd>Space</kbd> to open · <kbd>↓</kbd> focuses first item · <kbd>↑↓</kbd> navigate · <kbd>Esc</kbd> closes · <kbd>Tab</kbd> moves onward</p>
  <div class="dropdown-wrapper" id="dropdown-wrapper">
    <button class="btn btn-primary" id="dropdown-trigger" aria-haspopup="menu" aria-expanded="false" aria-controls="dropdown-menu">Menu</button>
    <div class="dropdown-menu" id="dropdown-menu" role="menu" aria-labelledby="dropdown-trigger">
      <button role="menuitem" data-action="copy">Copy</button>
      <button role="menuitem" data-action="paste">Paste</button>
      <button role="menuitem" data-action="cut">Cut</button>
      <button role="menuitem" data-action="select-all">Select All</button>
      <button role="menuitem" data-action="delete">Delete</button>
    </div>
  </div>
</section>

<!-- ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   TABS SECTION
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ -->
<section class="component-section" id="sec-tabs" aria-labelledby="heading-tabs">
  <h2 id="heading-tabs">Tabs</h2>
  <p class="caption"><kbd>←→</kbd> switch tabs · <kbd>Home/End</kbd> first/last · only active tab in tab order</p>
  <div role="tablist" aria-label="Sample tabs" class="tabs-nav" id="tabs-tablist">
    <button role="tab" aria-selected="true" aria-controls="panel-1" id="tab-1">Tab One</button>
    <button role="tab" aria-selected="false" aria-controls="panel-2" id="tab-2" tabindex="-1">Tab Two</button>
    <button role="tab" aria-selected="false" aria-controls="panel-3" id="tab-3" tabindex="-1">Tab Three</button>
  </div>
  <div role="tabpanel" id="panel-1" aria-labelledby="tab-1" class="tabs-panel visible">Content for Tab One. This panel is visible and reachable by Tab key.</div>
  <div role="tabpanel" id="panel-2" aria-labelledby="tab-2" class="tabs-panel" hidden>Content for Tab Two. Use the arrow keys to switch between tabs.</div>
  <div role="tabpanel" id="panel-3" aria-labelledby="tab-3" class="tabs-panel" hidden>Content for Tab Three. Only the active tab is focusable at a time.</div>
</section>

<!-- ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   COMBOBOX SECTION
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ -->
<section class="component-section" id="sec-combobox" aria-labelledby="heading-combobox">
  <h2 id="heading-combobox">Combobox (Autocomplete)</h2>
  <p class="caption"><kbd>Type</kbd> to filter · <kbd>↓↑</kbd> navigate · <kbd>Enter</kbd> select · <kbd>Esc</kbd> closes · <kbd>Tab</kbd> moves onward</p>
  <div class="combobox-wrapper" id="combobox-wrapper">
    <input type="text" id="combobox-input" role="combobox" aria-expanded="false" aria-controls="combobox-listbox" aria-activedescendant="" aria-autocomplete="list" placeholder="Search…">
    <div class="combobox-listbox" id="combobox-listbox" role="listbox" aria-label="Search suggestions">
      <div role="option" id="opt-0" data-value="Apple">Apple</div>
      <div role="option" id="opt-1" data-value="Apricot">Apricot</div>
      <div role="option" id="opt-2" data-value="Banana">Banana</div>
      <div role="option" id="opt-3" data-value="Blackberry">Blackberry</div>
      <div role="option" id="opt-4" data-value="Blueberry">Blueberry</div>
      <div role="option" id="opt-5" data-value="Cherry">Cherry</div>
      <div role="option" id="opt-6" data-value="Coconut">Coconut</div>
      <div role="option" id="opt-7" data-value="Cranberry">Cranberry</div>
      <div role="option" id="opt-8" data-value="Date">Date</div>
      <div role="option" id="opt-9" data-value="Dragonfruit">Dragonfruit</div>
      <div role="option" id="opt-10" data-value="Fig">Fig</div>
      <div role="option" id="opt-11" data-value="Grape">Grape</div>
      <div role="option" id="opt-12" data-value="Guava">Guava</div>
      <div role="option" id="opt-13" data-value="Kiwi">Kiwi</div>
      <div role="option" id="opt-14" data-value="Lemon">Lemon</div>
      <div role="option" id="opt-15" data-value="Lime">Lime</div>
      <div role="option" id="opt-16" data-value="Mango">Mango</div>
      <div role="option" id="opt-17" data-value="Nectarine">Nectarine</div>
      <div role="option" id="opt-18" data-value="Orange">Orange</div>
      <div role="option" id="opt-19" data-value="Papaya">Papaya</div>
    </div>
  </div>
</section>

<!-- ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   ACCORDION SECTION
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ -->
<section class="component-section" id="sec-accordion" aria-labelledby="heading-accordion">
  <h2 id="heading-accordion">Accordion / Disclosure Group</h2>
  <p class="caption"><kbd>Click</kbd> or <kbd>Enter</kbd>/<kbd>Space</kbd> to expand/collapse · multiple may be open · animate then toggle <code>hidden</code> at end</p>
  <div class="accordion-item">
    <button class="accordion-header" aria-expanded="false" aria-controls="acc-panel-1">First Section</button>
    <div class="accordion-panel" id="acc-panel-1" hidden><div class="inner"><p>Content for the first section. Collapses smoothly when hidden.</p></div></div>
  </div>
  <div class="accordion-item">
    <button class="accordion-header" aria-expanded="false" aria-controls="acc-panel-2">Second Section</button>
    <div class="accordion-panel" id="acc-panel-2" hidden><div class="inner"><p>Content for the second section. Also supports multiple open panels at once.</p></div></div>
  </div>
  <div class="accordion-item">
    <button class="accordion-header" aria-expanded="false" aria-controls="acc-panel-3">Third Section</button>
    <div class="accordion-panel" id="acc-panel-3" hidden><div class="inner"><p>Content for the third section. Uses height animation then toggles hidden attribute.</p></div></div>
  </div>
</section>

</main>

<script>
/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   UTILITIES
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
var $ = function(sel, ctx) { return (ctx || document).querySelector(sel); };
var $$ = function(sel, ctx) { return Array.prototype.slice.call((ctx || document).querySelectorAll(sel)); };

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   FOCUS TRAP (reusable)
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
function FocusTrap(container, onReturn) {
  this.container = container;
  this.onReturn = onReturn;
  this._prevFocus = null;
  this._activate = this.activate.bind(this);
  this._deactivate = this.deactivate.bind(this);
  this._keydown = this._keydownHandler.bind(this);
}

FocusTrap.prototype.activate = function() {
  this._prevFocus = document.activeElement;
  this._focusable = this._getFocusable(this.container);
  this._first = this._focusable[0] || null;
  this._last = this._focusable[this._focusable.length - 1] || null;
  if (this._first && this._first.focus) this._first.focus();
  document.addEventListener('keydown', this._keydown);
};

FocusTrap.prototype.deactivate = function() {
  document.removeEventListener('keydown', this._keydown);
  if (this._prevFocus && this._prevFocus.focus) this._prevFocus.focus();
};

FocusTrap.prototype._getFocusable = function(el) {
  var nodes = $$(
    'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])',
    el
  ).filter(function(e) {
    var node = e;
    while (node && node !== el) {
      if (node.hasAttribute && node.getAttribute('aria-hidden') === 'true') return false;
      node = node.parentElement;
    }
    return true;
  });
  return nodes;
};

FocusTrap.prototype._keydownHandler = function(e) {
  if (e.key !== 'Tab') return;
  var focusable = this._getFocusable(this.container);
  if (!focusable.length) return;
  var first = focusable[0];
  var last = focusable[focusable.length - 1];
  if (e.shiftKey) {
    if (document.activeElement === first || !this.container.contains(document.activeElement)) {
      e.preventDefault();
      last.focus();
    }
  } else {
    if (document.activeElement === last || !this.container.contains(document.activeElement)) {
      e.preventDefault();
      first.focus();
    }
  }
};

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   MODAL
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
var modalOverlay = $('#modal-overlay');
var modalTrigger = $('#modal-trigger');
var modalClose = $('#modal-close');
var modalDialog = modalOverlay.querySelector('.modal-dialog');
var modalTrap = null;

function openModal() {
  modalOverlay.classList.add('open');
  modalOverlay.setAttribute('aria-hidden', 'false');
  modalDialog.focus();
  modalTrap = new FocusTrap(modalDialog, function() {
    modalOverlay.classList.remove('open');
    modalOverlay.setAttribute('aria-hidden', 'true');
    modalTrigger.focus();
  });
  modalTrap.activate();
}

function closeModal() {
  modalOverlay.classList.remove('open');
  modalOverlay.setAttribute('aria-hidden', 'true');
  if (modalTrap) modalTrap.deactivate();
  modalTrigger.focus();
}

modalTrigger.addEventListener('click', openModal);
modalClose.addEventListener('click', closeModal);
modalOverlay.addEventListener('click', function(e) {
  if (e.target === modalOverlay) closeModal();
});

document.addEventListener('keydown', function(e) {
  if (e.key === 'Escape' && modalOverlay.classList.contains('open')) {
    closeModal();
  }
});

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   DROPDOWN MENU
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
var dropdownTrigger = $('#dropdown-trigger');
var dropdownMenu = $('#dropdown-menu');
var dropdownOpen = false;

function openDropdown() {
  dropdownOpen = true;
  dropdownTrigger.setAttribute('aria-expanded', 'true');
  dropdownMenu.classList.add('open');
  var firstItem = dropdownMenu.querySelector('[role="menuitem"]');
  if (firstItem) firstItem.focus();
}

function closeDropdown() {
  dropdownOpen = false;
  dropdownTrigger.setAttribute('aria-expanded', 'false');
  dropdownMenu.classList.remove('open');
  dropdownTrigger.focus();
}

dropdownTrigger.addEventListener('click', function() {
  if (dropdownOpen) closeDropdown(); else openDropdown();
});

dropdownTrigger.addEventListener('keydown', function(e) {
  if (e.key === ' ' || e.key === 'Enter') {
    e.preventDefault();
    if (dropdownOpen) closeDropdown(); else openDropdown();
  }
});

var menuItems = dropdownMenu.querySelectorAll('[role="menuitem"]');
var highlightedIdx = -1;

dropdownMenu.addEventListener('keydown', function(e) {
  if (!dropdownOpen) return;
  if (e.key === 'ArrowDown') {
    e.preventDefault();
    highlightedIdx = (highlightedIdx + 1) % menuItems.length;
    updateDropdownHighlight();
  } else if (e.key === 'ArrowUp') {
    e.preventDefault();
    highlightedIdx = (highlightedIdx - 1 + menuItems.length) % menuItems.length;
    updateDropdownHighlight();
  } else if (e.key === 'Escape') {
    e.preventDefault();
    closeDropdown();
  } else if (e.key === 'Tab') {
    closeDropdown();
  } else if (e.key === 'Enter' || e.key === ' ') {
    e.preventDefault();
    if (highlightedIdx >= 0 && menuItems[highlightedIdx]) {
      var action = menuItems[highlightedIdx].dataset.action;
      if (action) {
        alert('Action: ' + menuItems[highlightedIdx].textContent.trim());
      }
      closeDropdown();
    }
  }
});

function updateDropdownHighlight() {
  for (var i = 0; i < menuItems.length; i++) {
    if (i === highlightedIdx) {
      menuItems[i].classList.add('highlighted');
      menuItems[i].focus();
    } else {
      menuItems[i].classList.remove('highlighted');
    }
  }
}

dropdownMenu.addEventListener('click', function(e) {
  var item = e.target.closest('[role="menuitem"]');
  if (!item) return;
  var action = item.dataset.action;
  if (action) alert('Action: ' + item.textContent.trim());
  closeDropdown();
});

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   TABS
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
var tablist = $('#tabs-tablist');
var tabs = tablist.querySelectorAll('[role="tab"]');
var panels = $$('[role="tabpanel"]');

function activateTab(tab) {
  for (var i = 0; i < tabs.length; i++) {
    var isActive = tabs[i] === tab;
    tabs[i].setAttribute('aria-selected', isActive ? 'true' : 'false');
    tabs[i].setAttribute('tabindex', isActive ? '0' : '-1');
  }
  for (var j = 0; j < panels.length; j++) {
    panels[j].classList.remove('visible');
    panels[j].hidden = true;
  }
  var panelId = tab.getAttribute('aria-controls');
  var panel = $('#' + panelId);
  if (panel) {
    panel.classList.add('visible');
    panel.hidden = false;
  }
}

tablist.addEventListener('keydown', function(e) {
  var currentTab = document.activeElement;
  if (!currentTab || currentTab.getAttribute('role') !== 'tab') return;
  var tabArr = Array.prototype.slice.call(tabs);
  var idx = tabArr.indexOf(currentTab);
  if (idx === -1) return;

  if (e.key === 'ArrowRight') {
    e.preventDefault();
    var next = tabArr[(idx + 1) % tabArr.length];
    activateTab(next);
    next.focus();
  } else if (e.key === 'ArrowLeft') {
    e.preventDefault();
    var prev = tabArr[(idx - 1 + tabArr.length) % tabArr.length];
    activateTab(prev);
    prev.focus();
  } else if (e.key === 'Home') {
    e.preventDefault();
    activateTab(tabs[0]);
    tabs[0].focus();
  } else if (e.key === 'End') {
    e.preventDefault();
    activateTab(tabs[tabs.length - 1]);
    tabs[tabs.length - 1].focus();
  }
});

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   COMBOBOX
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
var comboboxInput = $('#combobox-input');
var comboboxListbox = $('#combobox-listbox');
var allOptions = Array.prototype.slice.call(comboboxListbox.querySelectorAll('[role="option"]'));
var comboboxHighlighted = -1;
var comboboxOpen = false;

function updateCombobox() {
  var val = comboboxInput.value.toLowerCase();
  var filtered = allOptions.filter(function(opt) {
    return opt.textContent.toLowerCase().indexOf(val) !== -1;
  });

  for (var i = 0; i < allOptions.length; i++) {
    allOptions[i].style.display = 'none';
    allOptions[i].classList.remove('highlighted');
  }

  if (filtered.length > 0) {
    comboboxListbox.classList.add('open');
    comboboxOpen = true;
  } else {
    comboboxListbox.classList.remove('open');
    comboboxOpen = false;
  }

  comboboxInput.setAttribute('aria-expanded', comboboxOpen ? 'true' : 'false');
  comboboxHighlighted = -1;

  if (comboboxOpen && filtered.length > 0) {
    for (var j = 0; j < filtered.length; j++) {
      filtered[j].style.display = '';
    }
  }
}

function highlightOption(idx) {
  var visible = Array.prototype.slice.call(comboboxListbox.querySelectorAll('[role="option"]')).filter(function(o) {
    return o.style.display !== 'none';
  });
  if (!visible.length) return;
  comboboxHighlighted = Math.max(0, Math.min(idx, visible.length - 1));
  for (var i = 0; i < visible.length; i++) {
    if (i === comboboxHighlighted) {
      visible[i].classList.add('highlighted');
      visible[i].setAttribute('aria-selected', 'true');
      visible[i].scrollIntoView({ block: 'nearest' });
    } else {
      visible[i].classList.remove('highlighted');
      visible[i].setAttribute('aria-selected', 'false');
    }
  }
  var activeId = comboboxHighlighted >= 0 ? visible[comboboxHighlighted].id : '';
  comboboxInput.setAttribute('aria-activedescendant', activeId);
}

comboboxInput.addEventListener('input', updateCombobox);
comboboxInput.addEventListener('focus', function() {
  var visible = Array.prototype.slice.call(allOptions).filter(function(o) {
    return o.style.display !== 'none';
  });
  if (visible.length > 0) {
    comboboxListbox.classList.add('open');
    comboboxOpen = true;
    comboboxInput.setAttribute('aria-expanded', 'true');
    highlightOption(0);
  }
});

comboboxInput.addEventListener('keydown', function(e) {
  if (!comboboxOpen) return;
  var visible = Array.prototype.slice.call(comboboxListbox.querySelectorAll('[role="option"]')).filter(function(o) {
    return o.style.display !== 'none';
  });
  if (!visible.length) return;

  if (e.key === 'ArrowDown') {
    e.preventDefault();
    highlightOption(comboboxHighlighted + 1);
  } else if (e.key === 'ArrowUp') {
    e.preventDefault();
    highlightOption(comboboxHighlighted - 1);
  } else if (e.key === 'Enter') {
    e.preventDefault();
    if (comboboxHighlighted >= 0) {
      var opt = visible[comboboxHighlighted];
      comboboxInput.value = opt.textContent;
      comboboxInput.setAttribute('aria-activedescendant', opt.id);
    }
    comboboxInput.setAttribute('aria-expanded', 'false');
    comboboxListbox.classList.remove('open');
    comboboxOpen = false;
  } else if (e.key === 'Escape') {
    e.preventDefault();
    comboboxInput.setAttribute('aria-expanded', 'false');
    comboboxListbox.classList.remove('open');
    comboboxOpen = false;
  } else if (e.key === 'Tab') {
    comboboxInput.setAttribute('aria-expanded', 'false');
    comboboxListbox.classList.remove('open');
    comboboxOpen = false;
  }
});

document.addEventListener('click', function(e) {
  if (comboboxOpen && !comboboxInput.contains(e.target) && !comboboxListbox.contains(e.target)) {
    comboboxInput.setAttribute('aria-expanded', 'false');
    comboboxListbox.classList.remove('open');
    comboboxOpen = false;
  }
});

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   ACCORDION
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
var accordionHeaders = $$('.accordion-header');

for (var i = 0; i < accordionHeaders.length; i++) {
  (function(header) {
    header.addEventListener('click', function() {
      toggleAccordion(header);
    });
    header.addEventListener('keydown', function(e) {
      if (e.key === 'Enter' || e.key === ' ') {
        e.preventDefault();
        toggleAccordion(header);
      }
    });
  })(accordionHeaders[i]);
}

function toggleAccordion(header) {
  var panelId = header.getAttribute('aria-controls');
  var panel = document.getElementById(panelId);
  if (!panel) return;

  var isExpanded = header.getAttribute('aria-expanded') === 'true';
  header.setAttribute('aria-expanded', isExpanded ? 'false' : 'true');

  if (isExpanded) {
    panel.style.maxHeight = panel.scrollHeight + 'px';
    panel.offsetHeight;
    panel.style.maxHeight = '0px';
    var onEnd = function() {
      panel.hidden = true;
      panel.style.maxHeight = '';
      panel.removeEventListener('transitionend', onEnd);
    };
    panel.addEventListener('transitionend', onEnd);
    setTimeout(function() {
      if (!panel.hidden) {
        panel.hidden = true;
        panel.style.maxHeight = '';
      }
    }, 400);
  } else {
    panel.hidden = false;
    panel.style.maxHeight = '0px';
    panel.offsetHeight;
    panel.style.maxHeight = panel.scrollHeight + 'px';
    var onEnd2 = function() {
      panel.style.maxHeight = '';
      panel.removeEventListener('transitionend', onEnd2);
    };
    panel.addEventListener('transitionend', onEnd2);
  }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
   REDUCED MOTION
   ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
  var accordionPanels = $$('.accordion-panel');
  for (var i = 0; i < accordionPanels.length; i++) {
    accordionPanels[i].style.transition = 'none';
  }
}
</script>

</body>
</html>
<!-- agent-meta {"model":"inclusionai/ling-3.0-flash","provider":"openrouter","persona":"full","sessionId":"d040ec5b-27e9-4876-8fd4-6ce0fe127455","tokensIn":420664,"tokensOut":37207,"tokensTotal":457871,"cost":0.014367984,"turns":13,"toolCalls":12,"failedToolCalls":0,"timestamp":"2026-08-24T23:13:07.400Z"} -->