/* KC Dispatch Trainer — themed CAD terminal, calm and encouraging.
   WO-014: EVERY color lives in the two theme blocks below. No color literal
   (hex, rgb(), rgba(), hsl(), or a named color other than transparent/
   currentColor/inherit) may appear anywhere else in this file. A regex scan in
   dev/probe.js enforces this. Non-color tokens (radius, fonts, type scale) live
   in the plain :root block, which carries no color. */

/* Dark is the default identity: plain :root (before JS sets data-theme) resolves
   to the dark set, so there is no light-flash on first paint. */
:root, :root[data-theme="dark"] {
  /* surfaces */
  --bg: #0b1220;
  --bg-2: #0e1526;
  --panel: #121c2e;
  --panel-2: #16233a;
  --line: #24344f;
  --line-hover: #33507a;
  --bg-glow: #13233c;
  --track: #0c1524;
  --track-tick: rgba(8, 14, 26, .55);
  --slip-bg: #07101d;
  --input-focus-bg: #0c1626;
  --cal-done-bg: #2f4a3f;
  --cal-done-border: #3a5a4c;
  /* text */
  --text: #e6edf6;
  --muted: #8aa0bd;
  --text-bright: #eafff4;
  --text-blue: #cfe2ff;
  --text-amber: #ffe1b0;
  --text-on-accent: #052012;
  --text-faint: #52607a;
  --text-placeholder: #46557079;
  /* accent + status hues */
  --accent: #38e08b;
  --accent-dim: #1f7d52;
  --btn-primary-a: #2fd07f;
  --btn-primary-b: #24b46c;
  --btn-primary-border: #1c9159;
  --amber: #ffb347;
  --soft-red: #ff7a85;
  --blue: #5aa9ff;
  /* alphas (kept as tokens per theme, so nothing derives colors outside here) */
  --accent-glow: rgba(56, 224, 139, .12);
  --accent-ring: rgba(56, 224, 139, .22);
  --accent-soft: rgba(56, 224, 139, .10);
  --accent-border-soft: rgba(56, 224, 139, .35);
  --blue-ring: rgba(90, 169, 255, .55);
  --scrim: rgba(4, 9, 18, .82);
  --shadow: rgba(0, 0, 0, .5);
  /* WO-037: the classify-prompt ALERT state (amber, deliberately unique from the green
     focus accent). A warm dark banner, a bright amber edge, and two glow stops for the pulse. */
  --alert-bg: #2a2012;
  --alert-border: #e0a53a;
  --alert-glow-soft: rgba(255, 179, 71, .20);
  --alert-glow-strong: rgba(255, 179, 71, .55);
}
:root[data-theme="light"] {
  /* surfaces: dark-gray ink on paper-white, muted >= 4.5:1 on its own background */
  --bg: #eef1f6;
  --bg-2: #e7ecf3;
  --panel: #ffffff;
  --panel-2: #eef3f9;
  --line: #d2d9e6;
  --line-hover: #a9b7cd;
  --bg-glow: #e4eaf4;
  --track: #dbe1ec;
  --track-tick: rgba(70, 90, 120, .40);
  --slip-bg: #f6f8fc;
  --input-focus-bg: #ffffff;
  --cal-done-bg: #b6e2cb;
  --cal-done-border: #7fc09c;
  /* text */
  --text: #1a2432;
  --muted: #4e5c74;
  --text-bright: #0b1420;
  --text-blue: #1f4f8c;
  --text-amber: #7c4a00;
  --text-on-accent: #052012;
  --text-faint: #7d8aa0;
  --text-placeholder: #98a4b6;
  /* accent as TEXT is darkened for >= 4.5:1 on the light surfaces; the Start button is a
     BRIGHT green so the near-black button text keeps strong contrast (a light CTA). */
  --accent: #06753a;
  --accent-dim: #2ba76d;
  --btn-primary-a: #2ec878;
  --btn-primary-b: #1eb267;
  --btn-primary-border: #12904f;
  --amber: #9a5a0a;
  --soft-red: #bd2f3c;
  --blue: #245fbf;
  /* alphas */
  --accent-glow: rgba(10, 125, 67, .12);
  --accent-ring: rgba(10, 125, 67, .22);
  --accent-soft: rgba(10, 125, 67, .10);
  --accent-border-soft: rgba(10, 125, 67, .42);
  --blue-ring: rgba(36, 95, 191, .48);
  --scrim: rgba(22, 30, 46, .42);
  --shadow: rgba(35, 50, 80, .22);
  /* WO-037: the alert state on paper-white: a pale amber banner, a darker amber edge, and
     two glow stops for the pulse. Dark ink on the pale amber stays well past 4.5:1. */
  --alert-bg: #fdefd6;
  --alert-border: #c9820f;
  --alert-glow-soft: rgba(201, 130, 15, .26);
  --alert-glow-strong: rgba(201, 130, 15, .60);
}
/* Non-color tokens (no literal colors here). */
:root {
  --radius: 14px;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
  /* WO-014 B / WO-030: type scale — small-caps labels / secondary / body+inputs+slip /
     idle+section title / hero. One shared sans scale so the standby text (title,
     subheader, status, plan) reads as one font family and size ladder. */
  --fs-label: 13px;
  --fs-secondary: 15px;
  --fs-body: 18px;
  --fs-title: 21px;
  --fs-hero: 2.1rem;
  /* WO-024 (Focus run): the slip became a compact incident card (~14px values,
     ~11.5px labels) and the entry inputs stay at the base body size, so WO-023's
     one-step-up run token is retired. The run-active sizes live inline in the run
     block below as literal px (jsdom can read them; colours stay tokenised). */
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: radial-gradient(1200px 700px at 70% -10%, var(--bg-glow) 0%, var(--bg) 55%) fixed;
  color: var(--text);
  font-family: var(--sans);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
.wrap { max-width: 940px; margin: 0 auto; padding: 22px 18px 40px; }

/* Header — WO-025: slim console status bar. A dashed underline, mono type, muted
   text: identity, not decoration. The three zones (brand / on-shift / clock+rank+
   toggle) sit on one line and collapse gracefully at narrow widths. */
.app-header {
  display: flex; justify-content: space-between; align-items: center;
  gap: 12px 18px; flex-wrap: wrap; margin-bottom: 18px;
}
.console-bar {
  font-family: var(--mono); border-bottom: 1px dashed var(--line);
  padding-bottom: 10px; margin-bottom: 16px;
}
.cb-left { display: flex; align-items: center; gap: 10px; }
.cb-brand { font-size: .78rem; margin: 0; font-weight: 600; letter-spacing: .08em; color: var(--muted); }
.cb-brand-name { color: var(--text); }
.cb-brand-sub { color: var(--muted); }
.cb-mid { display: flex; align-items: center; gap: 12px; }
.cb-mid .cb-shift {
  font-size: .72rem; letter-spacing: .14em; color: var(--accent); font-weight: 600;
}
/* WO-030: signed-in account chip (nickname "Blue"), quiet accent-tinted pill. */
.cb-acct {
  font-size: .68rem; letter-spacing: .12em; color: var(--text);
  background: var(--accent-soft); border: 1px solid var(--accent-border-soft);
  border-radius: 5px; padding: 2px 8px;
}
.cb-right { display: flex; align-items: center; gap: 14px; }
.cb-clock { font-size: .74rem; letter-spacing: .06em; color: var(--muted); font-variant-numeric: tabular-nums; }
/* WO-006: static dot (pulse removed), calm and non-distracting. */
.dot {
  width: 10px; height: 10px; border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-glow);
}

