/* ------------------------------------------------------------------
   The hidden attribute must always win.

   The UA stylesheet gives [hidden] display:none, but that is a bare
   attribute selector — any class rule setting display beats it on
   specificity, silently. This has now bitten twice: the submission gate
   (.modal-overlay) appeared on the setup screen at load, and the resume
   card (.resume-card) appeared for logged-out visitors. In both cases the
   JavaScript was correct and the element was marked hidden; CSS just
   ignored it.

   Fixing each class as it comes up leaves the next one waiting to be
   found in testing. This fixes the category.
   ------------------------------------------------------------------ */
[hidden] {
  display: none !important;
}

:root {
  --bg: #FCF6F8;
  --surface: #FFFFFF;
  --surface-alt: #F8E9EE;
  --ink: #24111A;
  --ink-soft: #7B6470;
  --primary: #7A1232;
  --primary-dark: #5B0D25;
  /* The color painted on TOP of a --primary-filled surface (button labels,
     active chip text). Coincides with --surface in light mode (white on
     white background alike), but the two must stay independent tokens:
     dark mode's --surface becomes dark ink, while text on a primary fill
     still needs to stay light regardless of theme. See the dark blocks
     below for why conflating the two used to force --primary itself to
     invert to a pale color just so var(--surface) text stayed readable. */
  --on-primary: #FFFFFF;
  --on-primary-hover: #FFFFFF;
  --correct: #168A64;
  --correct-bg: #E7FBF5;
  --incorrect: #C24132;
  --incorrect-bg: #FCEDEA;
  --caution: #A36A00;
  --line: #E8D8DE;

  /* Gamification accent — deliberately separate from --caution (which
     already means "difficulty/partial/warning" in a dozen places). XP,
     level-ups and badges get their own color so a level-up toast never
     reads as a warning. */
  --accent-gold: #B87A22;
  --accent-gold-bg: #F7EBD4;

  /* Second-tier tokens — every color that used to be a bare hex literal
     scattered through the file (auth form, count-custom input, resume
     card, exam badge, etc.), pulled up here so dark mode below only has
     to redefine ONE place per color, not hunt every usage site. */
  --primary-tint: #FAEAF0;
  --primary-tint-2: #F4DCE4;
  --caution-tint: #FFF7EC;
  --caution-ink: #6F4C15;
  --neutral-tint: #F7EEF1;
  --neutral-tint-border: #E8D8DE;
  --muted-2: #B99DA8;
  --muted-3: #8E7480;
  --error: #B3261E;
  --highlight-yellow: #FFF0B7;
  --exam-badge-bg: #FAEAF0;
  --exam-badge-border: #D9A5B6;
  --exam-badge-ink: #7A1232;
  --stage-grad-1: #FFFFFF;
  --stage-grad-2: #F8E9EE;
  --btn-disabled-bg: #CBB8BF;
  --overlay-scrim: rgba(0, 0, 0, 0.45);
  /* Raw triplets so an rgba() overlay (the anatomy hover chip, the
     correct-pulse glow) can follow the SAME color dark mode redefines
     below, instead of being a separate literal that silently stops
     matching once the theme flips. */
  --primary-rgb: 122, 18, 50;
  --correct-rgb: 22, 138, 100;
  --incorrect-rgb: 194, 65, 50;
  --caution-rgb: 163, 106, 0;
  --surface-rgb: 255, 255, 255;

  --font-display: "Manrope", sans-serif;
  --font-body: "Inter", sans-serif;
  /* Utility/data text deliberately stays within the two-family limit.
     Inter's tabular figures provide alignment without loading a third,
     monospace face. */
  --font-mono: "Inter", sans-serif;

  /* Shared 4/8-based spacing rhythm. --control-gap is the minimum distance
     between neighboring buttons everywhere in the candidate and admin UIs. */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --control-gap: var(--space-2);
  --control-gap-roomy: var(--space-3);

  /* Motion system — one duration/easing scale referenced everywhere else,
     instead of ad hoc numbers per component. See PNLE_MIGRATION_AUDIT
     redesign proposal, "Motion system". */
  --dur-card: 180ms;
  --dur-modal: 220ms;
  --dur-page: 320ms;
  --stagger-step: 40ms;
  --ease-standard: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);

  /* Fixed black at low opacity in BOTH themes — a shadow is not the
     foreground ink color re-tinted, so this is deliberately not one of
     the tokens dark mode below redefines. */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.08);
  --shadow-md: 0 6px 20px -8px rgba(0, 0, 0, 0.22);
}

/* Dark palette. Three-block pattern: bare :root above is the light
   default; this media-query block covers a visitor who never chose a
   theme explicitly (the common case — most people are on "system"); the
   [data-theme="dark"] block below covers someone who explicitly toggled
   dark regardless of their OS setting. Every token gets a value in every
   block it applies to — a color defined ONLY in one of these three places
   is the classic bug that leaves the unstamped state reading light text
   on a light ground or vice versa. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    /* Page background is the brand's own ink (#24111A) per the reference
       palette, not a further-darkened near-black — surface/surface-alt
       step UP from there for card elevation, continuing the same
       progression the old bg->surface->surface-alt ladder used. */
    --bg: #24111A;
    --surface: #2A151E;
    --surface-alt: #351923;
    --ink: #FCF6F8;
    --ink-soft: #D8BDC7;
    /* Same hue family as the light-mode merlot (~341°), but pulled DOWN in
       lightness/saturation rather than up — a lightened, highly-saturated
       version of a wine-red hue is what previously read as bubblegum pink
       rather than dark merlot. A mid-tone, moderately saturated wine-rose
       reads as "dark merlot" instead; --on-primary (added above) carries
       the text-on-fill contrast these no longer can provide on their own
       at this lightness. */
    --primary: #7A1232;
    --primary-dark: #9B2B4D;
    --on-primary: #FCF6F8;
    --on-primary-hover: #24111A;
    --correct: #55DDB5;
    --correct-bg: #12332B;
    --incorrect: #F19A8F;
    --incorrect-bg: #431B22;
    --caution: #E1B24F;
    --line: #5A2B3B;

    --accent-gold: #E0A44C;
    --accent-gold-bg: #2A230F;

    --primary-tint: #321722;
    --primary-tint-2: #421D2C;
    --caution-tint: #382713;
    --caution-ink: #F4D48C;
    /* #311923 sat only ~4-2-0 RGB units from --surface (#351723) — visually
       indistinguishable, the disabled-input/hover states this token drives
       just blended into whatever card they were on. #413035 is a
       deliberately DESATURATED (less red-dominant, more grey) warm-neutral
       rather than a further step up the same wine ladder as
       surface/surface-alt — "neutral" reads as neutral by being less
       colorful than its surroundings, not just a different lightness of
       the same hue. ~33 RGB units from --surface, ~50 from --bg. */
    --neutral-tint: #413035;
    --neutral-tint-border: #5A2B3B;
    --muted-2: #9D7A87;
    --muted-3: #C6A6B1;
    --error: #E38F82;
    --highlight-yellow: #5C4A1A;
    --exam-badge-bg: #3D1927;
    --exam-badge-border: #8C3B58;
    --exam-badge-ink: #E8A0B8;
    /* Same relationship as before (spans from surface-level down to
       bg-level) shifted to match the new ladder, so the 3D stage still
       reads as an elevated panel rather than flattening into the page. */
    --stage-grad-1: #2A151E;
    --stage-grad-2: #24111A;
    --btn-disabled-bg: #60414C;
    --overlay-scrim: rgba(0, 0, 0, 0.6);

    --primary-rgb: 178, 58, 92;
    --correct-rgb: 85, 221, 181;
    --incorrect-rgb: 241, 154, 143;
    --caution-rgb: 225, 178, 79;
    --surface-rgb: 42, 21, 30;
  }
}
:root[data-theme="dark"] {
  --bg: #24111A;
  --surface: #2A151E;
  --surface-alt: #351923;
  --ink: #FCF6F8;
  --ink-soft: #D8BDC7;
  --primary: #7A1232;
  --primary-dark: #9B2B4D;
  --on-primary: #FCF6F8;
  --on-primary-hover: #24111A;
  --correct: #55DDB5;
  --correct-bg: #12332B;
  --incorrect: #F19A8F;
  --incorrect-bg: #431B22;
  --caution: #E1B24F;
  --line: #5A2B3B;

  --accent-gold: #E0A44C;
  --accent-gold-bg: #2A230F;

  --primary-tint: #321722;
  --primary-tint-2: #421D2C;
  --caution-tint: #382713;
  --caution-ink: #F4D48C;
  --neutral-tint: #413035;
  --neutral-tint-border: #5A2B3B;
  --muted-2: #9D7A87;
  --muted-3: #C6A6B1;
  --error: #E38F82;
  --highlight-yellow: #5C4A1A;
  --exam-badge-bg: #3D1927;
  --exam-badge-border: #8C3B58;
  --exam-badge-ink: #E8A0B8;
  --stage-grad-1: #2A151E;
  --stage-grad-2: #24111A;
  --btn-disabled-bg: #60414C;
  --overlay-scrim: rgba(0, 0, 0, 0.6);

  --primary-rgb: 178, 58, 92;
  --correct-rgb: 85, 221, 181;
  --incorrect-rgb: 241, 154, 143;
  --caution-rgb: 225, 178, 79;
  --surface-rgb: 42, 21, 30;
}

/* A student under exam-timed stress should never be fighting motion they
   didn't ask for, and vestibular-sensitive users can't opt out per
   animation — this is the one blanket switch. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

* { box-sizing: border-box; }

.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;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  line-height: 1.5;
}

.mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* ---------- app bar ---------- */
.app-bar {
  position: relative; /* anchors .app-bar__sub's true-center overlay below */
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 28px;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}
.app-bar__mark {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-weight: 800;
  letter-spacing: 0;
  font-size: 1.05rem;
  color: var(--primary-dark);
  text-decoration: none;
  min-height: 44px;
}
.app-bar__mark img {
  width: 34px;
  height: 34px;
  border-radius: 9px;
  flex: 0 0 auto;
}
.app-bar__mark span { color: var(--ink); }
/* Centered on the BAR, not on the leftover space between its neighbours —
   a grid's middle column (the first attempt here) only centers within
   whatever room is left after the mark and the auth widget, and those
   two are never equal width (the auth widget grows with nav links plus
   however long the signed-in username is), so the tagline still drifted
   off true-center depending on who was logged in. Absolute-positioning
   it at the bar's own 50% guarantees it every time, independent of its
   siblings' widths. Decorative only (no click target), so
   pointer-events:none lets a click "through" it reach whatever's really
   underneath if the bar ever gets tight enough to overlap. */
.app-bar__sub {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  white-space: nowrap;
  pointer-events: none;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* Fixed to the viewport (bottom-right of the whole screen), not part of
   the app-bar's own layout — it used to sit inline between the tagline
   and the auth widget, which was also part of what threw the tagline's
   centering off. Sits just above the gamification toast stack, which
   shares this corner; see .gami-toast-host's own comment for the offset
   that keeps them from overlapping. */
.theme-toggle {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 950;
  width: 32px; height: 32px;
  flex-shrink: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--surface);
  color: var(--ink-soft);
  font-size: 0.95rem;
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--shadow-md);
  transition: border-color var(--dur-card) var(--ease-standard), color var(--dur-card) var(--ease-standard), transform var(--dur-card) var(--ease-standard);
}
.theme-toggle:hover { border-color: var(--primary); color: var(--primary); }
.theme-toggle:active { transform: scale(0.9); }

/* ---------- disclaimer bar ---------- */
/* In normal document flow (not fixed) so it can't collide with the
   theme toggle or the gamification toasts already fixed in the bottom
   corner — it just pushes page content down like any other block,
   visible above the fold on every screen since it lives outside every
   .screen div rather than inside one of them. */
.disclaimer-bar {
  position: relative;
  display: flex;
  align-items: center;
  background: var(--caution-tint);
  border-bottom: 1px solid var(--line);
  padding: 10px 28px;
}
.disclaimer-bar__close {
  position: absolute;
  top: 50%;
  right: var(--space-3);
  width: 44px;
  height: 44px;
  transform: translateY(-50%);
  border: 0;
  background: transparent;
  color: var(--caution-ink);
  font-size: 1.35rem;
  cursor: pointer;
}
.disclaimer-bar__close:hover { color: var(--ink); }
.disclaimer-bar p {
  max-width: 720px;
  margin: 0 auto;
  color: var(--caution-ink);
  font-size: 0.78rem;
  line-height: 1.5;
  text-align: center;
}
.disclaimer-bar strong { font-weight: 700; }
@media (max-width: 520px) {
  .disclaimer-bar { padding: 8px 16px; }
  .disclaimer-bar p { font-size: 0.72rem; text-align: left; }
}

/* ---------- shared ---------- */
.screen {
  max-width: 720px;
  margin: 0 auto;
  padding: 32px 20px 80px;
}
.eyebrow {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--primary);
  margin: 0 0 6px;
}
h1 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.8rem;
  margin: 0 0 24px;
  color: var(--ink);
}
h3 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1rem;
  text-transform: none;
  letter-spacing: -0.01em;
  color: var(--primary-dark);
  margin: 32px 0 12px;
}
.hint {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--ink-soft);
  margin: 8px 0 0;
}

