/* ═══════════════════════════════════════════════════════════
   Listen & Repeat — auditory memory trainer
   setup → exercise (Ready/Playing/Respond/Reveal/Rate/Tags) → summary
   English-only, LTR, mobile-first. Tokens come from base.css.
   ═══════════════════════════════════════════════════════════ */

/* ── Dark palette ──────────────────────────────────────────
   base.css owns the LIGHT tokens and this module inherits them untouched.

   Dark is a different story, and the reason is a platform bug this module
   must not trip over: base.css defines its dark palette under `.dark`, but
   app.html sets `data-theme="dark"` on <html> and nothing anywhere adds a
   `dark` class. So the base dark tokens never activate. Modules that look
   correct in dark mode (flashcard.css, chat.css, tracker.css) each redefine
   their own under [data-theme="dark"]; this follows that precedent.

   Values are copied from base.css:129 so the two stay visually identical.
   When the platform selector is eventually fixed to `.dark, [data-theme="dark"]`,
   this block becomes redundant but stays harmless — it sets the same values.

   Scoped to .lr so nothing here can affect the rest of the app. */
[data-theme="dark"] .lr,
.dark .lr {
  --bg:        #0f0e0c;
  --bg2:       #161410;
  --surface:   #1c1a16;
  --surface2:  #232018;
  --surface3:  #2a261e;
  --border:    rgba(255, 250, 240, 0.08);
  --border2:   rgba(255, 250, 240, 0.14);
  --text:      #f0ede6;
  --text2:     #9a9080;
  --text3:     #5a5248;
  --accent:    #6b8ef0;
  --accent2:   #8aaaf5;
  --accent-bg: rgba(107, 142, 240, 0.12);
  --accent-text: #a8c0f7;
  --shadow-sm: 0 1px 4px rgba(0,0,0,0.3);
  --shadow-md: 0 4px 20px rgba(0,0,0,0.4);
  --shadow-lg: 0 12px 48px rgba(0,0,0,0.55);
}

/* No background here — .lr is the FULL module wrapper (the whole width of
   .app-content-inner), not the visible panel. It must stay transparent and
   inherit whatever is actually behind it.
   Giving it an explicit `background: var(--bg)` was the bug reported after
   the last round: in light mode that fill is invisible (it happens to match
   the surrounding page, so nothing looked wrong), but in dark mode ONLY
   .lr picks up the scoped dark override below — the rest of the app's chrome
   never gets dark styling at all, because of the platform-wide bug this file
   already works around locally (see the comment on the dark palette block).
   So the fill became a real, full-width dark rectangle sitting on the
   still-light surrounding nav, with .lr-page's own panel nested inside THAT
   — a container inside a container, on a mismatched background. .lr-page
   already carries the one deliberate surface (--bg2 + border + shadow); .lr
   itself was never meant to be a surface at all. */
.lr { color: var(--text); font-family: var(--font-ui); }
.lr *, .lr *::before, .lr *::after { box-sizing: border-box; }
.lr .mono { font-family: var(--font-mono); }
.lr button { font-family: inherit; cursor: pointer; border: none; background: none; color: inherit; }
.lr button:disabled { cursor: not-allowed; opacity: .5; }
.lr button:focus-visible,
.lr [tabindex]:focus-visible,
.lr input:focus-visible,
.lr textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-xs); }
.lr svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.lr .icon { width: 18px; height: 18px; flex: none; display: inline-block; }
.lr .lr-screen { display: none; }
.lr .lr-screen.active { display: block; }
.lr .hidden { display: none !important; }
.lr .lr-sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0,0,0,0); white-space: nowrap; border: 0; }

/* ── shared layout ─────────────────────────────────────── */
/* The whole section now sits inside one visible panel — the previous pass
   fixed every individual card but left the SECTION ITSELF floating directly
   on the page with nothing framing it as a whole, which was the actual
   complaint. --bg2 is the fill: it reads as a real, if quiet, step down from
   --bg (Δ≈7-12 in light, enough combined with a full-perimeter border+shadow
   on a shape this large to register clearly — Gestalt continuity makes one
   long continuous edge far more noticeable than the same contrast on a small
   chip). Just as important, --bg2 is meaningfully darker than the --surface
   the inner cards use (Δ≈20-31 in light), so those cards still pop CLEARLY
   against the panel — using the same --surface for both layers would have
   flattened everything back out, recreating the exact bug from last time one
   level up.
   .lr-start-bar / .lr-dock's fade-to-solid gradients are matched to --bg2
   below, since they now sit on this panel, not the raw page. */
.lr-page { max-width: 640px; margin: 20px auto; padding: 24px 20px 130px;
  background: var(--bg2); border: 1px solid var(--border); border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm); }
@media (max-width: 480px) {
  .lr-page { margin: 10px auto; padding: 20px 14px 130px; border-radius: var(--r); }
}
.lr-wide { max-width: 1080px; }
.lr-eyebrow { font-family: var(--font-mono); font-size: .76rem; text-transform: uppercase; letter-spacing: .08em;
  color: var(--text3); margin-bottom: 6px; }
.lr-h { font-size: 1.15rem; font-weight: 700; margin-bottom: 4px; }
.lr-sub { color: var(--text2); font-size: .88rem; margin-bottom: 18px; }