/* Channel selector — WO-025 / WO-027 A: the mode tabs are a bordered segmented control
   that reads unmistakably as clickable buttons (not label text). A quiet CHANNEL caption
   sits above the segmented row; each segment carries its hotkey chip; the active channel
   is marked with brighter text, a low-saturation accent tint, and an accent underbar. */
.modes.channels {
  display: block; max-width: 720px; margin: 0 auto 18px;
}
.chan-label {
  display: block; font-family: var(--mono); font-size: .62rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--muted); margin: 0 0 6px 3px;
}
.channel-seg {
  display: flex; border: 1px solid var(--line); border-radius: 9px; overflow: hidden;
  background: var(--bg-2);
}
.channels .mode-tab {
  position: relative; flex: 1; cursor: pointer; font-family: var(--sans); font-size: .8rem;
  background: transparent; color: var(--muted); border: 0; border-right: 1px solid var(--line);
  border-radius: 0; padding: 9px 6px; display: inline-flex; align-items: center;
  justify-content: center; gap: 7px; transition: color .12s ease, background .12s ease;
}
.channels .mode-tab:last-child { border-right: 0; }
.channels .mode-tab:hover { color: var(--text); background: var(--panel-2); }
.channels .mode-tab.is-active { color: var(--text-bright); background: var(--accent-soft); }
.channels .mode-tab.is-active::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: var(--accent);
}
.channels .mode-tab .key {
  display: inline-grid; place-items: center; width: 16px; height: 16px;
  font-size: .64rem; border-radius: 4px; background: var(--track); border: 1px solid var(--line); color: var(--muted);
}
.channels .mode-tab.is-active .key { color: var(--accent); border-color: var(--accent-dim); }
.channels .mode-tab:focus-visible { outline: 3px solid var(--blue); outline-offset: -3px; border-radius: 4px; }

/* Stage / panels */
.stage { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 20px; min-height: 320px; }
.view { animation: fade .18s ease; }
@keyframes fade { from { opacity: 0; transform: translateY(4px); } }
.muted { color: var(--muted); }
h2 { margin: 0 0 6px; font-size: 1.2rem; }
h3 { margin: 0 0 12px; font-size: 1rem; }

/* Idle */
.field-row { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 9px 0; border-bottom: 1px dashed var(--line); }
.field-row:last-of-type { border-bottom: none; }
.field-row span { font-size: .92rem; }
.switch-row input { width: 20px; height: 20px; accent-color: var(--accent); }
select {
  font-family: var(--sans); background: var(--panel); color: var(--text);
  border: 1px solid var(--line); border-radius: 8px; padding: 7px 10px; font-size: .9rem;
}
.mini { font-size: .76rem; margin: 12px 0 0; }

/* Buttons */
.btn { cursor: pointer; font-family: var(--sans); border-radius: 10px; border: 1px solid var(--line);
  background: var(--panel-2); color: var(--text); padding: 10px 16px; font-size: .95rem; transition: all .12s ease; }
.btn:hover { border-color: var(--line-hover); }
.btn:focus-visible { outline: 3px solid var(--blue); outline-offset: 2px; }
.btn-primary { background: linear-gradient(180deg, var(--btn-primary-a), var(--btn-primary-b)); border-color: var(--btn-primary-border); color: var(--text-on-accent); font-weight: 700; }
.btn-primary:hover { filter: brightness(1.06); }
.btn-lg { width: 100%; padding: 13px; margin-top: 14px; font-size: 1.02rem; }
.btn-sm { padding: 6px 11px; font-size: .82rem; }
.btn-ghost { background: transparent; }
.btn .hint { font-weight: 400; opacity: .7; font-size: .78em; }
/* WO-026 A: the hint inside a filled primary button uses the button's own dark
   text color (not --muted, which is a light gray-blue with poor contrast on the
   green fill); a modest opacity dims it while staying AA-legible in both themes. */