button { touch-action: manipulation; }

.btn {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.95rem;
  padding: 13px 22px;
  border-radius: 3px;
  border: 1px solid transparent;
  cursor: pointer;
  transition: background 0.15s ease, transform 0.05s ease;
  min-height: 44px;
}
.btn:active { transform: translateY(1px); }
.btn--primary { background: var(--primary); color: var(--on-primary); }
.btn--primary:hover { background: var(--primary-dark); color: var(--on-primary-hover); }
.btn--primary:disabled { background: var(--btn-disabled-bg); cursor: not-allowed; }
.btn--ghost { background: var(--surface-alt); border-color: var(--line); color: var(--ink); }
.btn--ghost:hover { border-color: var(--primary); color: var(--primary-dark); }
.btn--ghost:disabled { opacity: 0.4; cursor: not-allowed; }
.btn--compact { min-height: 40px; padding: 8px 14px; font-size: 0.82rem; }
.btn--danger {
  background: var(--incorrect-bg);
  border-color: var(--incorrect);
  color: var(--incorrect);
}
.btn--danger:hover { filter: brightness(0.97); }

/* Forms often place their submit control directly after an input rather
   than wrapping both in an action row. Give that relationship the same
   minimum separation as every flex/grid button group. */
.field-group > .btn,
.auth-form > .btn { margin-top: var(--control-gap); }

/* ---------- setup screen ---------- */
.setup-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 32px;
}
.field-group { margin-bottom: 26px; }
.mode-subpanel {
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--surface-alt);
}
.rapid-timer-setting {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 88px;
  align-items: center;
  gap: var(--control-gap);
  max-width: 360px;
  margin-top: var(--space-3);
}
.rapid-timer-setting .field-label { margin: 0; }
.rapid-timer-setting .input { width: 100%; }
.field-label {
  display: block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--ink);
  margin-bottom: 10px;
}
.chip-list { display: flex; flex-wrap: wrap; gap: var(--control-gap); }
.chip {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  padding: 8px 14px;
  border: 1px solid var(--line);
  border-radius: 20px;
  background: var(--surface);
  color: var(--ink-soft);
  cursor: pointer;
  min-height: 44px;
}
.chip.is-active {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--on-primary);
}

/* ---- subject mastery path ----
   A visual map over the taxonomy picker below it, not a second selection
   surface — every node's click routes to the real chip (app.js), so
   there's exactly one place selection state lives. Purely additive: hidden
   whenever the active profile has no taxonomy tree. */
.mastery-path { margin-bottom: 18px; }
.mastery-path__track {
  display: flex;
  align-items: center;
  gap: 0;
  overflow-x: auto;
  padding: 4px 2px 10px;
}
.mastery-path__connector {
  flex: 1 1 24px;
  min-width: 16px;
  height: 2px;
  background: var(--line);
  margin: 0 -2px;
  align-self: center;
  /* Lines up with ring center, not the node's full height (label + pct
     sit below the ring), so the path reads as one continuous line rather
     than zigzagging between ring and text. */
  transform: translateY(-14px);
}
.mastery-path__node {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  flex: 0 0 auto;
  width: 84px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 2px;
  border-radius: 8px;
  opacity: 0.4;
  transition: transform var(--dur-card) var(--ease-standard), opacity var(--dur-card) var(--ease-standard);
}
@media (hover: hover) and (pointer: fine) {
  .mastery-path__node:hover { transform: translateY(-2px); }
}
.mastery-path__node:active { transform: scale(0.94); }
/* This is the piece that was missing: the node's own selected look, so
   clicking it (or the pill it mirrors) is visible right here, not just
   in the pill list below. Full opacity + a ring border when selected;
   faded when not — same "off/partial/active" vocabulary as the chips. */
.mastery-path__node.is-selected { opacity: 1; }
.mastery-path__node.is-selected .mastery-path__label { color: var(--primary); }
.mastery-path__node.is-selected:not(.is-partial) .mastery-path__ring-inner {
  box-shadow: inset 0 0 0 2px var(--primary);
}
.mastery-path__node.is-partial .mastery-path__ring-inner {
  box-shadow: inset 0 0 0 2px var(--caution);
}
@keyframes mastery-pulse {
  0%   { transform: scale(1); }
  40%  { transform: scale(0.88); }
  100% { transform: scale(1); }
}
.mastery-path__node.is-pulsing { animation: mastery-pulse 320ms var(--ease-spring); }
.mastery-path__ring {
  width: 46px; height: 46px;
  border-radius: 50%;
  /* Mastery % is a progress indicator, not a selection state — --correct
     (mint in dark mode) reads as "how much of this you've learned",
     distinct from --primary below, which still marks WHICH node is
     currently selected. Using one color for both would make a mint-ringed
     node ambiguous between "you've mastered this" and "this is picked". */
  background: conic-gradient(var(--correct) calc(var(--mastery, 0) * 360deg), var(--surface-alt) 0);
  display: flex; align-items: center; justify-content: center;
  padding: 3px;
}
.mastery-path__node.is-neutral .mastery-path__ring {
  /* Flat, not a 0%-filled conic-gradient — "no data yet" must not read as
     "you are failing this subject." */
  background: var(--surface-alt);
  border: 1px dashed var(--line);
}
.mastery-path__ring-inner {
  width: 100%; height: 100%;
  border-radius: 50%;
  background: var(--surface);
}
.mastery-path__label {
  font-family: var(--font-display);
  font-size: 0.72rem;
  font-weight: 600;
  text-align: center;
  line-height: 1.2;
}
.mastery-path__pct {
  font-size: 0.68rem;
  color: var(--ink-soft);
}
@media (max-width: 640px) {
  .mastery-path__node { width: 68px; }
  .mastery-path__ring { width: 38px; height: 38px; }
  .mastery-path__label { font-size: 0.64rem; }
}

/* ---- taxonomy picker (PNLE phase 4) ----
   Subject rows with an expander for their areas. Built from the same
   .chip primitives as the category picker so both examinations look
   like one product, and so the touch targets already tuned for phones
   apply here without a second set of rules. */
.taxonomy { display: flex; flex-direction: column; gap: 6px; }

.taxonomy__subject {
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 6px;
  background: var(--surface);
}

.taxonomy__head { display: flex; align-items: center; gap: 8px; }

.taxonomy__expander {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  font-family: var(--font-mono);
  font-size: 1rem;
  line-height: 1;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface-alt);
  color: var(--ink-soft);
  cursor: pointer;
}
.taxonomy__expander:disabled { opacity: 0.35; cursor: default; }

.taxonomy__chip { text-align: left; }

/* A subject with only some of its areas picked. Neither "on" nor "off"
   would be true, and showing it as off is the lie that matters — the
   user would think their narrowed selection had been dropped. */
.chip.is-partial {
  background: var(--surface-alt);
  border-color: var(--primary);
  color: var(--ink);
  box-shadow: inset 3px 0 0 var(--primary);
}

.taxonomy__children {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 8px 0 4px 36px;
}

.taxonomy__chip--area { font-size: 0.72rem; padding: 6px 11px; }

.taxonomy__tools {
  display: flex;
  align-items: center;
  gap: var(--control-gap);
  margin-top: 8px;
  flex-wrap: wrap;
}
.taxonomy__toolsep { color: var(--line); }
.taxonomy__count {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--ink-soft);
  margin-left: auto;
}

@media (max-width: 520px) {
  /* The 36px indent costs more than it communicates once the row is
     only a few chips wide. */
  .taxonomy__children { padding-left: 10px; }
  .taxonomy__count { margin-left: 0; width: 100%; }
}

@media (pointer: coarse) {
  .taxonomy__expander { width: 34px; height: 34px; }
  .taxonomy__chip--area { padding: 9px 13px; font-size: 0.75rem; }
}
.select {
  font-family: var(--font-mono);
  font-size: 0.9rem;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: var(--surface);
  color: var(--ink);
  width: 100%;
  max-width: 260px;
}
#setupError { color: var(--incorrect); }

/* ---------- pulse trace (signature element) ---------- */
.pulse-rail {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 6px 0;
  margin-bottom: 18px;
}
#pulseTrace { width: 100%; height: 44px; display: block; }

.quiz-topline {
  display: flex;
  justify-content: space-between;
  font-size: 0.8rem;
  color: var(--ink-soft);
  margin-bottom: 18px;
}
#quizTimer { color: var(--primary-dark); }

/* ---------- question card ---------- */
.question-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 28px;
}
.q-meta { display: flex; gap: 8px; margin-bottom: 18px; flex-wrap: wrap; }
.tag {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 4px 9px;
  border-radius: 3px;
  background: var(--surface-alt);
  color: var(--ink-soft);
}
/* A category label, not an action or a status — it appears on every single
   question. A solid merlot fill here is the single most-repeated "pink"
   surface in the app; a tinted/bordered treatment still reads as branded
   without turning every question view into a merlot block. */
.tag--type { background: var(--primary-tint); color: var(--primary-dark); border: 1px solid var(--line); }
.tag--difficulty { border: 1px solid var(--caution); color: var(--caution); background: transparent; }

.q-text {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 1.15rem;
  line-height: 1.55;
  margin: 0 0 8px;
  color: var(--ink);
}
.sata-note {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--incorrect);
  margin: 0 0 18px;
}

.options-list { display: flex; flex-direction: column; gap: 10px; margin-top: 18px; }
.option {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: 4px;
  cursor: pointer;
  background: var(--surface);
  transition: border-color var(--dur-card) var(--ease-standard),
              background var(--dur-card) var(--ease-standard),
              box-shadow var(--dur-card) var(--ease-standard),
              transform var(--dur-card) var(--ease-standard);
}
/* :hover only — the row is rebuilt on every click (see option_based.js's
   render()), so a hover lift is the one interaction that survives across
   redraws; the coarse-pointer media query keeps it from sticking on touch,
   where there's no real "hover" and a stuck lift would look like a bug. */
@media (hover: hover) and (pointer: fine) {
  .option:hover { border-color: var(--primary); transform: translateY(-1px); box-shadow: var(--shadow-sm); }
}
.option:active { transform: translateY(0); }
.option.is-selected {
  border-color: var(--primary);
  background: var(--primary-tint);
  animation: option-select var(--dur-card) var(--ease-standard);
}
@keyframes option-select {
  from { transform: scale(0.98); }
  to { transform: scale(1); }
}
.option__marker {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: 0.85rem;
  width: 22px; height: 22px;
  flex-shrink: 0;
  border-radius: 50%;
  border: 1px solid var(--line);
  display: flex; align-items: center; justify-content: center;
  color: var(--ink-soft);
  transition: background var(--dur-card) var(--ease-standard), border-color var(--dur-card) var(--ease-standard), color var(--dur-card) var(--ease-standard);
}
.option.is-selected .option__marker { background: var(--primary); border-color: var(--primary); color: var(--on-primary); }
.option__text { font-family: var(--font-body); font-size: 0.98rem; }

/* Rapid feedback is intentionally limited to correctness colors. Rationales
   stay absent until the dedicated results screen. */
.options-list.is-rapid-locked .option { cursor: default; pointer-events: none; }
.option.is-rapid-correct {
  border-color: var(--correct);
  background: var(--correct-bg);
  box-shadow: inset 4px 0 0 var(--correct);
}
.option.is-rapid-wrong {
  border-color: var(--incorrect);
  background: var(--incorrect-bg);
  box-shadow: inset 4px 0 0 var(--incorrect);
}
.rapid-topline { align-items: center; }
.rapid-countdown {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 52px;
  min-height: 52px;
  border: 2px solid var(--primary);
  border-radius: 50%;
  color: var(--primary-dark);
  font-size: 1.15rem;
  font-weight: 700;
}
.rapid-countdown.is-urgent { border-color: var(--incorrect); color: var(--incorrect); }
.rapid-feedback {
  min-height: 1.5em;
  margin: var(--space-4) 0 0;
  color: var(--ink-soft);
  font-weight: 600;
}
.rapid-wrong-nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--control-gap);
}
#rapidReviewList { margin-top: var(--space-4); }
#rapidDone { width: 100%; margin-top: var(--space-4); }

.quiz-actions {
  display: flex;
  justify-content: space-between;
  gap: var(--control-gap);
  margin-top: 22px;
}

/* ---------- results screen ---------- */
.results-hero {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 36px;
  text-align: center;
  /* Shared spacing rule, not a per-screen margin hack: every screen using
     this hero (Results, Progress, Flashcards, History, Analytics — all via
     .results-hero--compact) had whatever comes next sitting flush against
     it, since the hero itself carried no bottom margin and none of the
     cards that follow (.level-card, .breakdown-card, etc.) carry a
     matching margin-top. Matches h1's own established margin: 0 0 24px. */
  margin-bottom: var(--space-5);
}
.score-big {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 4rem;
  color: var(--primary-dark);
  line-height: 1;
}
.score-unit { font-size: 2rem; color: var(--ink-soft); }
#scoreFraction { color: var(--ink-soft); margin-top: 6px; }

