
  :root {
    --bg:#0b0f14; --bg2:#111820; --bg3:#18212b; --line:#243040;
    --text:#e6edf3; --text2:#9fb0c3; --text3:#6b7f94;
    --accent:#38bdf8; --good:#34d399; --warn:#fbbf24; --bad:#f87171;
    --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
    --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, sans-serif;
    /* Without this the browser paints form controls light. The copy box is the
       DEFAULT path when the page is opened from a file, where the clipboard
       API is unavailable -- so a white slab in a dark UI is what most people
       who receive this actually see. */
    color-scheme: dark;
  }
  textarea { background:var(--bg); color:var(--text); border:1px solid var(--line);
             border-radius:9px; padding:11px; width:100%; resize:vertical; }
  * { box-sizing:border-box; -webkit-tap-highlight-color:transparent; }
  body { margin:0; background:var(--bg); color:var(--text); font-family:var(--sans);
    padding: max(18px, env(safe-area-inset-top)) 16px calc(28px + env(safe-area-inset-bottom));
    -webkit-text-size-adjust:100%; }
  main { max-width:560px; margin:0 auto; }
  header { display:flex; align-items:center; gap:11px; margin-bottom:5px; }
  header svg { width:30px; height:30px; flex:none; }
  h1 { font-size:20px; margin:0; letter-spacing:-.02em; }
  .sub { color:var(--text3); font-size:13px; line-height:1.5; }
  .card { background:var(--bg2); border:1px solid var(--line); border-radius:13px;
          padding:16px; margin:14px 0; }
  .card h2 { font-size:14px; margin:0 0 12px; letter-spacing:-.01em; }
  button { background:var(--bg3); color:var(--text); border:1px solid var(--line);
    border-radius:10px; padding:14px 16px; font-size:15px; font-weight:650;
    font-family:inherit; cursor:pointer; display:inline-flex; align-items:center;
    justify-content:center; gap:8px; min-height:50px; }
  button:active:not(:disabled) { transform:translateY(1px); }
  button.primary { background:var(--accent); border-color:var(--accent); color:#04121b; }
  button.wide { width:100%; }
  button:disabled { opacity:.4; cursor:default; }
  .row { display:flex; gap:9px; }
  .row > * { flex:1; }
  .note { border-left:3px solid var(--accent); background:var(--bg3);
    padding:11px 13px; border-radius:0 9px 9px 0; font-size:12.5px;
    line-height:1.55; margin-top:12px; color:var(--text2); }
  .note.warn { border-left-color:var(--warn); }
  .note.bad  { border-left-color:var(--bad); }
  .note strong { color:var(--text); }

  /* The two response buttons are the whole interface during a test, so they are
     deliberately enormous -- this gets used at arm's length with headphones on. */
  .choice { display:flex; gap:11px; margin-top:14px; }
  .choice button { flex:1; flex-direction:column; gap:3px; min-height:104px;
                   font-size:26px; font-family:var(--mono);
                   /* Same 104px target tapped every couple of seconds for ~70
                      trials. Without this, iOS Safari reads that as
                      double-tap-to-zoom and the buttons leave the screen
                      mid-test; a tap that drags a few px selects the label
                      instead and the trial hangs waiting for a click. */
                   touch-action:manipulation; -webkit-user-select:none;
                   user-select:none; -webkit-touch-callout:none;
                   transition:background .08s, border-color .08s; }
  /* Tap feedback. The native highlight is switched off globally, and a 1px
     nudge is invisible on a phone -- so an answer produced no acknowledgement
     at all, seventy times in a row. */
  .choice button:not(:disabled):active { background:var(--accent);
                                         border-color:var(--accent); color:#04121b; }
  .choice button.picked { background:var(--accent); border-color:var(--accent);
                          color:#04121b; }
  .choice button.picked small { color:#04121b; }
  .choice button small { font-size:11px; font-weight:600; color:var(--text3);
                         font-family:var(--sans); letter-spacing:.04em; }
  .choice button:disabled { opacity:.25; }

  /* Which interval is sounding right now. Without this the listener does not
     know when to attend, and inattention reads as inaudibility. */
  .beats { display:flex; gap:11px; margin-top:14px; }
  .beat { flex:1; height:8px; border-radius:99px; background:var(--bg3);
          border:1px solid var(--line); transition:background .1s; }
  .beat.on { background:var(--accent); border-color:var(--accent); }

  .prog { height:6px; border-radius:99px; background:var(--bg3); overflow:hidden;
          margin-top:14px; }
  .prog i { display:block; height:100%; width:0; background:var(--accent);
            transition:width .25s; }
  .big { font-family:var(--mono); font-size:38px; font-weight:650; letter-spacing:-.02em; }

  .rung { display:flex; align-items:center; gap:11px; padding:9px 12px;
          border:1px solid var(--line); border-radius:9px; margin-bottom:6px;
          font-size:14px; }
  .rung .hz { font-family:var(--mono); font-weight:650; min-width:60px; }
  .rung .score { font-family:var(--mono); color:var(--text3); font-size:12.5px; }
  .rung .verdict { margin-left:auto; font-size:12px; font-weight:650; }
  .rung.heard   { border-color:var(--bad); }
  .rung.heard   .verdict { color:var(--bad); }
  .rung.unclear { border-color:var(--warn); }
  .rung.unclear .verdict { color:var(--warn); }
  .rung.deaf    { border-color:var(--good); }
  .rung.deaf    .verdict { color:var(--good); }
  .hidden { display:none !important; }

  /* Segmented control for which ear is under test. */
  .seg { display:flex; gap:0; border:1px solid var(--line); border-radius:10px;
         overflow:hidden; }
  .seg button { flex:1; border:0; border-radius:0; background:var(--bg);
                min-height:46px; font-size:14px; color:var(--text2); }
  .seg button + button { border-left:1px solid var(--line); }
  .seg button[aria-pressed="true"] { background:var(--accent); color:#04121b; }

  /* One pip per band. A bar filling toward 100% would be a lie: the ladder
     stops as soon as two bands in a row are inaudible, so the old bar topped
     out around 60% and then jumped to the results, which reads as a crash. */
  .pips { display:flex; gap:5px; margin:2px 0 10px; }
  .pips i { flex:1; height:6px; border-radius:99px; background:var(--bg3);
            border:1px solid var(--line); }
  .pips i.done    { background:var(--text3); border-color:var(--text3); }
  .pips i.current { background:var(--accent); border-color:var(--accent); }

  /* Whose turn it is. The app must never look frozen while it is playing. */
  .turn { display:flex; align-items:center; gap:9px; font-size:14px;
          font-weight:650; min-height:22px; margin-top:12px; }
  .turn .dot { width:9px; height:9px; border-radius:99px; background:var(--text3);
               flex:none; }
  .turn.listening .dot { background:var(--accent); animation:pulse 1s infinite; }
  .turn.answering .dot { background:var(--good); }
  @keyframes pulse { 0%,100% { opacity:1 } 50% { opacity:.25 } }
  @media (prefers-reduced-motion: reduce) { .turn.listening .dot { animation:none } }

  details.more { margin-top:10px; }
  details.more summary { cursor:pointer; font-size:12.5px; color:var(--accent);
                         list-style:none; padding:4px 0; }
  details.more summary::-webkit-details-marker { display:none; }
  .hist { font-size:12.5px; color:var(--text2); }
  .hist div { display:flex; gap:10px; padding:6px 0;
              border-bottom:1px solid var(--line); }
  .hist b { font-family:var(--mono); color:var(--text); font-weight:650;
            margin-left:auto; }


/* generated from style="" attributes, which CSP blocks */
.x-0ee7f66a { margin-top:12px }
.x-301647f5 { margin-top:6px }
.x-397d5369 { margin-top:16px; display:block; font-size:13px; font-weight:650 }
.x-43a97fe4 { margin-top:8px }
.x-5d9ae7d5 { margin-top:4px; opacity:.7 }
.x-8740a80d { margin:0 0 12px }
.x-8e658c1e { text-align:center; margin-top:20px }
.x-92c18f6b { margin-top:16px }
.x-a0fc2e0b { margin-top:7px }
.x-ab79ea2b { margin:0 }
.x-c53ea1da { margin-top:14px }
.x-cc0bc1de { margin-top:10px; width:100% }
.x-d588061d { margin-top:10px; font-family:var(--mono); font-size:12px }
.x-df928149 { margin-top:10px }
.x-e9adfef7 { margin:8px 0 0 }
.x-f4561649 { margin-top:2px }
.x-f85c702c { margin-bottom:6px }
.x-fb56b9b4 { font-weight:650; color:var(--text2) }
.x-fe3c94c1 { margin-top:9px }