.btn-primary .hint { color: var(--text-on-accent); opacity: .82; }

/* WO-008: secondary test-conditions button sits close under Start, tier-3 neutral. */
#testBtn { margin-top: 8px; }
.test-intro { margin-top: 6px; }
/* WO-011: overspeed "push pace" burst — tier-3 (neutral) button + expectation line. */
#pushBtn { margin-top: 8px; }
.push-intro { margin-top: 6px; }

/* Active run */
.active-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.progress { font-size: .95rem; color: var(--muted); }
.progress strong { color: var(--text); }
.live { display: flex; gap: 16px; font-size: .9rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.live strong { color: var(--accent); }

/* WO-021 A: the call slip is the data she must transcribe, so it must stay in view
   through a tall (8-field) run. Sticky pins it to the top of the viewport while the
   fields scroll beneath; the opaque slip background covers anything passing under it. */
.callslip { background: var(--slip-bg); border: 1px solid var(--line); border-radius: 12px; overflow: hidden; margin-bottom: 16px;
  position: sticky; top: 8px; z-index: 3; box-shadow: 0 8px 24px var(--shadow); }
/* WO-006: neutral slip header; the focused field is the one accent on this screen. */
.callslip-head { background: var(--bg-2); color: var(--muted); font-family: var(--mono);
  font-size: .76rem; letter-spacing: 1px; padding: 8px 14px; border-bottom: 1px solid var(--line); }
.callslip-body { padding: 14px 16px; font-family: var(--mono); font-size: var(--fs-body); display: grid; gap: 6px; }
.slip-line { display: grid; grid-template-columns: 130px 1fr; gap: 10px; align-items: baseline; }
.slip-label { color: var(--muted); font-size: var(--fs-label); text-transform: uppercase; letter-spacing: .5px; }
.slip-value { color: var(--text-bright); font-weight: 600; word-break: break-word; }
.slip-value.blank { color: var(--text-faint); font-weight: 400; font-style: italic; }
.slip-solo { font-size: 1.3rem; color: var(--text-bright); font-weight: 600; }

.fields { display: grid; gap: 12px; }
.f { display: grid; grid-template-columns: 130px 1fr; gap: 12px; align-items: center; }
.f label { color: var(--muted); font-size: var(--fs-label); text-transform: uppercase; letter-spacing: .5px; }
.f input {
  font-family: var(--mono); font-size: var(--fs-body); color: var(--text);
  background: var(--bg-2); border: 1px solid var(--line); border-radius: 9px; padding: 11px 13px; width: 100%;
}
.f input::placeholder { color: var(--text-placeholder); }
.f input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-ring); background: var(--input-focus-bg); }
.entry-foot { margin-top: 14px; text-align: center; }
.hint { color: var(--muted); font-size: .82rem; }

kbd {
  font-family: var(--mono); font-size: .78em; background: var(--track); border: 1px solid var(--line);
  border-bottom-width: 2px; border-radius: 5px; padding: 1px 6px; color: var(--text-blue);
}

/* Results */
.result-headline { font-size: 1.3rem; font-weight: 700; margin-bottom: 4px; }
.result-headline .sub { display: block; font-size: .9rem; font-weight: 400; color: var(--muted); margin-top: 2px; }
/* WO-012: quality-graded verdict. Routine stays calm (muted); a strong run brightens
   to full-strength text; a milestone (PB/promotion/first 80%) takes the accent and the
   ONE brief animation. The accent + motion here is the deliberate, rare celebration. */
.result-headline.verdict-0 { color: var(--muted); }
.result-headline.verdict-1 { color: var(--text); }
.result-headline.verdict-2 { color: var(--accent); animation: promoIn .35s ease; }
/* WO-006: neutral card, colored only by a 3px left border (green pass / amber near). */
.pass-line { border-radius: 12px; padding: 11px 15px; font-weight: 600; font-size: .93rem; margin-bottom: 10px;
  background: var(--bg-2); border: 1px solid var(--line); border-left-width: 3px; color: var(--text); }
.pass-line .big { font-size: 1.05rem; font-variant-numeric: tabular-nums; }
.pass-line.pass { border-left-color: var(--accent); }
.pass-line.pass .big { color: var(--accent); }
.pass-line.near { border-left-color: var(--amber); }
.pass-line.near .big { color: var(--amber); }
.result-metrics { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin: 16px 0; }
.metric { background: var(--bg-2); border: 1px solid var(--line); border-radius: 12px; padding: 14px 10px; text-align: center; }
/* WO-006: hero container stays neutral; the accuracy VALUE is the one accent. */
.metric-hero { border-color: var(--line); background: var(--bg-2); }
.m-num { display: block; font-size: 1.7rem; font-weight: 800; font-variant-numeric: tabular-nums; }
/* WO-012: the accuracy hero is the reveal — one size step larger than the others. */
.metric-hero .m-num { color: var(--accent); font-size: var(--fs-hero); }
.m-lbl { display: block; font-size: var(--fs-label); color: var(--muted); text-transform: uppercase; letter-spacing: .6px; margin-top: 2px; }
.m-sub { display: block; text-transform: none; letter-spacing: .2px; font-size: .66rem; color: var(--muted); margin-top: 1px; }
.gross-line { margin: 2px 0 8px; }

.tips { display: grid; gap: 8px; margin-bottom: 16px; }
/* WO-006: neutral tip card, colored only by a left border. */
.tip { background: var(--bg-2); border: 1px solid var(--line); border-left: 3px solid var(--blue); color: var(--text);
  padding: 11px 14px; border-radius: 11px; font-size: .9rem; }
