/* =====================================================================
   CHTR-1684 — the translator page, on the attendee app's chrome.

   Tokens, header bar, status dot, settings sheet, language picker, flags and meter are lifted from
   web/attendee/styles.css and the two operator surfaces, so the three read as one product. Only the
   live screen is new.

   THREE THINGS HERE ARE NOT WHAT THE DESIGN HANDOFF SHIPPED, and each is this page's browser floor
   talking. `web/translator` answers to Safari 14.1 / Chrome 90 — a volunteer on the phone they
   already own, handed a link by an organiser (tools/ci/browser-baseline.mjs, CHTR-756). That is
   lower than the console's floor and lower than the attendee page's, and the reference stylesheet
   was written above all three:

     * `color-mix()` is Safari 16.2. Every mix here is expressed instead as an alpha over a base —
       `rgb(var(--x-rgb) / .16)` as a background LAYER on top of a background COLOR. Same result, and
       it stays theme-correct without precomputing one flat hex per theme per mix.
     * `dvh` is Safari 15.4 and Chrome 108, and it genuinely matters here (a mobile browser's
       toolbars are exactly what the live screen has to survive). It is kept, with the same property
       declared in `vh` FIRST — an engine that cannot parse the second declaration keeps the first.
       That is the cascade doing its job and it is the rule the scanner encodes.
     * The `has` SELECTOR is Safari 15.4 and, being a selector, has no cascade rescue at all — an
       engine that cannot parse it drops the whole rule. Its three uses become classes `steps.js`
       assigns instead:
       `#app.diag-open`, `.setting-row.is-off`, `.segchoice label.on`.

   Fonts are the attendee page's, self-hosted: `font-src 'self'` means a Google Fonts @import is not
   slow, it is DEAD (CHTR-243). There is no JetBrains Mono in the repo and the handoff asks for no
   new binary assets, so the meter scale and the recent-language timestamps take the system mono.
   ===================================================================== */
@import url('../attendee/fonts/skin.css');