.breakdown-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}
.breakdown-row__label { flex: 1; font-family: var(--font-display); font-size: 0.88rem; }
.breakdown-row__bar {
  flex: 2;
  height: 8px;
  background: var(--surface-alt);
  border-radius: 4px;
  overflow: hidden;
}
.breakdown-row__fill {
  height: 100%;
  background: var(--primary);
  transition: width 700ms var(--ease-standard);
}
.breakdown-row__pct { font-family: var(--font-mono); font-size: 0.8rem; width: 46px; text-align: right; }

.review-item {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 4px;
  margin-bottom: 12px;
  overflow: hidden;
  animation: review-reveal var(--dur-page) var(--ease-standard) backwards;
}
@keyframes review-reveal {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.review-item__head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  cursor: pointer;
}
.review-item__badge {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 600;
  padding: 3px 8px;
  border-radius: 3px;
  text-transform: uppercase;
}
.review-item__save-card {
  margin-left: auto;
  flex-shrink: 0;
  font-size: 0.76rem;
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  transition: border-color var(--dur-card) var(--ease-standard), color var(--dur-card) var(--ease-standard);
}
.review-item__save-card:hover:not(:disabled) { border-color: var(--primary); color: var(--primary); }
.review-item__save-card:disabled { opacity: 0.7; cursor: default; }
.review-item__badge--correct { background: var(--correct-bg); color: var(--correct); }
.review-item__badge--incorrect { background: var(--incorrect-bg); color: var(--incorrect); }
.review-item__stem { font-family: var(--font-body); font-size: 0.92rem; flex: 1; }
/* Expand/collapse via the grid 0fr->1fr row trick rather than display:none,
   so opening a review item animates instead of cutting instantly — the
   inner wrapper (added in app.js) is what actually gets clipped, since a
   grid row's own overflow can't be set as it sizes itself. */
.review-item__body {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur-modal) var(--ease-standard);
}
.review-item.is-open .review-item__body { grid-template-rows: 1fr; }
.review-item__body-inner { overflow: hidden; min-height: 0; padding: 0 16px 16px; }

.review-option {
  padding: 10px 12px;
  border-radius: 3px;
  margin-bottom: 6px;
  font-size: 0.88rem;
  border: 1px solid var(--line);
}
.review-option--correct {
  border-color: var(--correct);
  background: var(--correct-bg);
  animation: correct-pulse 700ms var(--ease-standard);
}
.review-option--correct .review-option__label::after { content: " \2713"; color: var(--correct); }
.review-option--wrong-pick {
  border-color: var(--incorrect);
  background: var(--incorrect-bg);
  animation: wrong-shake 420ms var(--ease-standard);
}
@keyframes correct-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(var(--correct-rgb), 0.32); }
  60%  { box-shadow: 0 0 0 7px rgba(var(--correct-rgb), 0); }
  100% { box-shadow: 0 0 0 0 rgba(var(--correct-rgb), 0); }
}
@keyframes wrong-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-4px); }
  40% { transform: translateX(3px); }
  60% { transform: translateX(-2px); }
  80% { transform: translateX(1px); }
}
.review-option__label { font-family: var(--font-mono); font-weight: 600; margin-right: 6px; }
.review-option__rationale { color: var(--ink-soft); margin-top: 4px; font-size: 0.85rem; }

#restartBtn { margin-top: 24px; width: 100%; }

@media (max-width: 520px) {
  .score-big { font-size: 3rem; }
  .screen { padding: 20px 14px 60px; }
}

/* ---------- auth widget (increment 2) ---------- */
.app-bar__auth {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  display: flex;
  align-items: center;
  gap: var(--control-gap-roomy);
}
.app-bar__auth a,
.app-bar__auth button.link {
  color: var(--primary);
  text-decoration: none;
  cursor: pointer;
  background: none;
  border: none;
  font-family: inherit;
  font-size: inherit;
  padding: 0;
}
.app-bar__auth a:hover,
.app-bar__auth button.link:hover { text-decoration: underline; }
.app-bar__auth .app-bar__user { color: var(--ink-soft); }

.field-input {
  font-family: var(--font-mono);
  font-size: 0.9rem;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: var(--surface);
  color: var(--ink);
  width: 100%;
}
.field-input:focus {
  outline: none;
  border-color: var(--primary);
}

/* ---------- auth modal ---------- */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: var(--overlay-scrim);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  z-index: 50;
}
.is-auth-gated {
  overflow: auto;
}
.is-auth-gated > .app-bar,
.is-auth-gated > .disclaimer-bar,
.is-auth-gated > .theme-toggle,
.is-auth-gated > .screen,
.is-auth-gated > footer {
  visibility: hidden;
  pointer-events: none;
}
.auth-gate {
  overflow-y: auto;
  background:
    radial-gradient(circle at 18% 12%, color-mix(in srgb, var(--primary) 16%, transparent), transparent 34rem),
    var(--bg);
}
.auth-gate__card {
  max-width: 460px;
  border: 1px solid var(--line);
  box-shadow: 0 24px 70px color-mix(in srgb, var(--ink) 16%, transparent);
}
.auth-brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}
.auth-brand img {
  width: 56px;
  height: 56px;
  border-radius: 14px;
  flex: 0 0 auto;
}
.auth-brand span {
  color: var(--ink-soft);
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.25;
}
.auth-gate__title {
  margin: 4px 0 8px;
  font-size: clamp(1.8rem, 5vw, 2.35rem);
}
.auth-gate__intro { margin: 0 0 24px; }
.auth-switch { margin: var(--space-4) 0 0; text-align: center; color: var(--ink-soft); }
.auth-switch .link { width: auto; min-height: 0; display: inline; margin: 0; padding: 0; border: 0; background: transparent; color: var(--primary); cursor: pointer; }
.modal-card {
  background: var(--surface);
  border-radius: 4px;
  padding: 32px;
  width: 100%;
  max-width: 400px;
  position: relative;
}
.modal-close {
  position: absolute;
  top: 14px;
  right: 14px;
  background: none;
  border: none;
  font-size: 1.4rem;
  line-height: 1;
  color: var(--ink-soft);
  cursor: pointer;
}
.modal-close:hover { color: var(--ink); }

.auth-tabs {
  display: flex;
  gap: var(--space-1);
  margin-bottom: 24px;
  border-bottom: 1px solid var(--line);
}
.auth-tab {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.9rem;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 10px 4px 12px;
  margin-right: 20px;
  color: var(--ink-soft);
  cursor: pointer;
}
.auth-tab.is-active {
  color: var(--primary-dark);
  border-bottom-color: var(--primary);
}
.auth-form .btn { width: 100%; margin-top: var(--control-gap); }
#loginError, #registerError, #resetPasswordStatus { color: var(--incorrect); min-height: 1em; }
#requestResetStatus { min-height: 1em; }
.auth-form__row,
.auth-form__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.auth-form__row { margin-top: 4px; }
.auth-form__footer { margin-top: 18px; }
.auth-form__row .link,
.auth-form__footer .link {
  display: inline-flex;
  width: auto;
  min-height: 44px;
  align-items: center;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  text-decoration: underline;
}
.check-label {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  color: var(--ink-soft);
  cursor: pointer;
}
.check-label input { width: 18px; height: 18px; accent-color: var(--primary); }

/* ---------- history screen ---------- */
.results-hero--compact { padding-bottom: 8px; }
.history-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 14px;
  align-items: center;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
}
.history-row:last-child { border-bottom: none; }
.history-row__meta {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--ink-soft);
}
.history-row__filters { font-size: 0.9rem; color: var(--ink); }
.history-row__score {
  font-family: var(--font-mono);
  font-weight: 600;
  color: var(--primary-dark);
}
#historyBackBtn { margin-top: 20px; }
#saveStatus { text-align: center; }

/* =====================================================================
   Increment 3 — advanced question types.

   Appended, not merged into the blocks above, so a diff shows exactly
   what this increment added. Uses only the existing tokens; no new
   colors, no new typefaces. The one structural idea borrowed from the
   subject: an ordered-response question is laid out like a numbered
   care-plan checklist, because that is what it is.
   ===================================================================== */

/* ---------- ordered response ---------- */
.ordered {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.ordered__slots {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--line);
}
.ordered__slot {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 6px;
  border-bottom: 1px solid var(--line);
  min-height: 52px;
}
.ordered__slot.is-filled { background: var(--primary-tint); }

.ordered__rank {
  flex: 0 0 26px;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--ink-soft);
  text-align: right;
}
.ordered__slot.is-filled .ordered__rank { color: var(--primary); }

.ordered__empty {
  color: var(--line);
  font-family: var(--font-mono);
}

.ordered__item {
  flex: 1 1 auto;
  text-align: left;
  font-family: var(--font-body);
  font-size: 0.95rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 3px;
  /* 44px min target — this is a phone-first audience and these are the
     only controls on the screen. */
  min-height: 44px;
  padding: 10px 12px;
  cursor: pointer;
}
.ordered__item:hover { border-color: var(--primary); }
.ordered__item--placed {
  border-color: var(--primary);
  background: var(--surface);
}

.ordered__nudge { display: flex; flex-direction: column; gap: 2px; }
.ordered__arrow {
  width: 32px;
  height: 24px;
  line-height: 1;
  font-size: 0.8rem;
  color: var(--primary);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 3px;
  cursor: pointer;
}
.ordered__arrow:disabled { opacity: 0.3; cursor: default; }
.ordered__arrow:not(:disabled):hover { border-color: var(--primary); }

.ordered__pool { display: flex; flex-direction: column; gap: 8px; }
.ordered__pool-label {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-soft);
}

/* review */
.ordered__review { list-style: none; margin: 0; padding: 0; }
.ordered__review-row {
  display: flex;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--line);
}
.ordered__review-row.is-right .ordered__rank { color: var(--correct); }
.ordered__review-row.is-wrong .ordered__rank { color: var(--incorrect); }
.ordered__review-body { display: flex; flex-direction: column; gap: 2px; }
.ordered__review-text { font-size: 0.95rem; }
.ordered__review-yours {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--incorrect);
}

/* ---------- fill in the blank ---------- */
.blanks { display: flex; flex-direction: column; gap: 16px; }
.blank { display: flex; flex-direction: column; gap: 6px; }
.blank__prompt {
  font-family: var(--font-display);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink-soft);
}
.blank__row { display: flex; align-items: center; gap: 10px; }
.blank__input {
  flex: 0 1 180px;
  min-height: 48px;
  padding: 10px 12px;
  font-size: 1.15rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  /* Heavier bottom rule: a value written onto a flowsheet line, which is
     what a dosage answer is. */
  border-bottom: 2px solid var(--primary);
  border-radius: 3px 3px 0 0;
}
.blank__input:focus {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
}
.blank__unit { font-size: 0.9rem; color: var(--ink-soft); }
.blank__hint { margin: 0; font-size: 0.78rem; color: var(--caution); }

/* review */
.blank-review {
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 3px;
  margin-bottom: 8px;
}
.blank-review.is-right { border-color: var(--correct); background: var(--correct-bg); }
.blank-review.is-wrong { border-color: var(--incorrect); background: var(--incorrect-bg); }
.blank-review__prompt {
  display: block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.85rem;
  margin-bottom: 4px;
}
.blank-review__reason {
  font-family: var(--font-mono);
  font-size: 0.74rem;
  color: var(--ink-soft);
}

/* ---------- shared review primitives ---------- */
.rv-line { display: flex; gap: 8px; align-items: baseline; font-size: 0.88rem; }
.rv-line__label {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  flex: 0 0 auto;
}
.rv-line__value { font-family: var(--font-mono); }
.rv-line--right .rv-line__value { color: var(--correct); }
.rv-line--wrong .rv-line__value { color: var(--incorrect); }
.rv-rationale {
  margin: 4px 0 0;
  font-size: 0.85rem;
  color: var(--ink-soft);
}

/* ---------- accessibility floor ---------- */
.option:focus-visible,
.ordered__item:focus-visible,
.ordered__arrow:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

@media (max-width: 520px) {
  .ordered__slot { gap: 6px; }
  .ordered__rank { flex-basis: 20px; }
  .ordered__item { font-size: 0.9rem; padding: 10px; }
  .blank__input { flex-basis: 100%; }
}

/* ---------- drag and drop ---------- */
.dnd { display: flex; flex-direction: column; gap: 16px; }

.dnd__label {
  margin: 0 0 8px;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-soft);
}

.dnd__pool {
  padding: 12px;
  border: 1px dashed var(--line);
  border-radius: 3px;
  background: var(--surface-alt);
}
.dnd__zones { display: flex; flex-direction: column; gap: 10px; }
.dnd__zone {
  padding: 12px;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: var(--surface);
}
.dnd__zone-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 8px;
}
.dnd__zone-label {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.9rem;
}
.dnd__zone-count { font-size: 0.72rem; color: var(--ink-soft); }

/* Armed (tap mode) and hovered-during-drag states are visually distinct:
   armed says "you may drop here", is-over says "release now". */