.tip.good { border-left-color: var(--accent); }

.breakdown { background: var(--bg-2); border: 1px solid var(--line); border-radius: 12px; padding: 6px 14px 14px; margin-bottom: 16px; }
.breakdown summary { cursor: pointer; padding: 10px 2px; color: var(--muted); font-size: .9rem; }
.bd-card { border-top: 1px solid var(--line); padding: 10px 0; }
.bd-card:first-child { border-top: none; }
.bd-card-title { font-size: var(--fs-label); color: var(--muted); text-transform: uppercase; letter-spacing: .5px; margin-bottom: 6px; }
.bd-row { display: grid; grid-template-columns: 120px 1fr auto; gap: 10px; align-items: baseline; padding: 3px 0; font-family: var(--mono); font-size: .9rem; }
.bd-label { color: var(--muted); font-family: var(--sans); font-size: .82rem; }
.bd-cmp .want, .bd-cmp .lbl { color: var(--muted); }
.bd-cmp .got-ok { color: var(--accent); }
.bd-cmp .got-part { color: var(--amber); }
.bd-cmp .got-miss { color: var(--soft-red); }
/* WO-032: character-level diff marks. Only the differing spans are highlighted (colored
   + underlined by the field's accuracy tier), so a one-letter typo no longer paints the
   whole field; the equal text stays the normal breakdown color. Amber for a near miss,
   soft red for a low-accuracy field (the WO-006 breakdown palette, applied per span). */
.bd-cmp .diff { font-weight: 700; text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 2px; }
.bd-cmp .diff.part { color: var(--amber); }
.bd-cmp .diff.miss { color: var(--soft-red); }
.bd-pct { font-variant-numeric: tabular-nums; font-size: .82rem; }
.bd-pct.ok { color: var(--accent); }
.bd-pct.part { color: var(--amber); }
.bd-pct.miss { color: var(--soft-red); }

.result-actions { display: flex; gap: 10px; }
.result-actions .btn-lg { width: auto; flex: 1; margin-top: 0; }
/* WO-006: on results the accuracy hero value is the one full accent, so "Go again"
   is an accent OUTLINE (not a filled fill) — clearly primary, but not a second fill. */
.result-actions .btn-primary { background: transparent; border-color: var(--accent); color: var(--accent); }
.result-actions .btn-primary:hover { background: var(--accent-soft); border-color: var(--accent); filter: none; }

/* History */
/* WO-014: the sparkline stroke is currentColor, so it themes with this token. */
#sparkline { margin: 6px 0 12px; color: var(--accent); }

/* P2: practice-adherence calendar (primary consistency display) */
.calendar-wrap { margin: 4px 0 14px; }
.cal-head { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; margin-bottom: 8px; }
.cal-title { font-size: var(--fs-label); color: var(--muted); text-transform: uppercase; letter-spacing: .5px; }
.cal-head .mini { margin: 0; }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 5px; max-width: 320px; }
.cal-wd { text-align: center; font-size: .62rem; color: var(--muted); }
.cal-cell {
  aspect-ratio: 1 / 1; border-radius: 5px; background: var(--track); border: 1px solid var(--line);
}
/* WO-006: practiced cell = muted green-gray (low saturation), today = ring. */
.cal-cell.done { background: var(--cal-done-bg); border-color: var(--cal-done-border); }
.cal-cell.future { opacity: .35; }
.cal-cell.today { box-shadow: 0 0 0 2px var(--blue-ring); }
.history-list { display: grid; gap: 6px; }
.h-row { display: grid; grid-template-columns: 1fr auto auto auto; gap: 12px; font-size: .84rem; color: var(--muted);
  padding: 6px 10px; background: var(--bg-2); border: 1px solid var(--line); border-radius: 8px; }
.h-row strong { color: var(--text); font-variant-numeric: tabular-nums; }

/* Footer */
.app-footer { margin-top: 18px; display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  color: var(--muted); font-size: .8rem; }

@media (max-width: 720px) {
  .result-metrics { grid-template-columns: repeat(3, 1fr); }
  .f, .slip-line { grid-template-columns: 100px 1fr; }
  .bd-row { grid-template-columns: 90px 1fr auto; }
}

/* ================= Game layer: ranks, XP, badges, streaks ================= */
/* WO-006: neutral chip. */
.rank-chip {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 6px;
  background: var(--bg-2); border: 1px solid var(--line); color: var(--text-blue);
  border-radius: 999px; padding: 3px 12px; font-size: .78rem; font-weight: 600; letter-spacing: .3px;
}

.live-item.combo strong { color: var(--amber); }

.game-banner {
  background: var(--bg-2); border: 1px solid var(--line); border-radius: 12px;
  padding: 12px 16px; margin-bottom: 14px; display: grid; gap: 9px;
}
@keyframes promoIn { from { opacity: 0; transform: scale(.97); } }
.game-banner .xp-line { font-size: .9rem; color: var(--muted); }
.game-banner .xp-gain { color: var(--accent); font-weight: 700; }
.xp-bar { height: 7px; border-radius: 999px; background: var(--track); border: 1px solid var(--line); overflow: hidden; }
.xp-bar span { display: block; height: 100%; background: linear-gradient(90deg, var(--accent-dim), var(--accent)); border-radius: 999px; transition: width .6s ease; }

.badge-name { font-weight: 700; color: var(--accent); white-space: nowrap; }
.badge-desc { font-size: .84rem; color: var(--muted); }

/* In-run affordances: numbered fields + a clear "you are here" row */
.f-num {
  display: inline-grid; place-items: center; width: 17px; height: 17px; margin-right: 7px;
  font-size: .68rem; border-radius: 5px; background: var(--track); border: 1px solid var(--line);
  color: var(--muted); font-variant-numeric: tabular-nums;
}
.f:focus-within label { color: var(--text); }
.f:focus-within .f-num { color: var(--text-on-accent); background: var(--accent); border-color: var(--accent); font-weight: 700; }