/* ── buttons ───────────────────────────────────────────── */
/* Every primary/tappable surface below carries its shadow AT REST, not only on
   :hover — this is a mobile-first screen and :hover never fires on a
   touchscreen, so a hover-only shadow is invisible to most students. Hover
   still escalates the shadow for the minority on a mouse; :active gives every
   button a real press response, which nothing here had before. */
.lr-btn { font-size: .92rem; font-weight: 600; border-radius: 100px; padding: 13px 22px;
  display: inline-flex; align-items: center; justify-content: center; gap: 9px; transition: var(--t); min-height: 44px; }
.lr-btn .icon { width: 16px; height: 16px; }
.lr-btn--primary { background: var(--text); color: var(--bg); box-shadow: var(--shadow-sm); }
.lr-btn--primary:hover:not(:disabled) { transform: translateY(-1px); box-shadow: var(--shadow-md); }
.lr-btn--primary:active:not(:disabled) { transform: translateY(0) scale(.98); box-shadow: var(--shadow-sm); }
.lr-btn--ghost { background: transparent; color: var(--text2); border: 1.5px solid var(--border2); }
.lr-btn--ghost:hover:not(:disabled) { background: var(--surface3); color: var(--text); border-color: var(--text3); }
.lr-btn--ghost:active:not(:disabled) { transform: scale(.97); }
.lr-btn--full { width: 100%; }
@media (prefers-reduced-motion: reduce) {
  .lr-btn--primary:active:not(:disabled), .lr-btn--ghost:active:not(:disabled) { transform: none; }
}

/* ── setup: level grid ─────────────────────────────────── */
.lr-level-grid { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 22px; }
.lr-level-card { flex: 1 1 84px; min-width: 84px; border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 10px 13px; background: var(--surface); text-align: left; transition: var(--t); box-shadow: var(--shadow-sm); }
.lr-level-card:not(:disabled):hover { border-color: var(--text3); box-shadow: var(--shadow-md); }
.lr-level-card:not(:disabled):active { transform: scale(.98); }
.lr-level-card .n { font-weight: 700; font-size: .85rem; display: flex; align-items: center; gap: 5px; }
.lr-level-card .w { font-family: var(--font-mono); font-size: .76rem; color: var(--text3); margin-top: 2px; }
.lr-level-card.is-active { background: var(--accent-bg); border-color: var(--accent); box-shadow: var(--shadow-sm); }
.lr-level-card.is-active .n { color: var(--accent-text); }
/* Locked cards communicate unavailability through colour, not opacity — the
   generic `.lr button:disabled { opacity:.5 }` rule already dims them, and
   stacking a second opacity on top made the "Locked" label (added below,
   since a touch device never shows the old title="" tooltip) too faint to
   read. Higher specificity here wins over that generic rule. */
.lr-level-card.is-locked:disabled { opacity: 1; }
.lr-level-card.is-locked { background: var(--surface2); box-shadow: none; }
.lr-level-card.is-locked .n { color: var(--text3); }
.lr-level-card.is-locked .w { color: var(--text3); font-weight: 600; }
.lr-level-card.is-locked .icon { width: 11px; height: 11px; }

/* ── setup: pills (accent / gender) ────────────────────── */
.lr-pill-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.lr-pill { border: 1px solid var(--border); border-radius: 100px; padding: 8px 14px; font-size: .8rem;
  color: var(--text2); background: var(--surface); min-height: 36px; box-shadow: var(--shadow-sm); transition: var(--t); }
.lr-pill:hover { border-color: var(--text3); box-shadow: var(--shadow-md); }
.lr-pill:active { transform: scale(.97); }
.lr-pill.is-selected { background: var(--accent-bg); border-color: var(--accent); color: var(--accent-text); font-weight: 600; box-shadow: var(--shadow-sm); }

/* ── setup: question count ─────────────────────────────── */
.lr-count-row { display: flex; gap: 6px; margin-bottom: 22px; }
.lr-count-btn { flex: 1; border: 1px solid var(--border); border-radius: var(--r-sm); padding: 10px 4px;
  font-family: var(--font-mono); font-size: .82rem; color: var(--text2); background: var(--surface); text-align: center;
  box-shadow: var(--shadow-sm); transition: var(--t); }
.lr-count-btn:hover { border-color: var(--text3); box-shadow: var(--shadow-md); }
.lr-count-btn:active { transform: scale(.96); }
.lr-count-btn.is-selected { background: var(--text); color: var(--bg); border-color: var(--text); font-weight: 600; box-shadow: var(--shadow-sm); }

/* ── setup: speed control ──────────────────────────────── */
.lr-speed-row { display: flex; align-items: center; gap: 2px; background: var(--surface2); border: 1px solid var(--border);
  border-radius: 100px; padding: 4px; }
.lr-speed-step { flex: 1; text-align: center; padding: 8px 4px; border-radius: 100px; font-family: var(--font-mono);
  font-size: .78rem; color: var(--text3); min-height: 32px; }
.lr-speed-step.is-selected { background: var(--surface); color: var(--text); font-weight: 600; box-shadow: var(--shadow-sm); }
.lr-speed-caption { font-size: .78rem; color: var(--text3); margin-top: 10px; }