.dnd__pool.is-target, .dnd__zone.is-target { border-color: var(--primary); }
.dnd__pool.is-over, .dnd__zone.is-over {
  border-color: var(--primary);
  background: var(--primary-tint);
  box-shadow: inset 0 0 0 1px var(--primary);
}

.dnd__items { display: flex; flex-wrap: wrap; gap: 8px; min-height: 44px; }
.dnd__items--drop { align-items: flex-start; }
.dnd__empty {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--line);
  align-self: center;
}

.dnd__item {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 8px 12px;
  font-family: var(--font-body);
  font-size: 0.92rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 3px;
  cursor: grab;
  /* Without this the browser claims the gesture for scrolling and
     pointermove never fires on touch. */
  touch-action: none;
  user-select: none;
}
.dnd__item:hover { border-color: var(--primary); }
.dnd__item.is-armed {
  border-color: var(--primary);
  background: var(--primary-tint);
  box-shadow: 0 0 0 2px var(--primary);
}
.dnd__item.is-dragging { opacity: 0.35; }
.dnd__item:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.dnd__rank { font-size: 0.72rem; color: var(--primary); font-weight: 600; }

.dnd__ghost {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 999;
  /* Critical: without this, elementFromPoint under the finger returns
     the ghost and no zone is ever found. */
  pointer-events: none;
  transform: translate(-9999px, -9999px);
  margin-left: -20px;
  margin-top: -20px;
  padding: 8px 12px;
  background: var(--surface);
  border: 1px solid var(--primary);
  border-radius: 3px;
  box-shadow: 0 6px 18px rgba(var(--primary-rgb), 0.22);
  font-family: var(--font-body);
  font-size: 0.92rem;
  opacity: 0.95;
}

.dnd-review {
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 3px;
  margin-bottom: 8px;
}
.dnd-review.is-right { border-color: var(--correct); background: var(--correct-bg); }
.dnd-review.is-wrong { border-color: var(--incorrect); background: var(--incorrect-bg); }
.dnd-review__item {
  display: block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.9rem;
  margin-bottom: 4px;
}

@media (min-width: 620px) {
  .dnd__zones { flex-direction: row; flex-wrap: wrap; }
  .dnd__zone { flex: 1 1 calc(50% - 5px); min-width: 240px; }
}

/* =====================================================================
   Question navigator, submission gate, and save notices.
   Palette comes from the tokens at the top of this file — the three
   completion colours reuse --correct / --caution / --incorrect so a
   navigator cell means the same thing a review badge does.
   ===================================================================== */

.quiz-layout {
  display: grid;
  grid-template-columns: 180px minmax(0, 1fr);
  gap: 24px;
  align-items: start;
}

/* Exam mode has no navigator sidebar (no jumping between questions), so
   the layout collapses to a single column instead of leaving an empty
   180px gutter where the qnav aside used to be. */
.quiz-layout.is-exam-mode {
  grid-template-columns: minmax(0, 1fr);
}

.quiz-main { min-width: 0; }

.qnav {
  position: sticky;
  top: 16px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 14px;
}

.qnav__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 10px;
}

.qnav__title {
  font-family: var(--font-display);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.qnav__count { font-size: 12px; color: var(--ink-soft); }

/* Fixed-width cells in an auto-fill grid: adding a question rewraps the
   grid instead of resizing every cell, so a 12-question set and a
   200-question set look like the same control. Only this scrolls. */
.qnav__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, 34px);
  gap: 6px;
  max-height: 55vh;
  overflow-y: auto;
}

.qnav__cell {
  width: 34px;
  height: 34px;
  padding: 0;
  border-radius: 3px;
  border: 1px solid var(--line);
  background: var(--surface-alt);
  color: var(--ink-soft);
  font-family: var(--font-mono);
  font-size: 12px;
  cursor: pointer;
  transition: transform 80ms ease;
}

.qnav__cell:hover { transform: translateY(-1px); }

.qnav__cell.is-complete {
  background: var(--correct-bg);
  border-color: var(--correct);
  color: var(--correct);
}

.qnav__cell.is-partial {
  background: var(--caution-tint);
  border-color: var(--caution);
  color: var(--caution);
}

.qnav__cell.is-empty {
  background: var(--incorrect-bg);
  border-color: var(--incorrect);
  color: var(--incorrect);
}

/* The current question outranks its completion colour — you always need
   to be able to find where you are. */
.qnav__cell.is-current {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
  font-weight: 700;
}

.qnav__legend {
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 11px;
  color: var(--ink-soft);
}

.qnav__legend li { display: flex; align-items: center; gap: 6px; }

.qnav__swatch {
  width: 10px;
  height: 10px;
  border-radius: 2px;
  border: 1px solid var(--line);
  flex: none;
}

.qnav__swatch.is-complete { background: var(--correct-bg); border-color: var(--correct); }
.qnav__swatch.is-partial  { background: var(--caution-tint); border-color: var(--caution); }
.qnav__swatch.is-empty    { background: var(--incorrect-bg); border-color: var(--incorrect); }

.qnav__grid--modal {
  max-height: 160px;
  margin: 12px 0 4px;
  justify-content: center;
}

/* ---- submission gate ---- */

.modal-title {
  font-family: var(--font-display);
  font-size: 20px;
  margin: 0 0 8px;
}

.modal-actions {
  display: flex;
  gap: var(--control-gap-roomy);
  margin-top: 16px;
}

.modal-actions .btn { flex: 1; }

.link--quiet {
  /* No `color-scheme` is declared anywhere in this app, so every native
     <button> defaults to LIGHT chrome regardless of the page's own theme.
     .link--quiet's own properties below (small, muted, centered text) show
     the intent was always a plain text link, not a button — this just
     makes a <button class="link--quiet"> actually render that way instead
     of a native gray rectangle. `#fcDeckBack.link--quiet` further down
     intentionally overrides these (it's meant to look like a real button,
     not a text link) and wins on selector specificity regardless. */
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  cursor: pointer;
  display: block;
  width: 100%;
  margin-top: 12px;
  font-size: 12px;
  color: var(--ink-soft);
  text-align: center;
}

/* ---- anonymous save notices ---- */

.save-notice {
  border-left: 3px solid var(--caution);
  background: var(--caution-tint);
  color: var(--caution-ink);
  padding: 10px 14px;
  margin: 16px 0;
  font-size: 14px;
  line-height: 1.5;
  border-radius: 0 3px 3px 0;
}

.save-notice--slim {
  margin: 0 0 16px;
  padding: 8px 14px;
  font-size: 13px;
}

/* Below this the sidebar stops being a sidebar: it becomes a horizontal
   strip above the question, which is the only thing that fits a phone. */
@media (max-width: 780px) {
  .quiz-layout { grid-template-columns: 1fr; }
  .qnav { position: static; }
  .qnav__grid {
    grid-template-columns: repeat(auto-fill, 30px);
    max-height: 108px;
  }
  .qnav__cell { width: 30px; height: 30px; }
  .qnav__legend { flex-direction: row; flex-wrap: wrap; gap: 10px; }
}


/* ---------- set size ---------- */
.count-custom {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 10px;
}

.count-custom .input {
  width: 110px;
  padding: 10px 12px;
  border: 1.5px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
  font-size: 15px;
  color: var(--ink);
  transition: border-color 120ms ease, background 120ms ease, color 120ms ease;
}

/* Disabled is the resting state whenever a preset is selected. It has to read
   as clearly inert — a faint grey border alone was being mistaken for live. */
.count-custom .input:disabled {
  background: var(--neutral-tint);
  border-color: var(--neutral-tint-border);
  color: var(--muted-2);
  cursor: not-allowed;
}

.count-custom .hint {
  color: var(--muted-2);
}

.count-custom.is-active .hint {
  color: var(--primary);
  font-weight: 600;
}

/* Selected: a full-weight border in the accent colour plus a tinted fill, so
   the field reads as active without relying on the focus ring, which
   disappears the moment you click elsewhere. */
.count-custom.is-active .input {
  border-color: var(--primary);
  background: var(--primary-tint);
  color: var(--ink);
}

.count-custom .input:focus {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
}

.hint--quiet {
  color: var(--muted-3);
  font-size: 13px;
  margin-top: 12px;
}


/* ---------- modal close ---------- */
.modal-close {
  position: absolute;
  top: 12px;
  right: 14px;
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--muted-3);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
}

.modal-close:hover {
  background: var(--neutral-tint);
  color: var(--ink);
}

.modal-close:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
}

/* The card needs a positioning context for the close button, and room so the
   title does not run under it. */
.modal-card {
  position: relative;
}

.save-notice--inline {
  margin-top: 12px;
  margin-bottom: 4px;
}


/* ---------- password reveal ---------- */
.pw-field {
  position: relative;
  display: block;
}

.pw-field .field-input {
  width: 100%;
  padding-right: 46px; /* room for the toggle */
}

.pw-toggle {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  width: 34px;
  height: 34px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  cursor: pointer;
  padding: 0;
}

.pw-toggle:hover { background: var(--neutral-tint); }

.pw-toggle:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
}

.pw-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted-3);
}

.pw-toggle.is-on {
  color: var(--primary);
  background: var(--primary-tint-2);
}

/* Edge and Chrome inject their own reveal control inside password fields,
   which sat next to ours and gave two eyes — and the browser's one comes and
   goes on its own. Suppress theirs; ours is the one that is always present. */
.pw-field input::-ms-reveal,
.pw-field input::-ms-clear {
  display: none;
  width: 0;
  height: 0;
}

.pw-field input::-webkit-credentials-auto-fill-button,
.pw-field input::-webkit-strong-password-auto-fill-button {
  visibility: hidden;
  pointer-events: none;
  margin: 0;
  width: 0;
}

.hint--error { color: var(--error); }
.hint--ok { color: var(--primary); }

/* ---------- resume card ---------- */
.resume-card {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  align-items: center;
  justify-content: space-between;
  padding: 20px 22px;
  margin-bottom: 20px;
  border: 1.5px solid var(--primary);
  border-left-width: 5px;
  border-radius: 12px;
  background: var(--primary-tint);
}

.resume-card__text { flex: 1 1 280px; }

.resume-card__text h2 {
  margin: 4px 0 6px;
  font-size: 20px;
  color: var(--ink);
}

.resume-card__actions {
  display: flex;
  gap: var(--control-gap-roomy);
  flex-wrap: wrap;
}

/* ---------- brand as a button ---------- */
button.app-bar__mark {
  border: 0;
  background: transparent;
  cursor: pointer;
  padding: 0;
}

button.app-bar__mark:hover { opacity: 0.75; }

button.app-bar__mark:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
  border-radius: 4px;
}

/* ==================================================================
   Increment 5 — hotspot, highlight, case study.

   Mobile-first in the literal sense: every rule below is the 375px
   rule. There are exactly two min-width breakpoints in this block,
   and both were chosen from the content rather than from a table of
   device widths — 640px is where a hotspot image stops needing the
   full viewport, 900px is where a chart and a question can each hold
   a readable measure side by side.

   Nothing here reads window.innerWidth. Layout is CSS, state is JS,
   and the two never negotiate.
   ================================================================== */

/* Safe-area insets. viewport-fit=cover in index.html lets the page
   reach the edges of a notched phone; without these, the header sits
   under the notch and the submit bar sits under the home indicator. */
body {
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}

/* No horizontal scroll, structurally. Long lab values, unbroken URLs
   in a reference, and wide chart text are the three things that
   historically push a phone layout sideways. */
.hotspot,
.hl-passage,
.case,
.case__chart-text {
  max-width: 100%;
  overflow-wrap: anywhere;
}

/* ---------------- hotspot ---------------- */

.hotspot {
  position: relative;
  display: block;
  width: 100%;
  margin: 0 0 10px;
  border: 1px solid var(--line);
  border-radius: 10px;
  overflow: hidden;
  background: var(--surface-alt);
  /* Stops the double-tap-to-zoom delay and the accidental page pan
     while placing a marker. Pinch zoom still works. */
  touch-action: manipulation;
}

.hotspot__image {
  display: block;      /* kills the inline-element baseline gap that
                          would otherwise offset every marker by ~4px */
  width: 100%;
  height: auto;
  cursor: crosshair;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;  /* long-press must not offer "Save image" */
}

.hotspot__image:focus-visible {
  outline: 3px solid var(--primary);
  outline-offset: -3px;
}

/* Markers are positioned in percent and translated by half their own
   size, so they stay pinned to the same point on the picture through
   every resize, rotation and zoom without anything being recomputed. */