/* P6 peak-tier line */
.peak-line { font-size: .84rem; color: var(--muted); margin-bottom: 12px; font-style: italic; }

/* P1 practice plan + P5 milestone */
/* WO-006: neutral info card. */
.plan-line {
  margin: 12px 0 0; font-size: var(--fs-secondary); color: var(--text);
  background: var(--bg-2); border: 1px solid var(--line);
  border-radius: 10px; padding: 8px 12px;
}

.game-banner .milestone-line { font-size: .9rem; color: var(--text-amber); }

.plan-close { margin: 14px 0 0; text-align: center; font-size: .84rem; color: var(--muted); max-width: 75ch; }

/* WO-004: intra-day cadence (primary idle status) + retest countdown */
.today-tally { display: flex; align-items: center; justify-content: center; gap: 10px; margin: 0; flex-wrap: wrap; }
.tally-label { font-size: var(--fs-label); color: var(--muted); text-transform: uppercase; letter-spacing: .5px; }
.tally-slots { display: inline-flex; gap: 6px; }
.tally-slot { width: 20px; height: 8px; border-radius: 999px; background: var(--track); border: 1px solid var(--line); }
.tally-slot.filled { background: var(--accent-dim); border-color: var(--accent-dim); }
.tally-count { margin: 0; }
.retest-line { margin: 12px 0 0; font-size: var(--fs-secondary); color: var(--muted); }
.xp-spaced { color: var(--accent); font-weight: 600; }
/* WO-011: the "push" XP tag is a plain label, not a bonus — muted, never accent. */
.xp-push { color: var(--muted); font-weight: 600; }
.push-line { margin-bottom: 8px; }
.streak-line-progress { margin: 4px 0 10px; }

/* WO-005: progressive-disclosure idle layout (single column, collapsibles) */
.idle-main { display: grid; gap: 14px; max-width: 640px; }
.linkish {
  background: none; border: none; padding: 0; font: inherit; color: var(--blue);
  cursor: pointer; text-decoration: underline;
}
.linkish:hover { filter: brightness(1.1); }
.linkish:focus-visible { outline: 3px solid var(--blue); outline-offset: 2px; border-radius: 3px; }
.disclosure { background: var(--bg-2); border: 1px solid var(--line); border-radius: 12px; padding: 2px 14px; }
.disclosure > summary { cursor: pointer; padding: 11px 2px; color: var(--text); font-size: .95rem; font-weight: 600; list-style: none; }
.disclosure > summary::-webkit-details-marker { display: none; }
.disclosure > summary::before { content: '▸ '; color: var(--muted); }
.disclosure[open] > summary::before { content: '▾ '; }
.disclosure > summary:focus-visible { outline: 3px solid var(--blue); outline-offset: 2px; border-radius: 6px; }
.disclosure-body { padding: 2px 0 12px; }
/* WO-030: Settings callout = a 3px accent left-edge chip (not a full ring, which would
   read as selected). The padding-left compensates the border so the summary text does
   not shift. Progress stays plain. */
.disclosure.callout { border-left: 3px solid var(--accent); padding-left: 11px; }
.history-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; font-size: 1rem; }
.unlock-line { margin: 2px 0 8px; font-size: .86rem; color: var(--text-blue); }
.result-top { margin-bottom: 12px; }
.result-top .result-headline { margin-bottom: 8px; }

/* WO-005: intro overlay reuse via a close button ("Show keys") */
.intro-close {
  position: absolute; top: 12px; right: 12px; background: transparent; border: 1px solid var(--line);
  color: var(--muted); border-radius: 8px; padding: 4px 10px; font-size: .8rem; cursor: pointer;
}
.intro-close:hover { border-color: var(--line-hover); color: var(--text); }
.intro-close:focus-visible { outline: 3px solid var(--blue); outline-offset: 2px; }
.intro-card { position: relative; }

.intro-plan { display: grid; gap: 6px; margin: 4px 0 16px; font-size: .9rem; }
.intro-plan select { justify-self: start; }

/* First-run intro overlay */
.intro-overlay {
  position: fixed; inset: 0; z-index: 50; display: grid; place-items: center;
  background: var(--scrim); backdrop-filter: blur(3px); padding: 18px;
}
.intro-card {
  max-width: 560px; width: 100%; background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 26px 28px; box-shadow: 0 18px 60px var(--shadow);
  animation: fade .25s ease;
}
.intro-card h2 { font-size: 1.3rem; margin-bottom: 10px; }
.intro-lead { color: var(--muted); font-size: .95rem; max-width: 75ch; }
.intro-steps { list-style: none; margin: 16px 0; padding: 0; display: grid; gap: 12px; }
.intro-steps li {
  display: grid; grid-template-columns: 130px 1fr; gap: 14px; align-items: baseline;
  background: var(--bg-2); border: 1px solid var(--line); border-radius: 10px; padding: 11px 14px; font-size: .92rem;
}
.intro-key { white-space: nowrap; }
.intro-steps strong { color: var(--text); }
@media (max-width: 560px) { .intro-steps li { grid-template-columns: 1fr; gap: 4px; } }

/* ============================ WO-007 pass ============================ */
/* B: Settings as a compact two-column grid (label max-content, control right). */
#settingsDetails { justify-self: start; width: fit-content; max-width: 100%; }
.settings-grid {
  display: grid; grid-template-columns: max-content max-content;
  gap: 9px 20px; align-items: center; width: max-content; max-width: 100%;
}
.settings-grid .field-row { display: contents; }
.settings-grid .field-row > span { font-size: .92rem; padding: 0; }
.settings-grid .field-row > select,
.settings-grid .field-row > input { justify-self: start; }
.settings-grid > .mini { grid-column: 1 / -1; margin: 6px 0 0; }