/* ── setup: practice / test mode ───────────────────────── */
.lr-mode-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 26px; }
@media (max-width: 480px) { .lr-mode-row { grid-template-columns: 1fr; } }
.lr-mode-card { position: relative; border: 1.5px solid var(--border2); border-radius: var(--r); padding: 18px; background: var(--surface);
  text-align: left; transition: var(--t); box-shadow: var(--shadow-sm); }
.lr-mode-card:hover { border-color: var(--text3); box-shadow: var(--shadow-md); }
.lr-mode-card:active { transform: scale(.99); }
.lr-mode-card.is-selected { border-color: var(--text); box-shadow: var(--shadow-md); }
/* A border-colour change alone was too subtle a signal between two cards that
   are otherwise visually identical — add an unambiguous checkmark. */
.lr-mode-card .check-badge { position: absolute; top: 12px; right: 12px; width: 20px; height: 20px; border-radius: 50%;
  background: var(--text); color: var(--bg); display: flex; align-items: center; justify-content: center;
  opacity: 0; transform: scale(.6); transition: var(--t); }
.lr-mode-card .check-badge .icon { width: 12px; height: 12px; }
.lr-mode-card.is-selected .check-badge { opacity: 1; transform: scale(1); }
.lr-mode-card h4 { font-size: .92rem; font-weight: 700; display: flex; align-items: center; gap: 8px; padding-right: 22px; }
.lr-mode-card .badge { font-family: var(--font-mono); font-size: .74rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: .04em; padding: 3px 8px; border-radius: 100px; background: var(--surface3); color: var(--text3); }
.lr-mode-card.test .badge { background: var(--warn-bg); color: var(--warn); }
.lr-mode-card p { font-size: .78rem; color: var(--text2); margin-top: 8px; line-height: 1.5; }
.lr-mode-card .finality { margin-top: 10px; font-size: .74rem; font-weight: 600; color: var(--warn);
  display: flex; gap: 6px; align-items: flex-start; }
.lr-mode-card .finality .icon { width: 13px; height: 13px; flex: none; margin-top: 1px; }

/* ── setup: start bar ───────────────────────────────────── */
.lr-start-bar { position: sticky; bottom: 0; padding: 14px 0 4px; background: linear-gradient(to top, var(--bg2) 60%, transparent); }

/* ── exercise: head ─────────────────────────────────────── */
/* flex-wrap, not nowrap: the right-hand text (level · accent · gender) can run
   long ("Controlled Expansion · Australian · Female"), and with the exit
   button now sharing this row there is not always space for both on a 360px
   phone. Wrapping the metadata onto its own second line is safe; clipping it
   or forcing horizontal scroll is not. */
.lr-ex-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  font-family: var(--font-mono); font-size: .78rem; color: var(--text2); padding: 4px 2px 20px; gap: 6px 10px; }
.lr-ex-left { display: flex; align-items: center; gap: 10px; }
.lr-ex-head .pos { font-weight: 600; color: var(--text); flex: none; }
.lr-ex-right { flex: 0 1 auto; }
/* Once a session starts there was no in-flow way out except finishing it or
   navigating to another top-level tab — which silently leaves the session
   row 'active' forever server-side, and gives the student no deliberate,
   confirmed way to say "I want to stop." Present in every stage (A-F, FAIL)
   since it lives outside .lr-stage-wrap. */
.lr-exit-btn { display: inline-flex; align-items: center; gap: 5px; padding: 5px 11px 5px 7px; border-radius: 100px;
  border: 1px solid var(--border); background: var(--surface); color: var(--text3); font-weight: 600;
  min-height: 30px; box-shadow: var(--shadow-sm); transition: var(--t); flex: none; }
.lr-exit-btn .icon { width: 13px; height: 13px; }
.lr-exit-btn:hover { color: var(--danger); border-color: var(--danger); box-shadow: var(--shadow-md); }
.lr-exit-btn:active { transform: scale(.95); }

/* ── exercise: stage container ─────────────────────────── */
.lr-stage-wrap { min-height: 46vh; display: flex; flex-direction: column; }
.lr-stage[hidden] { display: none; }
.lr-stage { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 22px;
  text-align: center; padding: 20px 0; }

/* State A — Ready */
.lr-listen-btn { width: 120px; height: 120px; border-radius: 50%; background: var(--text); color: var(--bg);
  display: flex; align-items: center; justify-content: center; box-shadow: var(--shadow-lg); transition: var(--t); }
.lr-listen-btn .icon { width: 36px; height: 36px; }
.lr-listen-btn:hover:not(:disabled) { transform: scale(1.03); }
/* This is the single most important control on the whole screen, and until
   now its only press feedback was :hover — which never fires on the phones
   this module is designed for. :active gives every tap a real response. */