:root{color-scheme:dark;--bg:#1c1630;--surface:#221b3a;--surface-2:#272040;--surface-3:#2c2547;--band:#221b3a;--line:#3a3160;--line-soft:#2a2348;--control-border:#7d74a8;--fg:#f5f3fb;--muted:#b8b2c8;--faint:#a49cb8;--primary:#9384df;--primary-press:#6f61c4;--magenta:#d65a99;--green:#3fae86;--on-primary:#160f2b;--status-ok:var(--green);--status-warn:#e3a857;--status-error:#e0596b;--primary-weak:#2b2350;--magenta-weak:#3f2340;--green-weak:#1c3a3a;--green-ink:#7fdcb8;--magenta-ink:#f2a5cb;--primary-rgb:147 132 223;--magenta-rgb:214 90 153;--green-rgb:63 174 134;--error-rgb:224 89 107;--r-sm:8px;--r-md:10px;--r-lg:14px;--r-pill:999px;--tap:44px;--bar-core:60px;--safe-top:env(safe-area-inset-top,0px);--safe-bottom:env(safe-area-inset-bottom,0px);--font-family:"Hanken Grotesk",system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;--font-display:"Newsreader",Georgia,"Times New Roman",serif;--font-mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
@media (prefers-color-scheme:light){:root:not([data-theme="dark"]){color-scheme:light;--bg:#f5f3fb;--surface:#ffffff;--surface-2:#efecf8;--surface-3:#e6e2f4;--band:#ffffff;--line:#ddd7ef;--line-soft:#ebe7f7;--control-border:#827ba0;--fg:#1c1630;--muted:#5f5a74;--faint:#6a6480;--primary:#4f3a9c;--primary-press:#3a2a78;--magenta:#b02d6e;--green:#1c7a59;--on-primary:#ffffff;--status-warn:#8a5a13;--status-error:#c0394b;--primary-weak:#ece8f8;--magenta-weak:#f7e6ef;--green-weak:#e2f1ea;--green-ink:#0f4c37;--magenta-ink:#8a1c4f;--primary-rgb:79 58 156;--magenta-rgb:176 45 110;--green-rgb:28 122 89;--error-rgb:192 57 75}}
:root[data-theme="light"]{color-scheme:light;--bg:#f5f3fb;--surface:#ffffff;--surface-2:#efecf8;--surface-3:#e6e2f4;--band:#ffffff;--line:#ddd7ef;--line-soft:#ebe7f7;--control-border:#827ba0;--fg:#1c1630;--muted:#5f5a74;--faint:#6a6480;--primary:#4f3a9c;--primary-press:#3a2a78;--magenta:#b02d6e;--green:#1c7a59;--on-primary:#ffffff;--status-warn:#8a5a13;--status-error:#c0394b;--primary-weak:#ece8f8;--magenta-weak:#f7e6ef;--green-weak:#e2f1ea;--green-ink:#0f4c37;--magenta-ink:#8a1c4f;--primary-rgb:79 58 156;--magenta-rgb:176 45 110;--green-rgb:28 122 89;--error-rgb:192 57 75}
:root[data-theme="dark"]{color-scheme:dark;--bg:#1c1630;--surface:#221b3a;--surface-2:#272040;--surface-3:#2c2547;--band:#221b3a;--line:#3a3160;--line-soft:#2a2348;--control-border:#7d74a8;--fg:#f5f3fb;--muted:#b8b2c8;--faint:#a49cb8;--primary:#9384df;--primary-press:#6f61c4;--magenta:#d65a99;--green:#3fae86;--on-primary:#160f2b;--status-warn:#e3a857;--status-error:#e0596b;--primary-weak:#2b2350;--magenta-weak:#3f2340;--green-weak:#1c3a3a;--green-ink:#7fdcb8;--magenta-ink:#f2a5cb;--primary-rgb:147 132 223;--magenta-rgb:214 90 153;--green-rgb:63 174 134;--error-rgb:224 89 107}

*{box-sizing:border-box}
[hidden]{display:none !important}
html,body{margin:0;height:100%}
body{background:var(--bg);color:var(--fg);font-family:var(--font-family);font-size:17px;line-height:1.5;-webkit-text-size-adjust:100%;overscroll-behavior:none}
button,input,select,textarea{font:inherit}
:where(a,button,input,select,textarea,[tabindex]):focus-visible{outline:2px solid var(--primary);outline-offset:2px;border-radius:6px}
a{color:var(--primary)}a:hover{color:var(--fg)}
/* CHTR-1684: this rule did not exist, and `.sr-only` was already in the markup — so the "Language"
   label on the hidden select rendered as visible body text on the live page. */
.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}

#app{height:100vh;height:100dvh;min-height:0;overflow:hidden;display:flex;flex-direction:column;max-width:34rem;margin:0 auto;width:100%;background:var(--bg)}
.panel{flex:1;display:flex;flex-direction:column;gap:14px;min-height:0;overflow-y:auto;padding:14px 16px calc(16px + var(--safe-bottom))}

/* ── Header bar — same geometry as the attendee bar ─────────────────────── */
/* The height is FIXED and knowable in CSS, and the live screen's centring maths reads it back as
   `--bar-core`. Bar content must never size it: nested SVGs in the status dot did exactly that, and
   the symptom was a live screen centred on nothing in particular. */
.bar{flex:none;box-sizing:border-box;height:calc(var(--bar-core) + max(0px,var(--safe-top)));display:flex;align-items:center;gap:12px;padding:8px 12px;padding-top:max(10px,var(--safe-top));border-bottom:1px solid var(--line);background:var(--bg);overflow:hidden}
.bar .brand{display:flex;align-items:center;gap:9px;flex:1;min-width:0;margin:0}
/* Equaliser bars are CENTRED on the middle axis — never sitting on a common baseline. */
.bar .eq{display:inline-flex;align-items:center;gap:3px;flex:none}
.bar .eq i{width:4px;border-radius:2px;background:var(--primary);display:block}
.bar .eq i:nth-child(1){height:9px}
.bar .eq i:nth-child(2){height:17px}
.bar .eq i:nth-child(3){height:23px;background:var(--magenta)}
.bar .eq i:nth-child(4){height:13px;background:var(--green)}
.bar .eq i:nth-child(5){height:19px}
.bar .eq i:nth-child(6){height:10px;background:var(--magenta)}
.bar .eq i:nth-child(7){height:7px}
.brand-text{display:flex;flex-direction:column;justify-content:center;min-width:0;line-height:1.15}
.brand-word{font-weight:700;font-size:15px;letter-spacing:-.01em;white-space:nowrap}
.brand-role{color:var(--faint);font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.bar-actions{display:flex;align-items:center;gap:16px}
.bar-actions .iconbtn{min-width:24px;padding:0}
.iconbtn{flex:none;display:inline-flex;align-items:center;justify-content:center;min-width:32px;height:var(--tap);padding:0 5px;border:none;border-radius:var(--r-md);background:none;color:var(--muted);cursor:pointer;-webkit-tap-highlight-color:transparent}
.iconbtn:hover{color:var(--fg)}
.iconbtn:active{background:var(--surface-2);color:var(--fg)}
.iconbtn svg{width:22px;height:22px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round}

/* Word-free status dot, straight from the attendee bar. Colour is the traffic light and the SHAPE
   differs per state too, so the indicator survives red/green colour blindness. Drawn with a
   pseudo-element rather than nested SVGs so the glyph cannot affect the bar's height.

   The `is-` prefix is load-bearing: a bare `live` class here matches the `.live` PANEL selector and
   hands the dot the panel's grid and padding, which clips it out of the bar entirely. */
.status{flex:0 0 24px;align-self:center;display:flex;align-items:center;justify-content:center;width:24px;height:24px;color:var(--muted)}
.status::before{content:"";display:block;width:12px;height:12px;border-radius:50%;background:currentColor}
.status.is-live{color:var(--status-ok)}
.status.is-live::before{animation:status-pulse 1.6s ease-in-out infinite}
.status.is-wait{color:var(--status-warn)}
.status.is-wait::before{border-radius:2px;animation:status-spin .9s linear infinite}
.status.is-idle{color:var(--magenta)}
.status.is-idle::before{border-radius:2px}
@keyframes status-pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.45;transform:scale(.8)}}
@keyframes status-spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.status::before{animation:none !important;opacity:1}}

/* ── Type ───────────────────────────────────────────────────────────────── */
h1{font-family:var(--font-display);font-weight:500;font-size:28px;line-height:1.14;letter-spacing:-.01em;margin:0}
h2{font-family:var(--font-display);font-weight:500;font-size:19px;letter-spacing:-.01em;margin:0 0 6px}
p{margin:0 0 8px}
p:last-child{margin-bottom:0}
.lead{color:var(--muted)}
.note{color:var(--faint);font-size:14px}
/* Pink carries every warning and error state on this page. Red is reserved for the meter, where it
   means one thing only: clipping. */
.error{color:var(--magenta);font-weight:600}
.result{font-size:15px;color:var(--muted);margin-top:9px}
.result-quiet{color:var(--status-ok);font-weight:600}
.result-warm{color:var(--status-warn);font-weight:600}
.result-hot{color:var(--magenta);font-weight:700}
.lang{color:var(--primary);font-weight:600}
.section-head{padding:14px 6px 6px;font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--faint)}
.section-head:first-child{padding-top:6px}

.card{background:var(--surface);border:1px solid var(--line);border-radius:18px;padding:18px}
.btn{appearance:none;-webkit-appearance:none;width:100%;min-height:52px;padding:13px 16px;border:1px solid var(--control-border);border-radius:var(--r-pill);background:var(--surface-2);color:var(--fg);font-weight:600;font-size:16px;cursor:pointer}
.btn:hover{background:var(--surface-3)}
.btn:disabled{opacity:.55;cursor:default}
.btn-primary{background:var(--primary);border-color:var(--primary);color:var(--on-primary)}
.btn-primary:hover{background:var(--primary-press);border-color:var(--primary-press);color:#fff}
.btn-quiet{background:transparent;border-color:var(--line);color:var(--muted)}
.btn-quiet:hover{color:var(--fg);border-color:var(--control-border);background:transparent}

.field{display:block}
.field > span{display:block;font-size:14px;color:var(--muted);margin-bottom:6px}
.field input[type="text"],.field select,select.input{width:100%;min-height:52px;padding:11px 14px;border:1px solid var(--control-border);border-radius:14px;background:var(--surface-2);color:var(--fg)}
.field-inline{display:flex;align-items:center;gap:12px;min-height:var(--tap)}
.field-inline > span{margin:0;color:var(--fg);font-size:15px}
.field-inline input{width:24px;height:24px;accent-color:var(--primary)}

/* ── Meter — the DESKTOP operator's instrument: 18px bar, .55 ticks, a 68% gradient stop, the CLIP
   chip alongside and the dB scale underneath. One assembly, used for bleed, level and live. ── */
.meterblock{display:flex;flex-direction:column;margin-top:12px}
.meter-row{display:flex;align-items:stretch;gap:10px}
.meter{position:relative;flex:1;height:18px;border-radius:5px;background:var(--surface-2);border:1px solid var(--line);overflow:hidden}
.meter-fill{position:absolute;left:0;top:0;bottom:0;width:0;background:linear-gradient(90deg,var(--green),var(--green) 68%,var(--status-warn));transition:width 60ms linear}
.meter .peakzone{position:absolute;right:0;top:0;bottom:0;width:12%;background:rgb(var(--error-rgb) / .2)}
.meter .ticks{position:absolute;inset:0;background:repeating-linear-gradient(90deg,transparent 0 7px,var(--bg) 7px 9px);opacity:.55}
.meter .clip{position:absolute;left:88%;top:-1px;bottom:-1px;width:2px;background:var(--status-error)}
.clipchip{flex:none;display:inline-flex;align-items:center;gap:6px;font-size:10px;font-weight:700;letter-spacing:.05em;color:var(--faint);border:1px solid var(--line);border-radius:var(--r-sm);padding:0 9px}
.clipchip i{width:8px;height:8px;border-radius:50%;display:block;background-color:var(--surface-2);background-image:linear-gradient(rgb(var(--error-rgb) / .35),rgb(var(--error-rgb) / .35))}
.scalerow{display:flex;justify-content:space-between;margin-top:5px;font-family:var(--font-mono);font-size:10px;color:var(--faint)}
.scalerow span:last-child{color:var(--status-error)}
@media (prefers-reduced-motion:reduce){.meter-fill{transition:none}}

/* ── Language picker — the attendee picker ──────────────────────────────── */
#setup{padding:0;overflow:hidden}
.step{display:none;flex-direction:column;flex:1;min-height:0}
.step[data-active]{display:flex}
.step-body{display:flex;flex-direction:column;gap:14px;overflow-y:auto;flex:1;min-height:0;-webkit-overflow-scrolling:touch;padding:14px 16px 4px}
.step-foot{display:flex;flex-direction:column;gap:10px;flex:none;border-top:1px solid var(--line-soft);padding:12px 16px calc(14px + var(--safe-bottom))}
.progress{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--faint);font-weight:600;letter-spacing:.02em}
.progress b{color:var(--fg)}
.progress .dots{display:flex;gap:6px;margin-left:2px}
.progress .dots i{width:7px;height:7px;border-radius:50%;background:var(--line);display:block}
.progress .dots i.on{background:var(--primary)}

.picker-head{flex:none;display:flex;flex-direction:column;gap:10px;padding:14px 16px 10px}
.search{width:100%;min-height:52px;padding:12px 16px;border:1px solid var(--control-border);border-radius:var(--r-pill);background:var(--surface-2);color:var(--fg);-webkit-appearance:none;appearance:none}
.search::placeholder{color:var(--muted)}
.search:focus{outline:none;border-color:var(--primary);box-shadow:0 0 0 2px rgb(var(--primary-rgb) / .35)}
.picker-list{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;padding:0 12px max(12px,var(--safe-bottom))}
.lang-btn{display:flex;align-items:center;gap:12px;width:100%;min-height:var(--tap);margin:4px 0;padding:9px 14px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--band);color:var(--fg);cursor:pointer;text-align:start;-webkit-tap-highlight-color:transparent;transition:border-color 120ms ease,background 120ms ease}
.lang-btn:hover{border-color:var(--control-border)}
.lang-btn:active{border-color:var(--primary);background:var(--surface-3)}
.lang-btn .labels{display:flex;align-items:baseline;gap:8px;min-width:0;flex:1}
.lang-btn .native{font-size:17px;font-weight:600;white-space:nowrap}
.lang-btn .en{color:var(--muted);font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.lang-btn[aria-current="true"]{border-color:var(--primary);background-color:var(--band);background-image:linear-gradient(rgb(var(--primary-rgb) / .12),rgb(var(--primary-rgb) / .12))}
/* Flag chip — a reserved slot, so names align whether or not a flag exists. */
.flag{flex:none;display:block;width:31px;height:21px;border-radius:4px;overflow:hidden;box-shadow:inset 0 0 0 1px rgb(255 255 255 / .14)}
.flag svg{display:block;width:100%;height:100%}
.flag-empty{box-shadow:none;background:none}
.empty{padding:28px 12px;text-align:center;color:var(--muted)}
.recent-when{flex:none;color:var(--faint);font-size:12px;font-family:var(--font-mono)}
.recent .lang-btn{border-color:rgb(var(--primary-rgb) / .4)}

/* ── Live ───────────────────────────────────────────────────────────────── */
/* Three rows: everything above the meter, the meter card, everything below. `1fr auto 1fr` puts the
   card's CENTRE on the middle of the PANEL, and the extra bottom padding — one bar height — lifts
   that middle onto the middle of the SCREEN, since the panel starts below the header bar.
   `min-height:min-content` on the outer rows means a short viewport shrinks them rather than letting
   a control ride up over its neighbour; `overflow:hidden` is the backstop. */
/* CHTR-2282 — THIS GRID IS UNCHANGED, and two rounds of getting it wrong are why it is worth a note.
   The no-captions notice lives inside `.live-middle` (the `auto` row, with the meter) rather than as
   a fourth row of its own, because both other placements broke something:
     * inside `.live-top` it was invisible — that sits in a `1fr` track, a `1fr` track does not grow
       for its content, and `overflow:hidden` here meant the notice was laid out UNDER the meter card
       and painted over. It was in the DOM, both sentences in `innerText`, `toBeVisible()` green, and
       no reader could see a word of it.
     * widening this to `1fr auto auto 1fr` broke the ORDINARY screen, for every language that IS
       transcribed: `.meter-card` is pinned to `grid-row:2`, so the auto-placed controls moved out of
       the trailing `1fr` and the state card overflowed into the meter. A regression on the screen
       every interpreter sees, bought to fix one that only some of them do.
   In the `auto` row it costs nothing when hidden and takes its height from the two `1fr` tracks when
   shown, which is what an `auto` row is for. */
section.live{display:grid;grid-template-rows:1fr auto 1fr;justify-items:center;gap:0;text-align:center;overflow:hidden;padding-bottom:max(calc(16px + var(--safe-bottom)),calc(var(--bar-core) + max(0px,var(--safe-top)) + 14px))}
/* The notice above the meter, as one row. No `gap`: a gap is drawn between tracks whether or not the
   first one has anything in it, so it would move the ordinary screen 10px even with nothing to say.
   The spacing rides on the notice's own margin, which exists only while the notice does. */
.live-middle{grid-row:2;display:grid;justify-items:center;width:100%}
.live-lang{margin:0 0 4px;color:var(--muted);font-size:16px}
.live-top{display:grid;grid-template-rows:auto 1fr;place-items:center;min-height:min-content;width:100%}
/* CHTR-2282 — the standing note that this language gets no captions from the interpreter's own
   voice. Deliberately NOT `.error` styling: nothing has gone wrong, the carriage is working, and a
   magenta line saying so would tell an interpreter to stop — which is the one thing the second
   sentence exists to prevent. Quiet surface, muted ink, sized to the state card above it. */
.no-captions{width:min(340px,100%);margin:0 0 10px;padding:10px 12px;display:grid;gap:2px;
  background:var(--surface-2);border-radius:var(--r-md);color:var(--muted);font-size:13px;line-height:1.35}
.no-captions b{color:var(--fg);font-weight:600}
/* The state readout is a card with the meter card's chrome: a constant surface and border, with the
   state colour carried by an INSET fill, so the card's outline never changes. */
/* CHTR-2303 — THE CARD GIVES WAY TO THE ROOM ITS ROW ACTUALLY HAS: the `100%` in every height below.
   The card's height was a fixed `min(196px,30vh)` sitting in the `1fr` row of `.live-top`, and a
   `1fr` track takes its share of the panel whatever its content needs — so on a short screen the
   band kept the card at 196px and painted its bottom edge under `.live-middle`: 10.7px on the
   ordinary screen at 375x667, and 26.6px / 53.1px once the no-captions notice made the middle row
   taller. `min-height:min-content` on the band did not stop it, whatever the note above says.
   A percentage here resolves against the card's own grid area, so the card is its old height
   wherever that fits and the height of its row where it does not, down to the 64px floor. Every
   override of this height — diagnostics open, both short-viewport blocks — carries the same `100%`,
   or it re-specifies the defect. The grid's tracks are untouched: widening them is the trap
   `docs/gotchas/chtr-2282-tobevisible-is-green-on-an-occluded-element.md` records. */
.mic-state{width:min(340px,100%);height:min(196px,30vh,100%);height:min(196px,30dvh,100%);min-height:64px;padding:10px;display:flex;background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);flex:none}
#mic-label{flex:1;display:grid;place-items:center;align-content:center;gap:4px;padding:0 10px;border-radius:calc(var(--r-lg) - 4px);background:var(--surface-2);color:var(--muted);text-align:center}
/* CHTR-1704 — the word carries the card's type; the line under it is deliberately quieter and
   sized from its own clamp, so a long translation of "for the event to start" wraps inside the card
   rather than pushing the word off it. */