/* WO-010: best-today data callout. */
.best-today { margin: 0; }
.best-today.callout {
  display: inline-flex; flex-direction: column; align-items: flex-end; text-align: right;
  border: 1px solid var(--accent-border-soft); border-radius: 8px; padding: 4px 10px;
  margin-left: auto;
}
.best-today.callout .bt-label { font-size: .6rem; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); }
.best-today.callout .bt-val { font-size: var(--fs-secondary); font-weight: 600; color: var(--accent); font-variant-numeric: tabular-nums; }

/* A: banner extras are LINES, not nested boxes. */
.game-banner { gap: 7px; margin-bottom: 10px; }
.game-banner .promo-line { font-size: 1rem; }
.game-banner .promo-name { color: var(--amber); font-weight: 700; }
.game-banner .badge-line { font-size: .9rem; color: var(--muted); }
.game-banner .badge-line .badge-name { color: var(--accent); font-weight: 700; }
.game-banner .badge-line .badge-desc { color: var(--muted); }
.game-banner .today-best-line { font-size: .9rem; color: var(--text-blue); }

/* D: rank progress is the visual object on results. */
.rank-progress { display: grid; gap: 3px; }
.rank-ends { display: flex; justify-content: space-between; gap: 10px; font-size: .74rem; color: var(--muted); }
.rank-ends .rank-cur { color: var(--text); font-weight: 600; }
.rank-bar {
  position: relative; height: 9px; border-radius: 999px;
  background: var(--track); border: 1px solid var(--line); overflow: hidden;
}
.rank-fill {
  display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, var(--accent-dim), var(--accent)); transition: width .6s ease;
}
/* 10 subtle segment ticks so filling one is a visible near-term event. */
.rank-bar::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background-image: repeating-linear-gradient(to right,
    transparent 0 calc(10% - 1px), var(--track-tick) calc(10% - 1px) 10%);
}
.rank-remain { font-size: .8rem; color: var(--muted); }

/* Idle mini rank bar under the chip: same fill, no labels, 4px tall. */
.rank-mini {
  height: 4px; border-radius: 999px; background: var(--track); border: 1px solid var(--line);
  overflow: hidden; margin-top: 6px; max-width: 220px;
}
.rank-mini-fill {
  display: block; height: 100%;
  background: linear-gradient(90deg, var(--accent-dim), var(--accent));
}

/* A: trim leftover vertical padding so a typical results view fits one viewport. */
.result-metrics { margin: 12px 0; }
.tips { margin-bottom: 10px; }
.breakdown { margin-bottom: 12px; }
.peak-line { margin-bottom: 8px; }

/* WO-023 (Phase 1, Focus): during an active run the call is the whole surface.
   The chrome (header, tabs, footer) is hidden ENTIRELY, not dimmed (this replaces
   the WO-012 15% dim), and the work area centers in a comfortable reading column
   with the slip and fields enlarged one type step. display:none is inherently
   instant, so the reduced-motion state still happens (state, not decoration).
   Idle and results are unchanged. */
body.run-active .app-header,
body.run-active .modes,
body.run-active .app-footer { display: none; }
/* The stage sheds its card chrome and fills the viewport so the work area can
   center vertically; min-height (not height) lets a tall 8-field run grow and
   scroll from the top instead of clipping, and keeps the sticky slip working. */
body.run-active .stage {
  border: none; background: transparent; padding: 0;
  min-height: calc(100vh - 96px);
  display: flex; flex-direction: column; justify-content: center;
}
body.run-active #activeView { width: 100%; max-width: 620px; margin: 0 auto; }
/* WO-024: the slip is a compact incident card, not the enlarged WO-023 block.
   A thin accent left border (squared on that side) marks it as the incident card
   (redesign mockup direction A); values dial to ~14px, labels ~11.5px, rows are
   dense with a tight line-height and minimal vertical padding, so all 8 fields sit
   in a small block instead of eating half the viewport. The entry inputs drop back
   to the comfortable base body size (18px), not the WO-023 21px; numbered chips and
   the focused-field accent are untouched. Literal px here (not var) so the smoke
   check can read the computed compact size; colours stay tokenised (WO-014). */
body.run-active .callslip { border-left: 3px solid var(--accent); border-radius: 0 12px 12px 0; }
body.run-active .callslip-body { font-size: 14px; line-height: 1.5; padding: 10px 14px; gap: 2px; }
body.run-active .slip-label { font-size: 11.5px; }
body.run-active .f input { font-size: 18px; }

/* ===================== WO-013: READY screen restructure ===================== */
/* Slim top bar: brand left, rank name + mini progress center, theme toggle far right. */
.topbar-rank { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; }
.topbar-rank .rank-chip { margin-top: 0; }
.topbar-rank .rank-mini { margin-top: 0; width: 100%; max-width: 200px; }
.theme-toggle {
  background: var(--bg-2); border: 1px solid var(--line); color: var(--muted);
  border-radius: 8px; padding: 6px 12px; font-size: .8rem; cursor: pointer; font-family: var(--sans);
}
.theme-toggle:hover { border-color: var(--line-hover); color: var(--text); }
.theme-toggle:focus-visible { outline: 3px solid var(--blue); outline-offset: 2px; }

/* Idle column: centered, ~720px. */
.idle-main { max-width: 720px; margin: 0 auto; }
/* WO-030: title + subheader share one baseline row; the subheader is a quiet italic
   continuation (a hyphen prefix, not far-right-aligned) so it reads as suppressed. The
   idle title uses the shared --fs-title token so nothing reads as a different font. */