.lr-listen-btn:active:not(:disabled) { transform: scale(.95); box-shadow: var(--shadow-md); }
.lr-listen-btn.is-settling { pointer-events: none; animation: lr-settle .35s ease; }
@keyframes lr-settle { from { transform: scale(.94); opacity: .6; } to { transform: scale(1); opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .lr-listen-btn:active:not(:disabled) { transform: none; } }

/* A drop shadow is a colour of DARKNESS cast onto something lighter — it has
   no way to register once the surface behind it is already close to black.
   --shadow-lg in dark mode is still rgba(0,0,0,.55): blended onto a page
   background of #0f0e0c, the numbers move but nothing a human eye can
   register changes. No amount of retuning that shadow fixes this; it needs a
   light-coloured cue instead. A thin cream ring (--border2, already the
   platform's dark-mode border colour) plus a soft glow in the accent colour
   gives this — the hero control of the whole exercise — real presence against
   a near-black page. rgba here is the literal --accent hex for dark mode
   (#6b8ef0 = 107,142,240); CSS has no way to read a custom property's channel
   values into rgba(), so if that token ever moves this must move with it. */
[data-theme="dark"] .lr-listen-btn,
.dark .lr-listen-btn {
  box-shadow: var(--shadow-lg), 0 0 0 1px var(--border2), 0 0 30px -2px rgba(107, 142, 240, .4);
}
[data-theme="dark"] .lr-listen-btn:active:not(:disabled),
.dark .lr-listen-btn:active:not(:disabled) {
  box-shadow: var(--shadow-md), 0 0 0 1px var(--border2), 0 0 20px -2px rgba(107, 142, 240, .32);
}
.lr-listen-caption { font-size: .84rem; color: var(--text2); }
.lr-listen-once { font-family: var(--font-mono); font-size: .78rem; color: var(--warn); margin-top: 2px; display: block; }

/* State B — Playing */
.lr-pulse { display: flex; gap: 10px; }
.lr-pulse i { width: 11px; height: 11px; border-radius: 50%; background: var(--accent); animation: lr-breathe 1.6s ease-in-out infinite; }
.lr-pulse i:nth-child(2) { animation-delay: .2s; }
.lr-pulse i:nth-child(3) { animation-delay: .4s; }
@keyframes lr-breathe { 0%, 100% { opacity: .25; transform: scale(.8); } 50% { opacity: 1; transform: scale(1.15); } }
@media (prefers-reduced-motion: reduce) { .lr-pulse i { animation: none; opacity: .7; } }

/* State C — Respond */
/* Bar heights are set from the live microphone signal in listen.js — there is
   deliberately NO keyframe animation here. A CSS-animated meter looks identical
   and would happily show a healthy signal from a muted or dead mic, which the
   student would only discover after recording a session of silence. The
   transition just smooths the real values. */
.lr-mic-meter { display: flex; align-items: flex-end; gap: 3px; height: 30px; }
.lr-mic-meter i { width: 5px; background: var(--accent); border-radius: 2px; opacity: .85;
  height: 4px; transition: height .08s linear; }
@media (prefers-reduced-motion: reduce) { .lr-mic-meter i { transition: none; } }
.lr-record-btn { display: inline-flex; align-items: center; gap: 8px; padding: 12px 20px; border-radius: 100px;
  background: var(--surface); border: 1px solid var(--border); font-size: .86rem; font-weight: 600; min-height: 44px;
  box-shadow: var(--shadow-sm); transition: var(--t); }
.lr-record-btn:hover:not(.is-recording) { border-color: var(--text3); box-shadow: var(--shadow-md); }
.lr-record-btn:active { transform: scale(.98); }
.lr-record-btn.is-recording { background: var(--danger-bg); border-color: var(--danger); color: var(--danger); }
.lr-record-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--danger); }
.lr-record-btn.is-recording .lr-record-dot { animation: lr-recpulse 1s ease-in-out infinite; }
@keyframes lr-recpulse { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }
@media (prefers-reduced-motion: reduce) { .lr-record-btn.is-recording .lr-record-dot { animation: none; } }
.lr-privacy { display: flex; align-items: center; gap: 7px; font-size: .76rem; color: var(--text3); }
.lr-privacy .icon { width: 13px; height: 13px; flex: none; }
.lr-mic-denied { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r); padding: 18px;
  max-width: 340px; box-shadow: var(--shadow-sm); }
.lr-mic-denied h4 { font-size: .88rem; font-weight: 700; margin-bottom: 6px; }
.lr-mic-denied p { font-size: .8rem; color: var(--text2); line-height: 1.5; }

/* A short instructional line at the top of a stage — added to Respond and
   Reveal, the two places students reported not knowing what to do next. */
.lr-stage-hint { font-size: .82rem; color: var(--text2); max-width: 36ch; line-height: 1.5; }

/* State D — Reveal */
.lr-transcript-wrap { text-align: left; width: 100%; }
.lr-transcript-tag { display: inline-block; font-family: var(--font-mono); font-size: .76rem; text-transform: uppercase;
  letter-spacing: .05em; color: var(--text3); margin-bottom: 10px; }
.lr-transcript { font-size: clamp(1.15rem, 4vw, 1.5rem); line-height: 1.65; color: var(--text); font-weight: 500; }

/* ── transcript check (optional speech-to-text) ─────────────
   Colour alone never carries the meaning: every difference also has a shape
   (strikethrough, underline, dotted) and the legend spells each one out. Around
   1 in 12 men has a red/green colour vision deficiency, and this is a panel
   made entirely of red and green markers.
   The whole block is deliberately quiet — no big score, no verdict. It is one
   more piece of evidence next to the student's own judgement, and the PRD is
   explicit that a machine transcript must never be presented as the truth of
   what they said (§40). */