.hotspot__marker {
  position: absolute;
  width: 44px;
  height: 44px;
  margin: 0;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 3px solid var(--primary);
  background: rgba(var(--primary-rgb), 0.28);
  color: var(--on-primary);
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.hotspot__marker--ghost {
  border-style: dashed;
  background: rgba(var(--primary-rgb), 0.12);
  pointer-events: none;
}

.hotspot__marker--right { border-color: var(--correct); background: rgba(var(--correct-rgb), 0.3); }
.hotspot__marker--wrong { border-color: var(--incorrect); background: rgba(var(--incorrect-rgb), 0.3); }

.hotspot__region {
  position: absolute;
  pointer-events: none;
  border: 2px dashed var(--incorrect);
  background: rgba(var(--incorrect-rgb), 0.1);
}
.hotspot__region.is-correct {
  border: 2px solid var(--correct);
  background: rgba(var(--correct-rgb), 0.16);
}

.hotspot__status {
  margin: 0 0 4px;
  font-size: 0.82rem;
  color: var(--ink-soft);
}

/* On anything wider than a phone the image stops needing the whole
   column — a diagram blown up to 1200px is harder to read, not
   easier, and the markers get further apart than the anatomy. */
@media (min-width: 640px) {
  .hotspot { max-width: 520px; }
}

/* ---------------- highlight ---------------- */

.hl-passage {
  font-family: var(--font-body);
  font-size: 0.95rem;
  /* The whole mobile answer for this type. 2.5 gives each padded
     phrase a ~46px band without the type changing size, and keeps
     stacked lines from overlapping when a phrase wraps. */
  line-height: 2.5;
  background: var(--surface-alt);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 14px 16px;
  margin: 0 0 10px;
}

.hl-fixed { color: var(--ink); }

.hl-seg {
  display: inline;
  padding: 0.55rem 0.3rem;   /* ~44px tap band at this line-height */
  margin: 0 -0.05rem;
  border-radius: 4px;
  background: rgba(var(--primary-rgb), 0.07);
  box-shadow: inset 0 -2px 0 var(--line);
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

.hl-seg:hover { background: rgba(var(--primary-rgb), 0.13); }
.hl-seg:focus-visible { outline: 3px solid var(--primary); outline-offset: 1px; }

.hl-seg.is-picked {
  background: var(--highlight-yellow);
  box-shadow: inset 0 -2px 0 var(--caution);
  font-weight: 600;
}

.hl-seg--review { cursor: default; }
.hl-seg--review.is-key { box-shadow: inset 0 -3px 0 var(--correct); }
.hl-seg--review.is-hit { background: var(--correct-bg); }
.hl-seg--review.is-false-positive {
  background: var(--incorrect-bg);
  box-shadow: inset 0 -3px 0 var(--incorrect);
  text-decoration: line-through;
}

/* ---------------- case study ---------------- */

.case {
  display: flex;
  flex-direction: column;   /* the 375px layout: chart on top, step below */
  gap: 14px;
}

.case__chart {
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface);
  overflow: hidden;
}

.case__tabs {
  display: flex;
  overflow-x: auto;          /* four tabs do not fit across 375px */
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  background: var(--surface-alt);
  border-bottom: 1px solid var(--line);
}

.case__tab {
  flex: 0 0 auto;
  min-height: 44px;          /* touch target */
  padding: 10px 14px;
  border: 0;
  border-bottom: 3px solid transparent;
  background: transparent;
  font-family: var(--font-display);
  font-size: 0.82rem;
  letter-spacing: 0.02em;
  color: var(--ink-soft);
  cursor: pointer;
  white-space: nowrap;
}

.case__tab.is-active {
  color: var(--primary);
  border-bottom-color: var(--primary);
  background: var(--surface);
  font-weight: 600;
}

.case__chart-body { padding: 12px 14px; max-height: 40vh; overflow-y: auto; }

.case__chart-text {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  line-height: 1.6;
  color: var(--ink);
  /* pre-wrap keeps the chart's line breaks — a nurses' note is
     timestamped entries, not a paragraph — while still wrapping
     rather than scrolling sideways at 375px. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.case__steps { display: flex; flex-direction: column; gap: 14px; }

.case-step {
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface);
  padding: 12px 14px;
}

.case-step.is-hidden { opacity: 0.55; background: var(--surface-alt); }
.case-step.is-open { border-color: var(--primary); }

.case-step__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.case-step__number {
  font-family: var(--font-display);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.case-step__cjmm {
  font-family: var(--font-display);
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  padding: 3px 8px;
  border-radius: 999px;
  background: var(--surface-alt);
  color: var(--primary);
}

.case-step__done {
  font-family: var(--font-display);
  font-size: 0.7rem;
  padding: 3px 8px;
  border-radius: 999px;
  background: var(--correct-bg);
  color: var(--correct);
}

.case-step__context { margin: 0 0 8px; font-size: 0.88rem; color: var(--ink-soft); }
.case-step__stem { margin: 0 0 10px; font-size: 0.95rem; font-weight: 600; }
.case-step__locked { margin: 0; font-size: 0.82rem; color: var(--ink-soft); font-style: italic; }
.case-step__body.is-locked { opacity: 0.6; pointer-events: none; }

.case-review {
  border: 1px solid var(--line);
  border-left-width: 4px;
  border-radius: 8px;
  padding: 10px 12px;
  margin-bottom: 10px;
  background: var(--surface);
}
.case-review.is-right { border-left-color: var(--correct); }
.case-review.is-wrong { border-left-color: var(--incorrect); }
.case-review__head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.case-review__verdict { margin-left: auto; font-family: var(--font-display); font-size: 0.75rem; }
.case-review__body { margin-top: 8px; }
.case__chart-review { margin-bottom: 10px; }
.case__chart-summary { cursor: pointer; font-family: var(--font-display); font-size: 0.82rem; padding: 6px 0; }

/* Two columns only once both panes can hold a readable measure. Below
   this the chart would be a 40-character column of monospace, which is
   worse than stacking. */
@media (min-width: 900px) {
  .case {
    flex-direction: row;
    align-items: flex-start;
  }
  .case__chart {
    flex: 0 0 42%;
    position: sticky;
    top: 12px;   /* the chart is referred to constantly; keep it in view */
  }
  .case__steps { flex: 1 1 auto; min-width: 0; }
  .case__chart-body { max-height: 60vh; }
}

/* ---------------- shared: pointer-coarse touch targets ---------------- */
/* Keyed to the input device, not the screen width. A 1024px tablet is a
   touch device and a 600px desktop window is not; a width breakpoint
   gets both of those backwards. */
@media (pointer: coarse) {
  .case__tab { min-height: 48px; }
  .hl-seg { padding: 0.62rem 0.35rem; }
  .hotspot__marker { width: 48px; height: 48px; }
}

@media (prefers-reduced-motion: reduce) {
  .hotspot__marker,
  .hl-seg,
  .case__tab { transition: none !important; }
}

.history-row__format {
  font-size: 0.75rem;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 3px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.badge--study { background: var(--surface); border: 1px solid var(--line); }
.badge--exam  { background: var(--exam-badge-bg); border: 1px solid var(--exam-badge-border); color: var(--exam-badge-ink); }
/* =====================================================================
   Increment 7 — analytics dashboard.

   Appended, same convention as increment 3's block: nothing above this
   line is touched. Every class here either reuses an existing pattern
   verbatim (.breakdown-row for accuracy bars, .history-row + badge--*
   for the trend list and study/exam labels) or is new only where
   nothing existing fit (stat tiles, the study/exam comparison tiles).
   ===================================================================== */

.stats-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 8px;
}
.stat-tile {
  flex: 1 1 140px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 16px;
  text-align: center;
}
.stat-tile__value {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.6rem;
  color: var(--primary-dark);
  line-height: 1.1;
}
.stat-tile__label {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  margin-top: 6px;
}

.format-compare {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}
.format-compare__tile {
  flex: 1 1 180px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 16px;
}
.format-compare__tile .history-row__format { margin-bottom: 10px; display: inline-block; }
.format-compare__stat {
  font-family: var(--font-body);
  font-size: 0.88rem;
  color: var(--ink);
  margin-top: 4px;
}

#analyticsBackBtn { margin-top: 20px; }

@media (max-width: 520px) {
  .stat-tile { flex: 1 1 calc(50% - 6px); }
}

/* ---------- increment 7 fix: app-bar crowding at phone width ---------- */
/* .app-bar has no @media handling anywhere in this file — it was
   already tight with just "History" in the auth widget; adding
   "Analytics" pushed it over. At narrow widths .app-bar__sub (the
   decorative tagline) gets squeezed into a multi-line stack next to
   the logo. The tagline carries no function, so it's the one thing
   safe to drop rather than touching the logo or the nav links. */
@media (max-width: 520px) {
  .app-bar__sub { display: none; }
}

/* ---------- increment 7 fix: accuracy bars not rendering ---------- */
/* .breakdown-row__fill is a <span> with no display set, so it defaults
   to display:inline — and CSS ignores width AND height on inline,
   non-replaced elements. Both were being set (height:100% in the
   original rule, width:X% inline per row) and both were silently
   dropped, so the fill rendered as an invisible zero-size box on
   every row regardless of the actual percentage. This selector is
   UNSCOPED (plain .breakdown-row__fill, no screen prefix), so it
   applies everywhere that class is used — which includes the
   pre-existing Results screen category breakdown, since it is the
   same class name. Verified in the browser: the Results screen bar
   already renders correctly off this one rule. An earlier version of
   this comment claimed Results was still broken and "out of scope" —
   that was never re-checked after this rule landed, and it was wrong. */
.breakdown-row__fill { display: block; }

/* ---------- increment 8: progress screen ---------- */
/* Reuses .breakdown-row / .stat-tile / .format-compare__tile verbatim so
   the Progress screen inherits every fix those classes already carry
   (including the display:block on .breakdown-row__fill, which is the one
   that bit both Results and Analytics). Only genuinely new shapes get
   new classes below. */

.level-card {
  background: var(--surface, var(--surface));
  border: 1px solid var(--line, #e5e5e5);
  border-radius: 14px;
  padding: 18px;
  margin-bottom: 18px;
}
.level-card__top {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 12px;
  margin-bottom: 10px;
}
.level-card__rank {
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.level-card__level { font-size: 1.35rem; font-weight: 600; }
.level-card__xp { font-size: 1.05rem; color: var(--ink-soft); }
.level-card .breakdown-row__bar { display: block; margin: 8px 0 6px; }

.readiness { margin-bottom: 6px; }
/* The one number on this card worth making pop — --correct is already the
   mint hue family in dark mode (a readable dark green in light mode, for
   the same reason --primary doesn't just paint --primary raw as text). */
.readiness__score { font-size: 2.6rem; font-weight: 600; line-height: 1.1; color: var(--correct); }
.readiness__band {
  font-size: 0.85rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 8px;
}
.readiness .breakdown-row__bar { display: block; margin-bottom: 8px; }
.readiness__msg { margin: 0 0 6px; }

.badge-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.badge-chip {
  flex: 1 1 calc(33.333% - 7px);
  min-width: 170px;
  border: 1px solid var(--line, #e5e5e5);
  border-radius: 12px;
  padding: 12px;
  opacity: 0.5;
}
.badge-chip--earned {
  opacity: 1;
  border-color: var(--primary);
}
.badge-chip__name { font-weight: 600; margin-bottom: 3px; }
.badge-chip__desc { font-size: 0.85rem; color: var(--ink-soft); }
.badge-chip__progress { font-size: 0.8rem; margin-top: 6px; color: var(--ink-soft); }

.area-name { color: var(--ink-soft); }

/* ---------- gamification celebration toasts ---------- */
/* Fixed stack, bottom-right, above everything else. Click-to-dismiss early;
   otherwise a fixed read window is enough since this is a short title +
   one line, not something the learner needs to act on. Bottom offset
   cleared to sit ABOVE the theme toggle, which is fixed in the same
   corner and — unlike a toast — never goes away. */
.gami-toast-host {
  position: fixed;
  right: 18px;
  bottom: 68px;
  z-index: 900;
  display: flex;
  flex-direction: column-reverse;
  gap: 10px;
  max-width: min(320px, calc(100vw - 36px));
  pointer-events: none;
}
.gami-toast {
  pointer-events: auto;
  cursor: pointer;
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--primary);
  border-radius: 8px;
  padding: 12px 14px;
  box-shadow: var(--shadow-md);
  animation: gami-toast-in var(--dur-modal) var(--ease-standard);
}
.gami-toast.is-leaving { animation: gami-toast-out var(--dur-card) var(--ease-standard) forwards; }
@keyframes gami-toast-in {
  from { opacity: 0; transform: translateY(10px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes gami-toast-out {
  to { opacity: 0; transform: translateY(6px) scale(0.98); }
}
.gami-toast--level { border-left-color: var(--accent-gold); }
.gami-toast--badge { border-left-color: var(--accent-gold); }
/* An XP gain is a positive event, not a brand-emphasis moment — --level/
   --badge stay gold (bigger milestones already own that color), --xp
   moves to mint alongside the other positive/progress accents. */
.gami-toast--xp    { border-left-color: var(--correct); }
.gami-toast--flashcard { border-left-color: var(--muted-3, var(--ink-soft)); }
.gami-toast__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.94rem;
  margin-bottom: 2px;
}
.gami-toast--level .gami-toast__title,
.gami-toast--badge .gami-toast__title { color: var(--accent-gold); }
.gami-toast__body { font-size: 0.84rem; color: var(--ink-soft); }
@media (max-width: 520px) {
  .gami-toast-host { left: 18px; right: 18px; max-width: none; }
}

/* Badges reveal with the same stagger discipline as the review list, so
   opening Progress after unlocking several at once doesn't feel like a
   wall of boxes appearing at once. */
.badge-chip {
  animation: review-reveal var(--dur-page) var(--ease-standard) backwards;
}
.badge-chip:nth-child(1) { animation-delay: 0ms; }
.badge-chip:nth-child(2) { animation-delay: 40ms; }
.badge-chip:nth-child(3) { animation-delay: 80ms; }
.badge-chip:nth-child(4) { animation-delay: 120ms; }
.badge-chip:nth-child(5) { animation-delay: 160ms; }
.badge-chip:nth-child(n+6) { animation-delay: 200ms; }

/* Phone width. This rule used to hide the username below 640px, back when
   the app bar carried Progress, Analytics and History as their own links and
   the username was decoration you could drop for space.

   That is no longer true. The username IS the button that opens the account
   screen, and the account screen is the ONLY route to Progress, Analytics and
   History. Hiding it on a phone made all three unreachable after logging in.
   It must stay visible at every width — the space instead comes from the
   remaining links collapsing to icons below 520px. */
@media (max-width: 640px) {
  .app-bar__auth { gap: var(--control-gap); }
}
@media (max-width: 520px) {
  .badge-chip { flex: 1 1 100%; }
  .readiness__score { font-size: 2.1rem; }
  .level-card { padding: 14px; }
}

/* ---- interactive anatomy (3D study module) ----
   Two-column on desktop: model left, controls right. Stacks on phones
   with the canvas capped by viewport height, because a 3D canvas that
   grows with its container will happily fill a phone screen and push
   every control below the fold. */
.anat {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 340px;
  /* "detail" (the Learn-mode selected-structure card) sits directly under
     the model, in the same column, rather than at the end of .anat__panel's
     long filters/tools column — repeating "panel" in both rows makes it
     span the full height of the right column, unchanged from before this
     card existed. */
  grid-template-areas:
    "stage  panel"
    "detail panel";
  gap: 18px;
  align-items: start;
}
.anat__stage { grid-area: stage; }
.anat__panel { grid-area: panel; }

.anat__stage {
  position: relative;
  background: linear-gradient(180deg, var(--stage-grad-1) 0%, var(--stage-grad-2) 100%);
  border: 1px solid var(--line);
  border-radius: 14px;
  overflow: hidden;
}

.anat__canvas {
  display: block;      /* an inline canvas leaves a baseline gap */
  width: 100%;
  height: min(70vh, 620px);
  touch-action: none;  /* we handle drag/pinch ourselves */
  cursor: grab;
}
.anat__viewport-control {
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  z-index: 2;
  display: grid;
  gap: var(--space-1);
  width: min(180px, 42%);
  padding: var(--space-2);
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(var(--surface-rgb), 0.9);
  color: var(--ink-soft);
  font-size: 0.7rem;
}
.anat__viewport-control input { width: 100%; accent-color: var(--primary); }
.anat__debug-gestures {
  position: absolute;
  right: var(--space-3);
  bottom: var(--space-5);
  z-index: 3;
  display: flex;
  gap: var(--control-gap);
}
.anat__debug-gestures button {
  min-height: 44px;
  border: 1px solid var(--primary);
  border-radius: 6px;
  background: var(--surface);
  color: var(--primary-dark);
}

.anat__hover {
  position: absolute;
  left: 12px; top: 12px;
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--ink);
  background: rgba(var(--surface-rgb), 0.86);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 5px 10px;
  min-height: 1em;
  pointer-events: none;
}
.anat__hover:empty { display: none; }

/* Positioned via JS (viewer.js re-projects the focused structure's world
   position every frame; index.js sets the transform). top:0/left:0 here is
   the anchor the translate() is relative to — the label itself never
   participates in layout flow, so re-positioning it costs one composited
   transform, not a reflow. */
.anat__focus-label {
  position: absolute;
  top: 0; left: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.86rem;
  color: var(--ink);
  background: rgba(var(--surface-rgb), 0.94);
  border: 1px solid var(--primary);
  border-radius: 6px;
  padding: 4px 10px;
  white-space: nowrap;
  pointer-events: none;
  box-shadow: var(--shadow-sm);
}
.anat__focus-label::after {
  content: "";
  position: absolute;
  left: 50%; bottom: -5px;
  width: 8px; height: 8px;
  background: inherit;
  border-right: 1px solid var(--primary);
  border-bottom: 1px solid var(--primary);
  transform: translateX(-50%) rotate(45deg);
}

/* Base look now comes entirely from .btn/.btn--ghost/.btn--compact (see
   index.html) — same family as Clear/Reset. This ID rule only layers the
   ON-state tint on top; it used to also own shape/padding/radius, which
   fought the shared button classes and left the pill's OFF state with no
   background at all (native browser gray, regardless of theme). */
#anatomyXray.is-active {
  border-color: var(--primary);
  color: var(--primary);
  background: var(--primary-tint);
}

.anat__disclaimer {
  position: absolute;
  left: 12px; right: 12px; bottom: 8px;
  margin: 0;
  font-size: 0.68rem;
  line-height: 1.35;
  color: var(--ink-soft);
  text-align: center;
  pointer-events: none;
}

.anat__panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 18px;
}
.anat__panel h1 { margin-top: 0; font-size: 1.35rem; }

.anat__section-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--control-gap);
  margin: 0 0 var(--space-4);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--line);
}
.anat__mode-tab {
  flex: 1;
  min-height: 44px;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink-soft);
  font: 600 0.9rem var(--font-display);
  cursor: pointer;
}
.anat__mode-tab.is-active,
.anat__mode-tab[aria-selected="true"] {
  border-color: var(--primary);
  background: var(--primary-tint);
  color: var(--primary-dark);
  box-shadow: inset 0 0 0 1px var(--primary);
}
.anat__mode-description { margin: 0 0 var(--space-4); }
.anat__learn-list {
  display: grid;
  gap: var(--space-1);
  max-height: 190px;
  margin: var(--space-2) 0 var(--space-3);
  overflow-y: auto;
}
.anat__learn-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  min-height: 44px;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.anat__learn-item small { color: var(--ink-soft); white-space: nowrap; }
.anat__learn-item:hover,
.anat__learn-item.is-active { border-color: var(--primary); background: var(--primary-tint); }
.anat__learn-detail {
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface-alt);
  /* No max-width here: this card shares .anat__stage's grid column
     (grid-template-areas above), and its outer border/background must
     track the model's full width exactly — a narrower card reads as
     misaligned against the stage above it. Readable line length is
     constrained on the inner .anat__learn-detail__prose wrapper instead,
     which narrows the TEXT without narrowing the card. */
}
.anat__learn-detail__prose { max-width: 62ch; }
.anat__learn-detail h2 { margin: 0; font-size: 1.15rem; }
/* --space-1 (4px) reads as touching under "selected structure" — this
   card used to sit deep in a busy filters column where that was easy to
   miss; now that it stands alone directly under the model, the gap has
   to carry the whole visual separation on its own. */