.idle-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 9px; margin-bottom: 4px; }
.idle-head h2 { margin: 0; font-size: var(--fs-title); }
.idle-subhead { font-style: italic; color: var(--muted); font-size: var(--fs-secondary); }
.idle-subhead::before { content: '- '; }
/* WO-025: quiet STANDBY state label at the top of the standby console. Spaced mono
   caps, muted, centered; identity, not a heading. */
.standby-label {
  font-family: var(--mono); text-align: center; color: var(--muted);
  letter-spacing: .38em; font-size: .68rem; margin: 2px 0 16px; text-indent: .38em;
}

/* Status line: one grouped row (retest countdown, today ovals, best-today).
   WO-030: spread the three items across the width (justified) rather than bunched. */
.status-line { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 6px 18px; margin: 0; }
.status-line .retest-line { margin: 0; }
.status-line .today-tally { margin: 0; justify-content: flex-start; }
.status-line .best-today.callout { margin: 0; }

/* Secondary action row under Start (Test conditions, Push pace). */
/* WO-017 B: two columns, each [button then its caption], so a caption stays under
   its own button instead of both stacking left-aligned below the row. */
.secondary-actions { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-start; }
.secondary-col { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.secondary-col .btn { width: 100%; margin-top: 0; }
.secondary-col .test-intro, .secondary-col .push-intro { margin-top: 6px; }

/* Disclosure row: Settings and Progress inline; an opened one takes the full width. */
.disclosure-row { display: flex; gap: 22px; flex-wrap: wrap; align-items: flex-start; }
.disclosure-row > details { flex: 0 0 auto; }
.disclosure-row > details[open] { flex: 1 1 100%; }

/* Bests moved into Progress (out of the header stats strip). */
.progress-bests { display: flex; gap: 18px; flex-wrap: wrap; margin: 8px 0 12px; font-size: .86rem; color: var(--muted); }
.progress-bests .pb-stat strong { color: var(--accent); font-variant-numeric: tabular-nums; font-weight: 700; }

/* WO-017 A: earned badges inside Progress. Plain lines, no nested boxes, same
   accent-name / muted-desc treatment as the results banner badge lines. */
.badges-earned { margin: 4px 0 12px; }
.badges-earned .badges-head { margin-bottom: 4px; }
.badges-list .badge-line { font-size: .9rem; color: var(--muted); line-height: 1.5; }

/* Where-next sentence (its Drill button now lives in the action rail). */
.where-next-text { font-size: .9rem; color: var(--text); margin: 6px 0 10px; }
.result-actions #drillBtn { flex: 0 0 auto; white-space: nowrap; margin-top: 0; }

/* ===================== WO-027: console redesign v2 ===================== */
/* B: lighter STANDBY idle. The idle view sheds the heavy stage box so its content
   reads as grouped lines on the background; the green Take next call stays the one
   filled accent and carries the eye. The run already strips the stage (run-active);
   results keeps its panel. */
body.idle-active .stage {
  border: none; background: transparent; padding: 0; min-height: 0;
}

/* D: CALL CLEARED state label at the top of results — the console identity for a
   cleared call, mirroring the STANDBY idle label. Quiet spaced mono caps, muted. */
.cleared-label {
  font-family: var(--mono); text-align: center; color: var(--muted);
  letter-spacing: .34em; font-size: .68rem; margin: 0 0 14px; text-indent: .34em;
}

/* C: side-by-side run above a laptop-width breakpoint. The source (call slip) sits in
   its own left column and the numbered entry fields on the right, so the reference stays
   visible with no sticky pin and all 8 fields fit without the page running off the
   bottom. Below the breakpoint we keep the WO-021/023/024 stacked layout with the sticky
   slip (the base .callslip rule stays sticky; this only overrides it wide). One accent
   remains the focused field. */
@media (min-width: 900px) {
  body.run-active #activeView {
    max-width: 980px;
    display: grid;
    grid-template-columns: 43% 1fr;
    grid-template-areas: "top top" "slip form";
    column-gap: 30px; align-items: start;
  }
  body.run-active #activeView .active-top { grid-area: top; }
  body.run-active #activeView .callslip {
    grid-area: slip; position: static; top: auto; margin-bottom: 0;
  }
  body.run-active #activeView #entryForm { grid-area: form; }
}

/* ===================== WO-028: Test simulator ===================== */
/* Live readout (REC / TIME / KPH / ACC), test runs only — the one place live meters
   are wanted (overrides the meters-off default). Mono, muted, tabular; REC is the only
   colored token (a calm soft red), values brighten. */
.test-readout {
  display: flex; align-items: center; gap: 14px; font-family: var(--mono);
  font-size: .74rem; letter-spacing: .04em; color: var(--muted); font-variant-numeric: tabular-nums;
}
.test-readout .tr-rec { color: var(--soft-red); font-weight: 700; letter-spacing: .1em; }
.test-readout strong { color: var(--text); font-weight: 700; }

/* Clinical SOURCE RECORD / DATA ENTRY column headers. The slip head already carries the
   mono, letter-spaced style; the entry head matches it above the fields. */
.entry-head {
  font-family: var(--mono); font-size: .7rem; letter-spacing: .16em; color: var(--muted);
  text-transform: uppercase; margin-bottom: 12px;
}
body.test-active #callslipHead { letter-spacing: .16em; }