#mic-word{font-family:var(--font-display);font-weight:500;font-size:clamp(21px,6vh,29px);font-size:clamp(21px,6dvh,29px);letter-spacing:-.01em;line-height:1.1}
#mic-sub{font-size:clamp(12px,2.1vh,15px);font-size:clamp(12px,2.1dvh,15px);font-weight:400;line-height:1.25;color:var(--muted);max-width:26ch}
.mic-state[data-state="live"] #mic-label{color:var(--fg)}
/* `--green-ink` / `--magenta-ink` exist because the brand hues fail 4.5:1 against their own tint the
   moment the diagnostics view drops the word below the large-text threshold. Ink, never brand. */
.mic-state[data-state="speaking"] #mic-label{color:var(--green-ink);background-color:var(--surface-2);background-image:linear-gradient(rgb(var(--green-rgb) / .16),rgb(var(--green-rgb) / .16))}
.mic-state[data-state="muted"] #mic-label{color:var(--magenta-ink);background-color:var(--surface-2);background-image:linear-gradient(rgb(var(--magenta-rgb) / .16),rgb(var(--magenta-rgb) / .16))}
/* The meter is the INSTRUMENT and never absorbs the squeeze on a short viewport — the circle gives
   way instead. Without `flex:none` the 18px bar shrank to 2px in landscape. */