.anat__learn-detail .eyebrow { margin-bottom: var(--space-2); }
.anat__learn-detail .anat-fact { margin-top: var(--space-3); }
#anatomySaveFlashcard { width: auto; margin-top: var(--space-2); }

/* Phone gets a dialog instead (#anatomyLearnModal) — see renderLearnDetail()
   in anatomy/index.js. Without this the same content would render twice:
   once in the permanent inline card, once in the popup. */
@media (max-width: 520px) {
  .anat__learn-detail { display: none; }
}

.anat__detail-modal .modal-card {
  max-width: 460px;
  max-height: 80vh;
  overflow-y: auto;
}
.anat__detail-modal .modal-card .eyebrow { margin-bottom: var(--space-2); }
.anat__detail-modal .modal-card h2 { margin: 0 28px 0 0; font-size: 1.15rem; }

.anat__tools {
  display: flex;
  align-items: center;
  gap: var(--control-gap);
  flex-wrap: wrap;
  margin-bottom: 8px;
}

.anat__selected { max-height: 168px; overflow-y: auto; }

.chip--static { cursor: default; }

.anat__action-row { display: flex; gap: var(--control-gap-roomy); }
.anat__action-row .btn { flex: 1; }

.anat__progress {
  font-family: var(--font-mono);
  font-size: 1.1rem;
  color: var(--ink);
  margin: 0 0 4px;
}

.anat__score { font-size: 2.6rem; margin: 0; }
.anat__xp { font-family: var(--font-mono); color: var(--correct); margin: 4px 0; }

.anat__results { max-height: 46vh; overflow-y: auto; margin: 12px 0; }

.anat-result {
  border: 1px solid var(--line);
  border-left-width: 4px;
  border-radius: 10px;
  padding: 10px 12px;
  margin-bottom: 8px;
  font-size: 0.86rem;
}
.anat-result.is-correct { border-left-color: var(--correct); background: var(--correct-bg); }
.anat-result.is-wrong   { border-left-color: var(--incorrect); background: var(--incorrect-bg); }
.anat-result { cursor: pointer; }
.anat-result.is-focused { outline: 2px solid var(--primary); outline-offset: 2px; }

.anat-result__head {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  align-items: baseline;
  margin-bottom: 4px;
}
.anat-result__verdict {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--ink-soft);
  text-align: right;
}
.anat-result__note { margin: 6px 0 0; color: var(--ink-soft); }

/* Reveal panel: three labelled sections instead of one prose blob. The rule
   is that a section with no content renders nothing at all, so the spacing
   lives on the section rather than between siblings. */
.anat-fact { margin-top: 10px; }
.anat-fact__head {
  margin: 0 0 4px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.anat-fact__row { display: flex; gap: 6px; margin-bottom: 2px; line-height: 1.45; }
.anat-fact__label { flex: none; font-weight: 600; }
.anat-fact__value { color: var(--ink-soft); }
.anat-fact__prose { margin: 0; color: var(--ink-soft); line-height: 1.5; }
.anat-fact__aliases {
  margin: 0;
  color: var(--ink-soft);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.85em;
}
/* On a phone the label/value pair wraps rather than squeezing the value into
   a narrow column. */
@media (max-width: 520px) {
  .anat-fact__row { flex-direction: column; gap: 0; }
}

@media (max-width: 900px) {
  .anat {
    grid-template-columns: 1fr;
    grid-template-areas: "stage" "detail" "panel";
  }
  .anat__canvas { height: min(52vh, 440px); }
}

@media (max-width: 520px) {
  .anat__panel { padding: 14px; }
  .anat__canvas { height: 46vh; }
  .anat__disclaimer { font-size: 0.62rem; }
  /* .anat__learn-detail is display:none at this width (popup takes over —
     see above), so the "detail" row track is dropped entirely rather than
     leaving an empty row whose `gap` would still show. */
  .anat { grid-template-areas: "stage" "panel"; }
}

@media (pointer: coarse) {
  /* Bigger answer target on phones — this input is typed into once per
     question, so it earns the space. */
  #anatomyAnswer { font-size: 1rem; padding: 12px; }
}

/* The shared .screen cap of 720px is right for reading a question and
   wrong for a 3D model beside a control panel. */
#screen-anatomy { max-width: 1180px; }

/* ---- header nav items (icon + label) ----
   Both are always in the DOM; the label is hidden on a narrow portrait
   phone so the bar degrades to icons instead of wrapping into an
   unreadable stack. The username deliberately keeps its text — it is
   identity, not a control, and an avatar would say less. */
.nav-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  color: var(--ink-soft);
  background: none;
  border: none;
  font: inherit;
  padding: 6px 4px;
}
.nav-item:hover { color: var(--primary-dark); }
.nav-item__ico { display: none; }
.nav-item__ico svg { width: 22px; height: 22px; display: block; }

/* The username is now a control, so it must look like one.
   Selector is scoped to .app-bar__auth deliberately: the older
   `.app-bar__auth .app-bar__user { color: var(--ink-soft) }` rule higher up
   is two classes, so a single-class rule here loses on specificity and the
   username renders as grey body text — which is most of why it reads as a
   label rather than a button. */
.app-bar__auth .app-bar__user {
  font-weight: 700;
  color: var(--primary-dark);
  background: none;
  border: none;
  font-family: inherit;
  font-size: inherit;
  cursor: pointer;
  padding: 6px 4px;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 2px;
}
.app-bar__auth .app-bar__user:hover { color: var(--primary); }
/* A long username must not push the icons off a 360px screen. */
.app-bar__auth .app-bar__user {
  max-width: 9ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---- account screen ---- */
.account-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
  margin-bottom: 24px;
}
.account-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  text-align: left;
  padding: 20px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  cursor: pointer;
  font: inherit;
  color: var(--ink);
}
.account-card:hover { border-color: var(--primary); }
.account-card__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.1rem;
  color: var(--primary-dark);
}
.account-card__sub {
  font-family: var(--font-mono);
  font-size: 0.74rem;
  color: var(--ink-soft);
}
.account-card--danger { border-color: color-mix(in srgb, var(--incorrect) 45%, var(--line)); }
.account-card--danger .account-card__title { color: var(--incorrect); }
.delete-account-modal { max-width: 540px; }
.delete-account-modal > p { color: var(--ink-soft); line-height: 1.55; }
.account-deletion-success { width: min(780px, calc(100% - 40px)); margin: 16px auto; }