.lr-stt { width: 100%; text-align: left; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: 16px 18px; box-shadow: var(--shadow-sm); }
.lr-stt-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  flex-wrap: wrap; margin-bottom: 10px; }
.lr-stt-tag { font-family: var(--font-mono); font-size: .74rem; text-transform: uppercase;
  letter-spacing: .05em; color: var(--text3); }
.lr-stt-pct { font-family: var(--font-mono); font-size: 1.05rem; font-weight: 700; color: var(--text); }
.lr-stt-body { font-size: 1.02rem; line-height: 2.1; color: var(--text); }
.lr-stt-w { padding: 1px 3px; border-radius: 3px; }

/* ONE RULE, and it is the point of this whole block: a strikethrough means
   "this is NOT part of the correct sentence". It is therefore never applied to
   a reference word. The first version struck the reference word on a
   substitution — so a panel telling a student the sentence said "eight" and the
   recogniser heard "age" crossed out *eight*, presenting the right answer as
   the mistake. Exactly backwards, and worse than showing nothing. */
.lr-stt-w.equal   { color: var(--text); }
/* Target word that did not come through. Marked, not struck: it IS correct. */
.lr-stt-w.expect  { background: var(--warn-bg); color: var(--warn); border-bottom: 2px solid var(--warn); }
/* Not heard at all. Ghosted rather than struck — the word is right, it just
   never arrived, and crossing it out would say the opposite. */
.lr-stt-w.del     { color: var(--text3); border-bottom: 2px dashed var(--text3); opacity: .8; }
/* Said (or misheard) but not in the sentence. These are the only two that earn
   a strikethrough. */
.lr-stt-w.wrong   { background: var(--danger-bg); color: var(--danger); text-decoration: line-through; }
.lr-stt-w.ins     { background: var(--danger-bg); color: var(--danger); text-decoration: line-through;
                    border-bottom: 2px dotted var(--danger); }
.lr-stt-w.moved   { background: var(--gold-bg); color: var(--gold); border-bottom: 2px dashed var(--gold); }
/* Keeps a substitution's two halves on one line so "eight age" can never wrap
   apart and read as two unrelated words. */
.lr-stt-pair { white-space: nowrap; }
.lr-stt-pair .lr-stt-w.wrong { margin-left: 3px; font-size: .92em; }
.lr-stt-legend { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 12px; padding-top: 10px;
  border-top: 1px solid var(--border); font-size: .76rem; color: var(--text3); }
.lr-stt-legend span { display: inline-flex; align-items: center; gap: 5px; }
.lr-stt-legend i { width: 11px; height: 11px; border-radius: 3px; display: inline-block; }
.lr-stt-note { font-size: .78rem; color: var(--text3); line-height: 1.5; margin-top: 10px; }
.lr-stt-raw { font-size: .84rem; color: var(--text2); margin-top: 8px; font-style: italic; }

/* State E — self-assessment (non-hierarchical) */
.lr-rating-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px; width: 100%; }
@media (max-width: 620px) { .lr-rating-grid { grid-template-columns: repeat(2, 1fr); } .lr-rating-grid > *:last-child { grid-column: 1 / -1; } }
.lr-rating-tile { background: var(--surface); border: 1.5px solid var(--border); border-radius: var(--r);
  padding: 18px 10px; display: flex; flex-direction: column; align-items: center; gap: 9px; transition: var(--t); min-height: 44px;
  box-shadow: var(--shadow-sm); }
.lr-rating-tile:hover, .lr-rating-tile:focus-visible { border-color: var(--text3); box-shadow: var(--shadow-md); transform: translateY(-2px); }
.lr-rating-tile:active { transform: translateY(0) scale(.97); }
/* JS already toggled this class on tap — it just had no visual effect before,
   so a rating press produced no feedback at all before the screen advanced. */
.lr-rating-tile.is-selected { border-color: var(--accent); background: var(--accent-bg); box-shadow: var(--shadow-md); }
.lr-rating-tile .icon { width: 20px; height: 20px; color: var(--text2); }
.lr-rating-tile span { font-size: .76rem; font-weight: 600; }

/* State F — error tags */
.lr-tag-grid { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; width: 100%; }
.lr-tag { border: 1px solid var(--border); border-radius: 100px; padding: 8px 14px; font-size: .78rem;
  color: var(--text2); background: var(--surface); min-height: 36px; box-shadow: var(--shadow-sm); transition: var(--t); }
.lr-tag:hover { border-color: var(--text3); box-shadow: var(--shadow-md); }
.lr-tag:active { transform: scale(.96); }
.lr-tag.is-selected { background: var(--accent-bg); border-color: var(--accent); color: var(--accent-text); box-shadow: var(--shadow-sm); }

/* ── exercise: bottom action dock (thumb reach) ────────── */
.lr-dock { position: sticky; bottom: 0; padding: 14px 0 6px; background: linear-gradient(to top, var(--bg2) 65%, transparent); }
.lr-dock .lr-btn { width: 100%; }