/* WO-031/037: mid-run multitask prompt is NON-MODAL. The OVERLAY is a transparent full-width
   wrapper (no scrim, no surface, click-through) that only centers the card at the bottom middle
   of the viewport; the CARD is the visible surface. WO-037: the card is a compact panel sized to
   its content, flush to the bottom edge with soft rounded TOP corners, so it reads as a small
   prompt rising in the middle rather than a bar spanning the screen. The source record and entry
   fields stay fully visible while she classifies. It pauses entry focus but never stops the run. */
.multitask-overlay {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  display: flex; justify-content: center; align-items: flex-end;
  padding: 0; pointer-events: none;
}
.multitask-card {
  pointer-events: auto;
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 18px;
  width: fit-content; max-width: min(560px, calc(100vw - 24px));
  padding: 12px 20px 14px;
  background: var(--bg-2);
  border: 1px solid var(--accent-border-soft); border-bottom: 0;
  border-radius: 14px 14px 0 0;
  box-shadow: 0 -8px 24px var(--shadow);
  animation: fade .2s ease;
}
.multitask-top { display: flex; align-items: baseline; gap: 10px; }
.multitask-q { font-size: 1rem; font-weight: 700; }
.multitask-count { font-family: var(--mono); color: var(--muted); font-variant-numeric: tabular-nums; }
.multitask-count strong { color: var(--text); }
.multitask-sub { margin: 0; flex-basis: 100%; }
/* WO-035: the four services (Police / Fire / EMS / Utility) sit inline; they wrap only if
   the banner is narrow. Each choice leads with its 1 to 4 keyboard number. */
.multitask-choices { display: flex; flex-wrap: wrap; gap: 6px; flex-basis: 100%; }
.multitask-choice {
  display: inline-flex; align-items: center; justify-content: center;
  font-size: .8rem; padding: 5px 10px; margin: 0;
}
.multitask-choice .mt-num { opacity: .6; margin-right: .45em; font-variant-numeric: tabular-nums; }
/* WO-034: keyboard focus is the one accent while the prompt is up (she navigates by
   Tab / arrows), so the focused choice reads clearly against the neutral others. */
.multitask-choice:focus-visible { outline: 3px solid var(--blue); outline-offset: 2px; }

/* WO-037: the classify prompt is the ONE place we WANT attention capture (a deliberate
   interruption), so it draws the eye while the rest of the run stays calm. Three separable
   pieces so nothing collides on the single `animation` slot:
     .mt-alert  the amber STATE (bg + edge). No motion: this alone keeps it clearly present
                under prefers-reduced-motion.
     .mt-cue    a one-time appear cue: the bar slides up (overlay transform) and a downward
                chevron (::before) flicks the eye to it, both <= 400ms.
     .mt-pulse  a gentle ~1.5s amber glow on the card (its own box-shadow), until answered.
   The global prefers-reduced-motion block above drops the slide, chevron, and pulse; the base
   styles rest in-place (overlay untransformed, chevron opacity 0), so the amber state remains. */
/* The amber ALERT state paints the CARD (the visible surface), not the transparent wrapper. */
.multitask-overlay.mt-alert .multitask-card { background: var(--alert-bg); border-color: var(--alert-border); }
/* The appear cue (slide-up) and exit (slide-down) transform the wrapper, which carries the card;
   the chevron sits just above the centered card. */
.multitask-overlay.mt-cue { animation: mtSlideUp .3s ease-out; }
.multitask-overlay.mt-cue::before {
  content: ""; position: absolute; top: -15px; left: 50%; width: 13px; height: 13px; margin-left: -7px;
  border-right: 3px solid var(--alert-border); border-bottom: 3px solid var(--alert-border);
  transform: rotate(45deg); opacity: 0; animation: mtChevron .4s ease-out;
}
.multitask-overlay.mt-pulse .multitask-card { animation: mtPulse 1.5s ease-in-out infinite; }
/* WO-037: on answer / skip the card slides down and fades out, quick (~200ms) but graceful. It
   keeps the amber state through the slide (.mt-alert stays until it is gone) and ignores clicks
   on the way out. Under prefers-reduced-motion the JS hides it instantly, so this never runs. */
.multitask-overlay.mt-leaving { animation: mtSlideDown .2s ease-in forwards; }
.multitask-overlay.mt-leaving .multitask-card { pointer-events: none; }
@keyframes mtSlideUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
@keyframes mtSlideDown { from { transform: translateY(0); opacity: 1; } to { transform: translateY(100%); opacity: 0; } }
@keyframes mtChevron {
  0% { opacity: 0; transform: translateY(-8px) rotate(45deg); }
  40% { opacity: 1; transform: translateY(0) rotate(45deg); }
  100% { opacity: 0; transform: translateY(4px) rotate(45deg); }
}
@keyframes mtPulse {
  0%, 100% { box-shadow: 0 -8px 24px var(--shadow), 0 0 0 0 var(--alert-glow-soft); }
  50% { box-shadow: 0 -8px 24px var(--shadow), 0 0 16px 3px var(--alert-glow-strong); }
}

/* WO-034: the scored call-type result line (test runs only). Neutral by default; a
   right pick reads accent-green, a wrong / missed pick reads soft-red, matching the
   breakdown palette. Data-level color only (the container stays a plain line). */
.call-type-line { margin: 10px 0 0; font-size: var(--fs-secondary); color: var(--text); }
.call-type-line .ct-verdict { font-weight: 700; }
.call-type-line.ct-right .ct-verdict { color: var(--accent); }
.call-type-line.ct-wrong .ct-verdict { color: var(--soft-red); }

/* Accessibility: honor reduced-motion preferences across the whole app */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* BUGFIX: author display rules (e.g. .intro-overlay { display: grid }) override
   the browser's default [hidden] -> display:none, so hidden overlays stayed on
   screen and ate clicks/keys. This restores the expected behavior globally. */
[hidden] { display: none !important; }