/* Return-to-quiz from the Anatomy screen. Hidden unless an attempt is
   actually open — a dead button is worse than no button. */
.anat__back { margin-bottom: 14px; width: 100%; }

@media (max-width: 520px) {
  /* Portrait phone: icons only. Landscape is wider than this and keeps
     the labels, which is the desktop behaviour. */
  .nav-item__label {
    position: absolute;
    width: 1px; height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  .nav-item__ico { display: block; }
  .nav-item { padding: 8px 6px; gap: 0; }
  .app-bar__auth { gap: var(--control-gap); }
  .app-bar__auth .app-bar__user { font-size: 0.9rem; max-width: 8ch; }
}

@media (pointer: coarse) {
  .nav-item { min-width: 40px; min-height: 40px; justify-content: center; }
}

/* ---- mock exam (PNLE phase 5) ---- */

.mock-entry { margin-top: 18px; }
.mock-actions { display: flex; gap: var(--control-gap-roomy); flex-wrap: wrap; align-items: center; }

/* The coverage panel. Deliberately loud when the bank is thin: the whole
   point of this screen is that a partial set is never mistaken for a board
   simulation, so the warning is not a hint-grey footnote. */
.coverage {
  margin: 18px 0;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface-alt);
}
.coverage h3 { margin: 0 0 8px; font-size: 0.95rem; }
.coverage__headline { margin: 0 0 8px; font-weight: 600; }

.coverage__bar {
  height: 8px;
  border-radius: 999px;
  background: var(--line);
  overflow: hidden;
}
/* display:block matters — it is a <span>, and CSS ignores width on inline
   elements. This exact bug hit the results and analytics accuracy bars. */
.coverage__bar > span {
  display: block;
  height: 100%;
  width: 0;
  border-radius: 999px;
  transition: width 160ms ease;
  background: var(--primary);
}
.coverage__bar > span.is-complete { background: var(--correct); }
.coverage__bar > span.is-partial  { background: var(--caution); }
.coverage__bar > span.is-thin     { background: var(--incorrect); }

.coverage__warning {
  margin: 10px 0 0;
  padding: 10px 12px;
  border-left: 3px solid var(--incorrect);
  border-radius: 4px;
  background: var(--incorrect-bg);
  color: var(--ink);
  font-weight: 500;
  line-height: 1.5;
}

.coverage__gaps {
  margin-top: 8px;
  max-height: 220px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.coverage__gap {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 3px 0;
  font-size: 0.86rem;
  color: var(--ink-soft);
  border-bottom: 1px solid var(--line);
}
.coverage details > summary {
  margin-top: 10px;
  cursor: pointer;
  font-size: 0.86rem;
  color: var(--ink-soft);
}

/* On the results screen the panel sits directly under the score, so it
   loses its own heading and tightens up. */
.coverage--result { margin-top: 0; }

@media (max-width: 520px) {
  .coverage { padding: 12px; }
  .mock-actions { flex-direction: column; align-items: stretch; }
  .coverage__gap { font-size: 0.8rem; }
}

/* ---- flashcards (Foundation phase) ---- */

.fc-deck-row {
  display: flex;
  align-items: center;
  gap: var(--control-gap);
  width: 100%;
  margin-bottom: 8px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
  font-family: var(--font-body);
  color: var(--ink);
  transition: border-color var(--dur-card) var(--ease-standard), transform var(--dur-card) var(--ease-standard);
}
@media (hover: hover) and (pointer: fine) {
  .fc-deck-row:hover { border-color: var(--primary); transform: translateY(-1px); }
}
.fc-deck-row__open {
  flex: 1;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  min-width: 0;
  text-align: left;
  padding: 14px 16px;
  border: 0;
  background: none;
  cursor: pointer;
  font-family: inherit;
  color: inherit;
}
.fc-deck-row__name { font-weight: 600; }
.fc-deck-row__meta { font-family: var(--font-mono); font-size: 0.76rem; color: var(--ink-soft); white-space: nowrap; }
.fc-deck-row__actions { display: flex; gap: var(--control-gap); padding-right: var(--space-3); flex-shrink: 0; }

.fc-deck-select {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  margin-left: var(--space-1);
  cursor: pointer;
}
.fc-deck-select input { width: 18px; height: 18px; accent-color: var(--primary); }
.fc-deck-row.is-selected { border-color: var(--primary); background: var(--primary-tint); }
.fc-deck-bulk {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--control-gap);
  padding: var(--space-3);
  margin-bottom: var(--space-3);
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface-alt);
}
.fc-deck-bulk > span { flex: 1 1 180px; font-weight: 600; }
@media (pointer: coarse) {
  .fc-deck-select { display: none; }
  .fc-deck-list.is-selecting .fc-deck-select { display: inline-flex; }
}

.fc-search {
  display: block;
  max-width: 680px;
  margin: var(--space-4) 0;
}
.fc-search .field-label { margin-bottom: var(--space-2); }

.fc-library-actions {
  display: grid;
  grid-template-columns: repeat(5, minmax(72px, 1fr));
  gap: var(--control-gap);
  max-width: 680px;
  margin-bottom: var(--space-3);
}
.fc-library-actions #fcPracticeBtn { grid-column: 1 / -1; }
.fc-subject-btn.is-selected,
.fc-subject-btn[aria-pressed="true"] {
  border-color: var(--primary);
  background: var(--primary-tint);
  color: var(--primary-dark);
  box-shadow: inset 0 0 0 1px var(--primary);
}
.fc-subject-start {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--control-gap);
  max-width: 680px;
  margin-bottom: var(--space-4);
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface-alt);
}
.fc-subject-start .hint { margin: 0; }

.fc-timed-reveal {
  max-width: 680px;
  margin: var(--space-4) 0;
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface-alt);
}
.fc-timed-reveal legend {
  padding: 0 var(--space-2);
  margin-left: var(--space-1);
  background: var(--surface-alt);
  font-family: var(--font-display);
  font-weight: 700;
}
.fc-timed-reveal__toggle { display: flex; align-items: center; gap: var(--control-gap); min-height: 44px; cursor: pointer; }
.fc-timed-reveal__toggle input { width: 18px; height: 18px; accent-color: var(--primary); }
.fc-timed-reveal__options { display: flex; align-items: center; flex-wrap: wrap; gap: var(--control-gap); margin-top: var(--space-2); }
.fc-timed-reveal__label { color: var(--ink-soft); font-size: 0.86rem; }
.fc-timed-reveal__chip {
  min-width: 48px;
  min-height: 40px;
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}
.fc-timed-reveal__chip.is-selected,
.fc-timed-reveal__chip[aria-pressed="true"] { border-color: var(--primary); background: var(--primary-tint); color: var(--primary-dark); box-shadow: inset 0 0 0 1px var(--primary); }

/* The generic quiet-link pattern is intentionally full-width. This back
   control is a compact button instead, with token-driven foreground,
   background and border so it stays legible in both themes. */
#fcDeckBack.link--quiet {
  display: inline-flex;
  align-items: center;
  width: auto;
  min-height: 44px;
  margin: var(--space-5) 0 var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--surface-alt);
  color: var(--ink);
  text-align: left;
}
#fcDeckBack.link--quiet:hover {
  border-color: var(--primary);
  color: var(--primary-dark);
}

.fc-card-row {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  border-bottom: 1px solid var(--line);
}
.fc-card-row__summary {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: 52px minmax(0, 1fr) 28px;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: 52px;
  padding: var(--space-3) var(--space-2);
  border: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.fc-card-row__summary:hover { background: var(--primary-tint); }
.fc-card-row.is-expanded { background: var(--surface-alt); }
.fc-card-row__type {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
  flex-shrink: 0;
  width: 52px;
}
.fc-card-row__front { flex: 1; font-size: 0.9rem; }
.fc-card-row__chevron {
  color: var(--primary-dark);
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 700;
  text-align: center;
}
.fc-card-row__details {
  grid-column: 1 / -1;
  padding: 0 var(--space-4) var(--space-4) calc(52px + var(--space-5));
  color: var(--ink);
  line-height: 1.55;
}
.fc-card-row__details > :first-child { margin-top: 0; }
.fc-card-row__details > :last-child { margin-bottom: 0; }
.fc-card-row > [data-remove] {
  grid-column: 1 / -1;
  justify-self: end;
  width: auto;
  min-height: 44px;
  margin: 0 var(--space-2) var(--space-2);
}

@media (max-width: 560px) {
  .fc-library-actions { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fc-library-actions #fcPracticeBtn { grid-column: 1 / -1; }
  .fc-library-actions .fc-subject-btn:last-child { grid-column: 1 / -1; }
  .fc-card-row__summary { grid-template-columns: 46px minmax(0, 1fr) 24px; }
  .fc-card-row__details { padding-left: var(--space-3); }
}

/* Everything in a study session — progress line, card, rate row, hints —
   shares ONE max-width/centering here rather than each element picking
   its own. Without this the rate row (full-width, no constraint of its
   own) rendered WIDER than the 480px card above it and started further
   left, so the row looked misaligned with the card it belongs to, and
   "Again" (the first, leftmost button) ended up sitting oddly off to the
   side instead of lining up under the card's own left edge. */
.fc-study-wrap { width: min(100%, 680px); margin: 0 auto; }
.fc-timer { margin: var(--space-3) 0; padding: var(--space-2) var(--space-3); border: 1px solid var(--line); border-radius: 10px; background: var(--surface-alt); }
.fc-timer__heading { display: flex; justify-content: space-between; gap: var(--control-gap); color: var(--ink-soft); font-size: 0.82rem; }
.fc-timer__heading span:last-child { color: var(--ink); font-family: var(--font-mono); font-weight: 700; }
.fc-timer__track { height: 6px; margin: var(--space-2) 0; overflow: hidden; border-radius: 999px; background: var(--line); }
.fc-timer__track span { display: block; width: 100%; height: 100%; border-radius: inherit; background: var(--correct); }
.fc-timer__actions { display: flex; gap: var(--control-gap); }
.fc-timer__actions .btn { min-height: 40px; padding: var(--space-1) var(--space-3); }
.fc-complete { padding: clamp(var(--space-4), 6vw, var(--space-7)) 0; text-align: center; }
.fc-complete h2 { margin-bottom: var(--space-2); }
.fc-complete__summary { margin: 0 auto var(--space-5); color: var(--ink-soft); }
.fc-complete__stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--control-gap); margin: 0 auto var(--space-5); }
.fc-complete__stats > div { display: grid; gap: 3px; padding: var(--space-3) var(--space-2); border: 1px solid var(--line); border-radius: 10px; background: var(--surface-alt); }
.fc-complete__stats strong { font: 700 1.25rem/1 var(--font-display); color: var(--ink); }
.fc-complete__stats span { color: var(--ink-soft); font-size: 0.78rem; }
/* Mirrors .fc-rate--again/--hard/--good/--easy's own color scheme onto the
   summary tally, so "Easy" reads as a genuine positive/mint moment rather
   than four identical neutral tiles. Relies on the fixed Again/Hard/Good/
   Easy markup order in index.html (the standard spaced-repetition scale),
   not a class per tile, to avoid touching the HTML for a color-only change. */
.fc-complete__stats > div:nth-child(1) strong { color: var(--incorrect); }
.fc-complete__stats > div:nth-child(2) strong { color: var(--caution); }
.fc-complete__stats > div:nth-child(3) strong { color: var(--primary); }
.fc-complete__stats > div:nth-child(4) strong { color: var(--correct); }
.fc-complete__actions { display: flex; justify-content: center; flex-wrap: wrap; gap: var(--control-gap); }
.fc-complete__actions .btn { min-width: 140px; }
@media (max-width: 420px) {
  .fc-complete__stats { gap: 6px; }
  .fc-complete__stats > div { padding-inline: 4px; }
  .fc-complete__stats span { font-size: 0.7rem; }
  .fc-complete__actions { flex-direction: column; }
  .fc-complete__actions .btn { width: 100%; }
}

/* The card itself — a real 3D flip, not a crossfade, so the "turning it
   over" gesture in Anki/Quizlet/UWorld is legible rather than guessed at. */
.fc-card {
  perspective: 1400px;
  margin: 20px 0;
  cursor: pointer;
  outline: none;
}
.fc-card__inner {
  position: relative;
  height: 264px;
  min-height: 264px;
  max-height: 620px;
  transform-style: preserve-3d;
  transition: transform var(--dur-modal) var(--ease-standard);
}
.fc-card__inner.is-flipped { transform: rotateY(180deg); }
.fc-card__face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface);
  padding: 26px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--control-gap-roomy);
  box-shadow: var(--shadow-md);
  text-align: center;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.fc-card__face--back { transform: rotateY(180deg); }
.fc-card__face.is-long { justify-content: flex-start; }
.fc-card__type {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted-2, var(--ink-soft));
}
.fc-card__face p {
  margin: 0;
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.6;
}
.fc-card__face .hint { font-size: 0.78rem; }
.fc-card__face p strong { font-weight: 700; color: var(--ink); }
.fc-card__face p em { font-style: italic; }
.fc-card__face p u { text-decoration: underline; text-underline-offset: 2px; }