/* ── exercise: speed corner control ────────────────────── */
/* Was bare "–"/"+" text with no border, background or shadow — indistinguishable
   from static punctuation until a mouse happened to hover it. Now a small
   bordered pill holding two bordered circular buttons, so it reads as
   controls before anyone touches it. Disabled while the audio in a Test-Mode
   playback is actually sounding, so the one authorised play can't be
   re-paced partway through — see `.is-disabled` toggled from listen.js. */
.lr-speed-corner { display: flex; align-items: center; gap: 6px; font-family: var(--font-mono); font-size: .78rem;
  color: var(--text3); padding: 4px 6px; border-radius: 100px; background: var(--surface2);
  border: 1px solid var(--border); transition: var(--t); }
.lr-speed-corner.is-disabled { opacity: .45; pointer-events: none; }
.lr-speed-corner button { width: 22px; height: 22px; border-radius: 50%; display: inline-flex; align-items: center;
  justify-content: center; font-size: .85rem; line-height: 1; color: var(--text2); background: var(--surface);
  border: 1px solid var(--border2); transition: var(--t); }
.lr-speed-corner button:hover { background: var(--surface3); color: var(--text); border-color: var(--text3); }
.lr-speed-corner button:active { transform: scale(.92); }

/* ── keyboard hint (desktop only) ──────────────────────── */
/* Named specifically as the smallest text in the whole section — .7rem was
   under 10.5px at this site's 15px root. Raised well past every other small
   label in this file (all sit at .72-.78rem now), not just up to meet them. */
.lr-kbd-hint { display: none; }
@media (min-width: 760px) and (hover: hover) {
  .lr-kbd-hint { display: flex; gap: 16px; justify-content: center; margin-top: 24px; font-size: .84rem; color: var(--text3); }
  .lr-kbd-hint kbd { font-family: var(--font-mono); background: var(--surface2); border: 1px solid var(--border2);
    border-radius: 4px; padding: 2px 7px; }
}

/* ── summary ─────────────────────────────────────────────── */
.lr-summary-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-bottom: 24px; }
@media (max-width: 480px) { .lr-summary-stats { grid-template-columns: repeat(2, 1fr); } }
.lr-summary-stat { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm); padding: 14px 10px;
  text-align: center; box-shadow: var(--shadow-sm); }
.lr-summary-stat .v { font-family: var(--font-mono); font-size: 1.3rem; font-weight: 600; }
.lr-summary-stat .l { font-size: .76rem; color: var(--text3); margin-top: 2px; }
.lr-dist-row { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.lr-dist-label { width: 108px; flex: none; font-size: .78rem; color: var(--text2); }
.lr-dist-bar { flex: 1; height: 10px; border-radius: 5px; background: var(--surface3); overflow: hidden; }
.lr-dist-bar > i { display: block; height: 100%; background: var(--text3); border-radius: 5px; }
.lr-dist-n { font-family: var(--font-mono); font-size: .78rem; color: var(--text2); width: 24px; text-align: right; }
.lr-missed-list { list-style: none; margin-top: 10px; }
.lr-missed-list li { padding: 10px 0; border-top: 1px solid var(--border); font-size: .86rem; color: var(--text); }

/* ── dashboard ──────────────────────────────────────────── */
.lr-dash-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-bottom: 26px; }
@media (max-width: 640px) { .lr-dash-row { grid-template-columns: repeat(2, 1fr); } }
.lr-dash-stat { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm); padding: 14px 16px;
  box-shadow: var(--shadow-sm); }
.lr-dash-stat .v { font-family: var(--font-mono); font-size: 1.4rem; font-weight: 600; }
.lr-dash-stat .l { font-size: .78rem; color: var(--text3); margin-top: 2px; }

.lr-chart-panel { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
  padding: 24px 22px; box-shadow: var(--shadow-sm); margin-bottom: 22px; }
.lr-chart-panel .chead { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 18px; flex-wrap: wrap; gap: 6px; }
.lr-chart-panel h4 { font-size: .92rem; font-weight: 700; }
.lr-chart-panel .sub { font-family: var(--font-mono); font-size: .78rem; color: var(--text3); }
.lr-bars { display: flex; align-items: flex-end; gap: 14px; height: 160px; padding: 0 4px; }
.lr-bar-col { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 8px; height: 100%; justify-content: flex-end; }
.lr-bar-col .bar { width: 100%; max-width: 46px; background: var(--accent); border-radius: 6px 6px 3px 3px; }
.lr-bar-col.is-drop .bar { background: var(--warn); }
.lr-bar-col .pct { font-family: var(--font-mono); font-size: .78rem; font-weight: 600; }
.lr-bar-col.is-drop .pct { color: var(--warn); }
.lr-bar-col .lbl { font-family: var(--font-mono); font-size: .74rem; color: var(--text3); }

.lr-table { width: 100%; border-collapse: collapse; font-size: .82rem; }
.lr-table th { text-align: left; font-size: .76rem; text-transform: uppercase; letter-spacing: .04em; color: var(--text3);
  padding: 8px 10px; border-bottom: 1px solid var(--border2); font-weight: 600; }