.meter-card{width:min(340px,100%);grid-row:2;background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);padding:14px 16px 12px;display:flex;flex-direction:column;gap:2px;flex:none;align-self:end}
.meter-card .sect{margin:0 0 8px;font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--faint);text-align:start}
.meterblock-live{width:100%;margin-top:0;flex:none}
.live-controls{display:grid;grid-template-rows:3fr auto 1fr;place-items:center;min-height:min-content;width:100%}
/* Round control — the mobile operator's capture button. No px floor above the tap target, so on a
   short viewport it really can give way to the meter. The radial lift is a white bloom over the flat
   brand colour rather than a mix of the two: same picture, and it reads at this page's floor. */
.btn-round{width:clamp(96px,26vh,198px);width:clamp(96px,26dvh,198px);height:clamp(96px,26vh,198px);height:clamp(96px,26dvh,198px);min-width:0;min-height:0;padding:0 14px;margin:0;border:none;border-radius:50%;display:grid;place-items:center;text-align:center;font-family:var(--font-family);font-size:clamp(15px,3.4vh,26px);font-size:clamp(15px,3.4dvh,26px);font-weight:800;letter-spacing:.01em;line-height:1.12;color:#fff;cursor:pointer;-webkit-tap-highlight-color:transparent;grid-row:2}
.btn-round:hover{color:#fff}
#talk{touch-action:none;user-select:none;-webkit-user-select:none;background-color:var(--primary);background-image:radial-gradient(circle at 50% 34%,rgb(255 255 255 / .1),rgb(255 255 255 / 0));box-shadow:0 0 0 12px var(--primary-weak),0 20px 40px -14px rgb(var(--primary-rgb) / .6)}
#talk.held{background-color:var(--green);background-image:radial-gradient(circle at 50% 34%,rgb(255 255 255 / .1),rgb(255 255 255 / 0));box-shadow:0 0 0 16px var(--green-weak),0 20px 40px -14px rgb(var(--green-rgb) / .6)}
#mute{background-color:var(--magenta);background-image:radial-gradient(circle at 50% 34%,rgb(255 255 255 / .1),rgb(255 255 255 / 0));box-shadow:0 0 0 12px var(--magenta-weak),0 20px 40px -14px rgb(var(--magenta-rgb) / .6)}
#mute[aria-pressed="true"]{background-color:var(--magenta);background-image:radial-gradient(circle at 50% 34%,rgb(0 0 0 / .3),rgb(0 0 0 / .38));box-shadow:0 0 0 16px rgb(var(--magenta-rgb) / .22),0 20px 40px -14px rgb(var(--magenta-rgb) / .6)}
/* Row 1, bottom-aligned, so an error appears ABOVE the meter without moving the meter. */
#live-error{grid-row:1;align-self:end;margin:0 0 12px}

/* ── Settings sheet — the attendee sheet ────────────────────────────────── */
.sheet{position:fixed;inset:0;z-index:40;display:flex;align-items:flex-end;justify-content:center;background:rgb(10 6 24 / .55)}
.sheet-card{width:min(460px,100%);background:var(--surface);border-top-left-radius:22px;border-top-right-radius:22px;padding:22px 20px calc(22px + var(--safe-bottom));animation:rise 200ms ease}
@media (prefers-reduced-motion:reduce){.sheet-card{animation:none}}
.sheet-card h2{margin:0 0 6px;font-size:20px}
.sheet-card > p{margin:0 0 18px;color:var(--muted)}
.setting-row{display:flex;align-items:center;justify-content:space-between;gap:16px;margin:0 0 16px;min-height:var(--tap)}
.setting-row .setting-text{display:flex;flex-direction:column;gap:2px;text-align:start;min-width:0}
.setting-row .setting-text b{font-weight:600;font-size:15px}
.setting-row .setting-text span{color:var(--faint);font-size:13px}
.setting-link{flex:none;min-height:var(--tap);padding:0 16px;border:1px solid var(--control-border);border-radius:var(--r-pill);background:transparent;color:var(--fg);font-weight:600;font-size:14px;cursor:pointer}
.setting-link:hover{background:var(--surface-2)}
.setting-link:disabled{opacity:.45;cursor:default;background:transparent}
/* `.is-off` is what a `has` selector on the disabled button would say. steps.js sets it wherever it
   sets the `disabled` property, so the two cannot come apart. */
.setting-row.is-off .setting-text{opacity:.6}
/* Either/or control choice — two radios styled as one segmented switch. */
.segchoice{display:flex;gap:0;flex:none;border:1px solid var(--control-border);border-radius:var(--r-pill);overflow:hidden}
.segchoice label{display:inline-flex;align-items:center;justify-content:center;min-height:var(--tap);padding:0 15px;font-size:14px;font-weight:600;color:var(--muted);cursor:pointer;background:transparent}
.segchoice label:not(:last-child){border-right:1px solid var(--line)}
.segchoice input{position:absolute;opacity:0;pointer-events:none}
.segchoice label.on{background:var(--primary);color:var(--on-primary)}
.segchoice input:checked + span{color:var(--on-primary)}
.segchoice input:focus-visible + span{outline:2px solid var(--primary);outline-offset:2px}
.sheet-close{width:100%;min-height:52px;margin-top:4px;border:none;border-radius:var(--r-pill);background:var(--primary);color:var(--on-primary);font-weight:700;cursor:pointer}
.sheet-close:hover{background:var(--primary-press);color:#fff}
@keyframes rise{from{transform:translateY(100%)}to{transform:translateY(0)}}
/* The mark appears on the save card too, so the bar's equaliser rules are shared by both. */
.save-title .eq{display:inline-flex;align-items:center;gap:3px;flex:none}
.save-title .eq i{width:4px;border-radius:2px;background:var(--primary);display:block}
.save-title .eq i:nth-child(1){height:9px}
.save-title .eq i:nth-child(2){height:17px}
.save-title .eq i:nth-child(3){height:23px;background:var(--magenta)}
.save-title .eq i:nth-child(4){height:13px;background:var(--green)}
.save-title .eq i:nth-child(5){height:19px}
.save-title .eq i:nth-child(6){height:10px;background:var(--magenta)}
.save-title .eq i:nth-child(7){height:7px}
.save-title{display:flex;align-items:center;gap:10px;padding:12px 14px;border:1px dashed var(--control-border);border-radius:var(--r-md);background:var(--surface-2);margin:0 0 16px;font-weight:600;font-size:15px}
.save-steps{margin:0 0 18px;padding-left:1.2em;color:var(--muted);font-size:15px;display:flex;flex-direction:column;gap:6px}
.save-steps b{color:var(--fg);font-weight:600}

/* ── Diagnostics — plain by intent, just legible ────────────────────────── */
.diag{flex:0 0 auto;max-height:45vh;max-height:45dvh;overflow-y:auto;border-top:1px solid var(--line);padding:12px 16px calc(14px + var(--safe-bottom));font-size:14px;gap:8px;text-align:start}
.diag h2{font-family:var(--font-family);font-size:13px;text-transform:uppercase;letter-spacing:.14em;color:var(--faint);margin:0}
.diag h3{font-size:13px;margin:6px 0 4px;color:var(--muted)}
.diag-grid{display:grid;grid-template-columns:auto 1fr;gap:4px 14px;margin:0}
.diag-grid dt{color:var(--muted)}
.diag-grid dd{margin:0;font-family:var(--font-mono);font-size:12.5px;word-break:break-word}
.diag-list{margin:0;padding-left:1.1em;font-family:var(--font-mono);font-size:12.5px;color:var(--muted)}

#bad-link,#waiting{justify-content:center;gap:14px;text-align:center}
/* Diagnostics steals a third of the screen, so the instrument and the control come down to 2/3.
   `.diag-open` is what a `has` selector on an unhidden `#diag` would say. */
#app.diag-open .mic-state{height:min(131px,14vh,100%);height:min(131px,14dvh,100%);min-height:0;padding:7px}
#app.diag-open #mic-word{font-size:clamp(19px,3.4vh,20px);font-size:clamp(19px,3.4dvh,20px)}
#app.diag-open #mic-sub{font-size:clamp(11px,1.6vh,13px);font-size:clamp(11px,1.6dvh,13px)}
#app.diag-open .btn-round{width:clamp(80px,14vh,132px);width:clamp(80px,14dvh,132px);height:clamp(80px,14vh,132px);height:clamp(80px,14dvh,132px);font-size:clamp(13px,2.1vh,18px);font-size:clamp(13px,2.1dvh,18px);box-shadow:none}
#app.diag-open .live-controls{grid-template-rows:1fr auto 1fr}
#app.diag-open .scalerow{display:none}
#app.diag-open .diag{max-height:34vh;max-height:34dvh}
#app.diag-open section.live{padding-top:8px;padding-bottom:calc(10px + var(--safe-bottom))}
#app.diag-open .meter-card{padding:9px 12px 8px}
#app.diag-open .live-lang{font-size:14px}

/* Short viewports — landscape, or a browser showing both toolbars. The instrument holds; the state
   card, the control and the trimmings come down until the three bands fit without a scroll. */
@media (max-height:620px){
section.live{padding-top:8px}
.live-lang{font-size:14px;margin-bottom:2px}
.mic-state{height:min(196px,20vh,100%);height:min(196px,20dvh,100%);padding:7px}
#mic-word{font-size:clamp(15px,4vh,29px);font-size:clamp(15px,4dvh,29px)}
#mic-sub{font-size:clamp(11px,1.7vh,14px);font-size:clamp(11px,1.7dvh,14px)}
.meter-card{padding:9px 12px 8px}
.meter-card .sect{margin-bottom:5px}
.scalerow{display:none}
.btn-round{width:clamp(88px,20vh,198px);width:clamp(88px,20dvh,198px);height:clamp(88px,20vh,198px);height:clamp(88px,20dvh,198px)}
}
@media (max-height:470px){
.mic-state{height:min(196px,17vh,100%);height:min(196px,17dvh,100%);padding:6px}
.meter-card .sect{display:none}
.btn-round{width:clamp(80px,17vh,198px);width:clamp(80px,17dvh,198px);height:clamp(80px,17vh,198px);height:clamp(80px,17dvh,198px);font-size:clamp(13px,2.6vh,26px);font-size:clamp(13px,2.6dvh,26px)}
/* Landscape plus the diagnostics panel is the tightest case on the page. */
#app.diag-open .diag{max-height:30vh;max-height:30dvh}
#app.diag-open .mic-state{height:min(131px,12vh,100%);height:min(131px,12dvh,100%)}
#app.diag-open .btn-round{width:clamp(64px,12vh,132px);width:clamp(64px,12dvh,132px);height:clamp(64px,12vh,132px);height:clamp(64px,12dvh,132px);font-size:12px}
#app.diag-open .live-lang{display:none}
}