.fc-card__image {
  /* Fixed px bounds rather than a %, and an explicit align-self — the
     face's flex column defaults every child to align-items:stretch, which
     was quietly forcing the image's own box to the container's full
     cross-axis width regardless of the max-width set here, throwing off
     both its rendered size and its centering. */
  align-self: center;
  width: auto;
  height: auto;
  max-width: min(80%, 220px);
  max-height: 160px;
  object-fit: contain;
  border-radius: 10px;
  background: var(--surface-2, var(--surface));
}

.fc-rate-row { display: flex; gap: var(--control-gap); margin: 18px 0 6px; }
.fc-rate {
  flex: 1;
  padding: 12px 6px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--surface);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.86rem;
  cursor: pointer;
  transition: transform var(--dur-card) var(--ease-standard), background var(--dur-card) var(--ease-standard);
}
#flashcardsBackBtn { margin-top: var(--space-4); }
#fcStudyEnd { margin-top: var(--space-3); }
.fc-rate:active { transform: scale(0.95); }
.fc-rate--again { border-color: var(--incorrect); color: var(--incorrect); }
.fc-rate--hard   { border-color: var(--caution); color: var(--caution); }
.fc-rate--good   { border-color: var(--primary); color: var(--primary); }
.fc-rate--easy   { border-color: var(--correct); color: var(--correct); }
@media (hover: hover) and (pointer: fine) {
  .fc-rate--again:hover { background: var(--incorrect-bg); }
  .fc-rate--hard:hover   { background: var(--caution-tint, var(--surface-alt)); }
  .fc-rate--good:hover   { background: var(--primary-tint, var(--surface-alt)); }
  .fc-rate--easy:hover   { background: var(--correct-bg); }
}

@media (max-width: 520px) {
  .fc-card { min-height: 200px; }
  .fc-rate { font-size: 0.78rem; padding: 12px 4px; }
}

/* ---------- public landing page ---------- */
.public-landing {
  min-height: 100vh;
  overflow-x: hidden;
  color: var(--ink);
  background:
    radial-gradient(circle at 84% 8%, rgba(85, 221, 181, 0.16), transparent 27rem),
    linear-gradient(180deg, var(--bg), var(--surface) 44%, var(--bg));
}
.landing-nav {
  position: sticky;
  top: 0;
  z-index: 12;
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: min(1180px, calc(100% - 40px));
  min-height: 76px;
  margin: 0 auto;
  border-bottom: 1px solid color-mix(in srgb, var(--line) 80%, transparent);
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(16px);
}
.landing-brand { display: inline-flex; align-items: center; gap: 11px; color: var(--ink); text-decoration: none; font-family: var(--font-display); font-size: 1.1rem; font-weight: 800; }
.landing-brand img { width: 38px; height: 38px; border-radius: 11px; }
.landing-nav__links { display: flex; align-items: center; gap: var(--space-5); }
.landing-nav__links > a { color: var(--ink-soft); text-decoration: none; font-size: 0.9rem; font-weight: 600; }
.landing-nav__links > a:hover { color: var(--primary); }
.landing-nav__links .btn { min-height: 40px; padding: 8px 16px; }
.landing-hero { display: grid; grid-template-columns: minmax(0, 0.92fr) minmax(460px, 1.08fr); align-items: center; gap: clamp(48px, 7vw, 100px); width: min(1180px, calc(100% - 40px)); min-height: 720px; margin: 0 auto; padding: 84px 0 110px; }
.landing-hero > * { min-width: 0; }
.landing-hero__copy h1 { max-width: 720px; margin: 12px 0 24px; font-size: clamp(3.4rem, 7vw, 6.7rem); line-height: 0.93; letter-spacing: -0.065em; }
.landing-hero__copy h1 span { color: var(--primary); }
.landing-lede { max-width: 590px; margin: 0; color: var(--ink-soft); font-size: clamp(1.02rem, 1.8vw, 1.24rem); line-height: 1.7; }
.landing-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: 32px; }
.landing-fineprint { margin-top: 14px; color: var(--muted-3); font-size: 0.82rem; }
.landing-product { position: relative; overflow: hidden; border: 1px solid var(--line); border-radius: 24px; background: var(--surface); box-shadow: 0 38px 90px -40px rgba(36, 17, 26, 0.48); transform: rotate(1.4deg); }
.landing-product::before { content: ""; position: absolute; inset: -1px; border-radius: inherit; box-shadow: inset 0 0 0 8px color-mix(in srgb, var(--primary) 5%, transparent); pointer-events: none; }
.landing-product__bar { display: flex; justify-content: space-between; align-items: center; padding: 16px 20px; border-bottom: 1px solid var(--line); color: var(--ink-soft); font-size: 0.78rem; }
.landing-product__mark { display: inline-flex; align-items: center; gap: 8px; color: var(--ink); font-family: var(--font-display); font-weight: 800; }
.landing-product__mark img { width: 28px; height: 28px; border-radius: 8px; }
.landing-product__body { padding: clamp(26px, 4vw, 46px); }
.landing-product__body h2 { margin: 20px 0 24px; font-size: clamp(1.35rem, 2.5vw, 2rem); line-height: 1.25; }
.landing-answer { display: grid; grid-template-columns: 30px 1fr; gap: 10px; align-items: center; min-height: 56px; margin-top: 10px; padding: 10px 14px; border: 1px solid var(--line); border-radius: 10px; color: var(--ink-soft); }
.landing-answer.is-selected { border-color: var(--primary); background: var(--primary-tint); color: var(--ink); box-shadow: inset 4px 0 0 var(--primary); }
.landing-rationale { margin-top: 18px; padding: 16px; border-left: 4px solid #55DDB5; border-radius: 0 8px 8px 0; background: var(--correct-bg); color: var(--ink-soft); font-size: 0.9rem; line-height: 1.55; }
.landing-rationale strong { color: var(--ink); }
.landing-pulse { height: 44px; overflow: hidden; border-top: 1px solid var(--line); background: var(--primary); }
.landing-pulse span { display: block; width: 120%; height: 100%; background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 600 44'%3E%3Cpath d='M0 24h105l12-15 15 30 18-19h80l12-10 15 22 18-8h325' fill='none' stroke='%2355DDB5' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/600px 44px repeat-x; }
.landing-section { width: min(1180px, calc(100% - 40px)); margin: 0 auto; padding: 110px 0; border-top: 1px solid var(--line); }
.landing-section > h2 { max-width: 700px; margin: 12px 0 44px; font-size: clamp(2rem, 4vw, 3.8rem); line-height: 1.04; letter-spacing: -0.045em; }
.feature-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; overflow: hidden; border: 1px solid var(--line); border-radius: 18px; background: var(--line); }
.feature-grid article { min-height: 230px; padding: 32px; background: var(--surface); }
.feature-grid article > span { color: var(--primary); font-family: var(--font-mono); font-size: 0.76rem; font-weight: 700; }
.feature-grid h3 { margin: 44px 0 10px; font-size: 1.25rem; }
.feature-grid p { margin: 0; color: var(--ink-soft); line-height: 1.65; }
.pricing-section { text-align: center; }
.pricing-section > h2 { margin-inline: auto; }
.pricing-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-3); text-align: left; }
.price-card { display: flex; flex-direction: column; min-height: 290px; padding: 26px; border: 1px solid var(--line); border-radius: 14px; background: var(--surface); }
.price-card.is-featured { border-color: var(--primary); box-shadow: inset 0 5px 0 var(--primary); }
.price-card p { min-height: 42px; margin: 0; color: var(--ink-soft); font-weight: 600; }
.price-card h3 { margin: 18px 0 0; font-size: 2.8rem; letter-spacing: -0.05em; }
.price-card h3 span { font-size: 1.2rem; color: var(--primary); vertical-align: top; }
.price-card small { display: block; margin-bottom: auto; color: var(--ink-soft); }
.price-card .btn { width: 100%; margin-top: 30px; }
.pricing-note { max-width: 700px; margin: 22px auto 0; color: var(--ink-soft); font-size: 0.84rem; }
.landing-footer { display: flex; justify-content: space-between; gap: 30px; width: min(1180px, calc(100% - 40px)); margin: 0 auto; padding: 36px 0 52px; border-top: 1px solid var(--line); }
.landing-footer > div { display: flex; gap: 18px; align-items: center; }
.landing-footer span { color: var(--ink-soft); font-size: 0.84rem; }
.landing-footer a, .landing-footer .link { width: auto; margin: 0; padding: 0; border: 0; background: transparent; color: var(--ink-soft); font: inherit; font-size: 0.84rem; }
.legal { width: min(780px, calc(100% - 40px)); margin: 0 auto; padding: 48px 0 80px; }
.legal h1 { margin-bottom: 8px; }
.legal h2 { margin-top: 36px; margin-bottom: 10px; font-size: 1.15rem; }
.legal p, .legal li { color: var(--ink); line-height: 1.7; }
.legal ul { padding-left: 22px; }
.legal li { margin-bottom: 8px; }
.legal .updated { color: var(--ink-soft); font-size: .85rem; margin-bottom: 32px; }
.legal .back { margin-top: 44px; }
.account-help-links { display: flex; flex-wrap: wrap; gap: 14px; margin: 24px 0; font-size: .88rem; }
.account-help-links a { color: var(--primary-dark); }

/* ---------- Slideshow and results review navigation ---------- */
.slideshow-feedback { margin-top: var(--space-5); padding: var(--space-4); border: 1px solid var(--line); border-left: 4px solid #55DDB5; border-radius: 8px; background: var(--surface-alt); }
.slideshow-feedback__head { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-3); }
.slideshow-feedback #slideshowRetryBtn { margin-top: var(--space-3); }
.results-review-layout { display: grid; grid-template-columns: minmax(210px, 0.3fr) minmax(0, 1fr); gap: var(--space-4); align-items: start; margin-top: var(--space-4); }
.missed-nav { position: sticky; top: 96px; max-height: calc(100vh - 120px); overflow-y: auto; padding: var(--space-4); border: 1px solid var(--line); border-radius: 8px; background: var(--surface); }
.missed-nav h2 { margin: 4px 0 16px; font-size: 1rem; }
.missed-nav .eyebrow { margin: 0; }
.missed-nav .rapid-wrong-nav { display: grid; grid-template-columns: 1fr; }
.missed-nav .chip { width: 100%; text-align: left; }
.results-review-layout .review-card, .results-review-layout .review-list { min-width: 0; margin: 0; }

/* ---------- embedded flashcard setup ---------- */
.embedded-mode-panel { margin-top: var(--space-5); }
.fc-embedded-heading { padding: var(--space-5); text-align: left; background: var(--surface-alt); }
.fc-deck-source { margin: var(--space-4) 0 var(--space-5); padding: var(--space-4); border: 1px solid var(--line); border-radius: 10px; background: var(--surface-alt); }
.fc-source-panel { margin: var(--space-4) 0 0; }
.fc-bank-choices { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2); margin-top: var(--space-3); }
.fc-bank-choice { display: flex; flex-direction: column; gap: 4px; min-height: 76px; padding: 14px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); color: var(--ink); text-align: left; cursor: pointer; }
.fc-bank-choice:hover { border-color: var(--primary); }
.fc-bank-choice span { color: var(--ink-soft); font-size: 0.78rem; }
.fc-card-fields { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); align-items: stretch; }
.fc-card-fields textarea { width: 100%; min-height: 76px; max-height: 180px; resize: none; }
#fcNewCardForm > .btn { display: block; margin-top: var(--space-3); }
#flashcardsBackBtn { display: none; }

@media (max-width: 900px) {
  .landing-hero { grid-template-columns: 1fr; padding-top: 60px; }
  .landing-product { max-width: 680px; transform: none; }
  .pricing-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 680px) {
  .landing-nav { width: calc(100% - 28px); }
  .landing-nav__links > a { display: none; }
  .landing-nav__links { gap: var(--space-2); }
  .landing-hero, .landing-section, .landing-footer { width: calc(100% - 28px); }
  .landing-hero { min-height: 0; padding: 56px 0 80px; }
  .landing-hero__copy h1 { font-size: clamp(3.2rem, 16vw, 5rem); }
  .landing-actions { flex-direction: column; }
  .landing-actions .btn { width: 100%; white-space: normal; }
  .landing-product { width: 100%; }
  .feature-grid, .pricing-grid { grid-template-columns: 1fr; }
  .feature-grid article { min-height: 190px; }
  .landing-footer, .landing-footer > div { align-items: flex-start; flex-direction: column; }
  .results-review-layout { grid-template-columns: 1fr; }
  .missed-nav { position: sticky; top: 68px; z-index: 5; max-height: none; }
  .missed-nav .rapid-wrong-nav { display: flex; flex-wrap: nowrap; overflow-x: auto; }
  .missed-nav .chip { width: auto; white-space: nowrap; }
  .fc-bank-choices, .fc-card-fields { grid-template-columns: 1fr; }
}