.lr-table td { padding: 10px 10px; border-bottom: 1px solid var(--border); }
.lr-table td.mono, .lr-table th.mono { font-family: var(--font-mono); }
.lr-table tr.is-locked td { color: var(--text3); }
.lr-table--sortable th { cursor: pointer; user-select: none; }
.lr-table--sortable th:hover { color: var(--text); }

.lr-empty { text-align: center; padding: 50px 20px; color: var(--text2); }
.lr-empty .icon { width: 34px; height: 34px; color: var(--text3); margin: 0 auto 12px; }
.lr-empty h4 { font-size: .92rem; font-weight: 700; color: var(--text); margin-bottom: 4px; }
.lr-empty p { font-size: .82rem; }

/* ── admin ──────────────────────────────────────────────── */
.lr-toolbar { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: 16px; }
.lr-input { font: inherit; font-size: .84rem; padding: 9px 12px; border: 1px solid var(--border2); border-radius: var(--r-sm);
  background: var(--surface); color: var(--text); min-height: 40px; }
.lr-select { font: inherit; font-size: .84rem; padding: 9px 12px; border: 1px solid var(--border2); border-radius: var(--r-sm);
  background: var(--surface); color: var(--text); min-height: 40px; }
.lr-readiness { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  background: var(--gold-bg); border: 1px solid var(--gold); border-radius: var(--r); padding: 14px 18px; margin-bottom: 20px; }
.lr-readiness .txt { font-size: .86rem; }
.lr-readiness .txt b { color: var(--text); }
.lr-readiness .hint { display: block; font-size: .78rem; color: var(--text3); margin-top: 2px; }
.lr-level-access-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 0;
  border-bottom: 1px solid var(--border); font-size: .86rem; }
.lr-toggle { position: relative; width: 40px; height: 22px; border-radius: 100px; background: var(--surface3); flex: none; }
.lr-toggle > i { position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%; background: var(--surface);
  box-shadow: var(--shadow-sm); transition: var(--t); }
.lr-toggle.is-on { background: var(--accent); }
.lr-toggle.is-on > i { transform: translateX(18px); }

.lr-import-report { border: 1px solid var(--border); border-radius: var(--r); overflow: hidden; margin: 16px 0; }
.lr-import-row { display: flex; align-items: center; gap: 10px; padding: 9px 14px; font-size: .8rem; border-bottom: 1px solid var(--border); }
.lr-import-row:last-child { border-bottom: none; }
.lr-import-status { font-family: var(--font-mono); font-size: .74rem; font-weight: 600; text-transform: uppercase;
  padding: 2px 8px; border-radius: 100px; flex: none; }
.lr-import-status.ok { background: var(--green-bg); color: var(--green); }
.lr-import-status.warn { background: var(--danger-bg); color: var(--danger); }

/* ── edge states ────────────────────────────────────────── */
.lr-offline-banner { display: flex; align-items: center; gap: 8px; background: var(--warn-bg); color: var(--warn);
  font-size: .82rem; font-weight: 600; padding: 10px 16px; border-radius: var(--r-sm); margin-bottom: 16px; }
.lr-offline-banner .icon { width: 15px; height: 15px; }
/* This card previously had no border and no shadow — a real error state was
   100% invisible against the page, differentiated from nothing only by its
   text. Elevated like `.lr-interrupt-card` below, since both are the same
   kind of thing: an interrupt the student did not ask for. */
.lr-audio-fail { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r); padding: 22px;
  max-width: 340px; box-shadow: var(--shadow-lg); }
.lr-audio-fail h4 { font-size: .92rem; font-weight: 700; margin-bottom: 6px; display: flex; align-items: center; gap: 8px; }
.lr-audio-fail h4 .icon { width: 18px; height: 18px; color: var(--warn); flex: none; }
.lr-audio-fail p { font-size: .82rem; color: var(--text2); line-height: 1.5; margin-bottom: 14px; }
.lr-interrupt-overlay { position: fixed; inset: 0; background: rgba(26,22,18,.5); display: flex; align-items: center;
  justify-content: center; z-index: 500; padding: 20px; }
[data-theme="dark"] .lr-interrupt-overlay,
.dark .lr-interrupt-overlay { background: rgba(0,0,0,.65); }

/* ── admin: settings panel ──────────────────────────────── */
.lr-setting-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
  padding: 14px 0; border-bottom: 1px solid var(--border); }
.lr-setting-row:last-child { border-bottom: none; }
.lr-setting-row .lbl { font-size: .86rem; font-weight: 600; }
.lr-setting-row .hint { display: block; font-size: .74rem; color: var(--text3); margin-top: 3px;
  font-weight: 400; max-width: 46ch; line-height: 1.45; }
.lr-setting-row .ctl { flex: none; display: flex; align-items: center; gap: 8px; }
.lr-seg { display: inline-flex; border: 1px solid var(--border2); border-radius: 100px; padding: 3px; background: var(--surface2); }
.lr-seg button { padding: 6px 14px; border-radius: 100px; font-size: .78rem; color: var(--text2); min-height: 32px; }
.lr-seg button.is-on { background: var(--surface); color: var(--text); font-weight: 600; box-shadow: var(--shadow-sm); }
.lr-stepper { display: inline-flex; align-items: center; gap: 2px; border: 1px solid var(--border2);
  border-radius: 100px; background: var(--surface2); padding: 3px; }
.lr-stepper button { width: 30px; height: 30px; border-radius: 50%; font-size: 1rem; color: var(--text2);
  border: 1px solid var(--border2); background: var(--surface); transition: var(--t); }
.lr-stepper button:hover:not(:disabled) { background: var(--surface3); color: var(--text); border-color: var(--text3); }
.lr-stepper button:active:not(:disabled) { transform: scale(.92); }
.lr-stepper .v { font-family: var(--font-mono); font-size: .82rem; min-width: 26px; text-align: center; }
.lr-note { background: var(--warn-bg); border-left: 3px solid var(--warn); border-radius: var(--r-xs);
  padding: 10px 14px; font-size: .78rem; color: var(--text2); line-height: 1.5; margin-top: 10px; }
.lr-note b { color: var(--text); }
.lr-interrupt-card { background: var(--surface); border-radius: var(--r-lg); padding: 26px; max-width: 340px; text-align: center;
  box-shadow: var(--shadow-lg); }
.lr-interrupt-card h4 { font-size: 1rem; font-weight: 700; margin-bottom: 6px; }
.lr-interrupt-card p { font-size: .84rem; color: var(--text2); margin-bottom: 18px; }

/* ── desktop: wider panel, larger type ─────────────────────
   .lr-page was a flat 640px at every width — on a real desktop monitor that
   reads as a narrow column stranded in the middle of a much wider page. Not
   the same problem as the panel's fill/border from the previous round; this
   is plain sizing. Widened here rather than in the mobile-first base rule,
   and a second step for genuinely wide monitors.

   Sized phone-first on purpose otherwise, but the site's root is 15px, so
   .78rem is under 12px — genuinely small at desktop viewing distance on a
   screen with plenty of room to spare. ~10-15% up across the text a student
   actually reads, at the same breakpoint the keyboard hints already use.

   .lr-h serves two different roles and needs splitting, not one blanket
   value: full screen titles ("Set up your session") carry no inline style,
   while section sub-labels ("Level", "Rating distribution") are all given an
   inline font-size by the JS templates. A single !important number would
   have flattened the intended size difference between the two — titles
   should end up noticeably bigger than sub-labels, not equal to them.
   :not([style]) reaches only the titles through the normal cascade; [style]
   reaches the sub-labels and needs !important, since nothing but an
   !important stylesheet rule can beat an inline style regardless of
   selector specificity. */
@media (min-width: 760px) {
  /* :not(.lr-wide) matters: .lr-page and .lr-wide (the dashboard) have equal
     selector specificity and this rule comes later in the file, so a plain
     `.lr-page { max-width }` here would silently win over .lr-wide's 1080px
     and shrink the dashboard back down on desktop. Padding is fine to share
     — only the width needs to dodge that clash. */
  .lr-page { padding: 32px 36px 130px; }
  .lr-page:not(.lr-wide) { max-width: 820px; }
  .lr-h:not([style]) { font-size: 1.45rem; }
  .lr-h[style]        { font-size: 1.05rem !important; }
  .lr-sub { font-size: 1rem; }
  .lr-eyebrow { font-size: .82rem; }
  .lr-btn { font-size: 1rem; padding: 14px 24px; }
  .lr-transcript { font-size: clamp(1.3rem, 2.4vw, 1.7rem); }
  .lr-stage-hint { font-size: .92rem; }
  .lr-listen-caption { font-size: .94rem; }
  .lr-listen-once { font-size: .82rem; }
  .lr-mode-card h4 { font-size: 1.02rem; }
  .lr-mode-card p { font-size: .88rem; }
  .lr-mode-card .badge { font-size: .78rem; }
  .lr-level-card .n { font-size: .94rem; }
  .lr-level-card .w { font-size: .82rem; }
  .lr-pill { font-size: .88rem; }
  .lr-tag { font-size: .86rem; }
  .lr-count-btn { font-size: .9rem; }
  .lr-speed-step { font-size: .82rem; }
  .lr-speed-caption { font-size: .84rem; }
  .lr-speed-corner { font-size: .82rem; }
  .lr-rating-tile span { font-size: .84rem; }
  .lr-record-btn { font-size: .94rem; }
  .lr-mic-denied h4, .lr-audio-fail h4 { font-size: 1rem; }
  .lr-mic-denied p, .lr-audio-fail p { font-size: .9rem; }
  .lr-transcript-tag { font-size: .78rem; }
  .lr-summary-stat .v, .lr-dash-stat .v { font-size: 1.5rem; }
  .lr-summary-stat .l, .lr-dash-stat .l { font-size: .82rem; }
  .lr-dist-label, .lr-dist-n { font-size: .84rem; }
  .lr-table { font-size: .9rem; }
  .lr-table th { font-size: .8rem; }
  .lr-chart-panel h4 { font-size: 1rem; }
  .lr-chart-panel .sub { font-size: .8rem; }
  .lr-bar-col .lbl { font-size: .8rem; }
  .lr-ex-head { font-size: .86rem; }
  .lr-exit-btn { font-size: .82rem; }
}
@media (min-width: 1300px) {
  .lr-page:not(.lr-wide) { max-width: 900px; }
  .lr-wide { max-width: 1200px; }
}
