/* Carry the One
 * ─────────────────────────────────────────────────────────────────────────
 * Three decisions worth writing down.
 *
 * 1. A wrong step is amber, never red. The engine refuses to cascade one
 *    mistake into four marks; a page of red would undo that restraint
 *    visually. Amber reads as "look here", red reads as "you failed".
 *
 * 2. Colour never carries meaning alone (WCAG 1.4.1). Every verdict has a
 *    badge, a symbol and a written label, so the page survives greyscale,
 *    colour-blindness and being read aloud.
 *
 * 3. Clean is not the same as plain. The difference is almost entirely
 *    typographic scale and depth: a page where everything is the same size
 *    and sits on the same plane reads as unfinished, however tidy its
 *    spacing. Hence a real type ramp — 2.2rem headings against 0.68rem
 *    tracked labels — and a three-stop elevation system rather than flat
 *    boxes with hairline borders.
 */

:root {
  /* Surfaces, warm rather than clinical — this is a page of homework, not a
     dashboard. Three planes, so cards can actually sit above the page. */
  --bg: #f7f4ee;
  --surface: #ffffff;
  --sunk: #efeade;

  --ink: #1c1a17;
  --ink-soft: #4a453e;
  --muted: #676158;
  --line: #e3ddd0;

  --accent: #14563a;
  --accent-soft: #e3efe8;
  --accent-ink: #ffffff;
  --focus: #14563a;

  --ok: #14603e;
  --ok-bg: #e4f0e9;
  --attention: #8a4708;
  --attention-bg: #fbeedf;
  --slip: #1c4d79;
  --slip-bg: #e5eef7;
  --unsure: #514c45;
  --unsure-bg: #edeae3;

  /* Elevation. Two or three layers each: a tight ambient shadow for edge
     definition plus a wide soft one for the lift. One-layer shadows are the
     tell of an unstyled interface. */
  --shadow-1: 0 1px 2px rgb(28 26 23 / 6%), 0 2px 6px rgb(28 26 23 / 4%);
  --shadow-2:
    0 1px 2px rgb(28 26 23 / 7%), 0 6px 14px rgb(28 26 23 / 7%),
    0 14px 30px rgb(28 26 23 / 5%);
  --shadow-accent: 0 2px 5px rgb(20 86 58 / 24%), 0 8px 18px rgb(20 86 58 / 20%);

  --radius: 12px;
  --radius-lg: 18px;
  --radius-full: 999px;
  --tap: 48px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #171512;
    --surface: #211e19;
    --sunk: #100e0c;

    --ink: #f5f1e8;
    --ink-soft: #cfc7ba;
    --muted: #aca496;
    --line: #38322a;

    --accent: #82d3a6;
    --accent-soft: #1d2c24;
    --accent-ink: #0d1b14;
    --focus: #82d3a6;

    --ok: #82d3a6;
    --ok-bg: #19271f;
    --attention: #eeb66f;
    --attention-bg: #2e2317;
    --slip: #92c1e6;
    --slip-bg: #18222d;
    --unsure: #aca496;
    --unsure-bg: #24211a;

    --shadow-1: 0 1px 2px rgb(0 0 0 / 40%), 0 2px 6px rgb(0 0 0 / 30%);
    --shadow-2:
      0 1px 2px rgb(0 0 0 / 45%), 0 6px 14px rgb(0 0 0 / 38%),
      0 14px 30px rgb(0 0 0 / 30%);
    --shadow-accent: 0 2px 5px rgb(0 0 0 / 45%), 0 8px 18px rgb(0 0 0 / 35%);
  }
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

/* The `hidden` attribute is only `display: none` in the user-agent stylesheet,
 * so *any* author `display` rule beats it. `.button { display: flex }` did
 * exactly that, and every control that hides itself by attribute — the camera
 * buttons, the second hint — stayed on screen in every state. This is the
 * one place `!important` is the correct tool: `hidden` is not a suggestion. */
[hidden] { display: none !important; }

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--bg);
  color: var(--ink);
  font: 17px/1.6 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-synthesis-weight: none;
}

/* A link inherits nothing useful from the user agent: #0000EE on a warm cream
 * page, and a hit area exactly the height of its text. The one link the
 * student screens have — "Parent view" — was the only control in the app
 * failing the 48px floor everything else is built to. */
a {
  color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
a:hover { color: var(--ink); }

/* --- Accessibility utilities -------------------------------------------- */

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: 0.5rem;
  top: -4rem;
  z-index: 10;
  padding: 0.75rem 1rem;
  background: var(--accent);
  color: var(--accent-ink);
  border-radius: var(--radius);
  text-decoration: none;
  transition: top 120ms ease;
}
.skip-link:focus { top: 0.5rem; }

:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
  border-radius: 6px;
}

/* --- Layout -------------------------------------------------------------- */

.app-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  max-width: 34rem;
  margin: 0 auto;
  padding: 1rem 1.25rem 0;
}

.wordmark {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}

main {
  max-width: 34rem;
  margin: 0 auto;
  padding: 1.5rem 1.25rem calc(3.5rem + env(safe-area-inset-bottom));
}

/* Screens with two or three controls left half a phone of empty space below
   them, which reads as unfinished rather than spacious. Centring the content
   makes the emptiness look deliberate. Content taller than the viewport just
   flows as normal. */
section[data-fit="short"] {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: calc(100dvh - 8rem);
}

/* The type ramp. The gap between 2.2rem and 0.68rem is what stops the page
   reading as a wall of undifferentiated 17px. */
h1 {
  font-size: clamp(1.75rem, 6.5vw, 2.2rem);
  line-height: 1.12;
  letter-spacing: -0.025em;
  font-weight: 700;
  margin: 0 0 0.65rem;
  text-wrap: balance;
}
/* Headings are focused programmatically on every screen change so the change
 * is announced. They are not interactive, so a focus ring on them is noise —
 * and on first load it drew a heavy box around the page title. */
h1[tabindex="-1"]:focus,
h1[tabindex="-1"]:focus-visible { outline: none; }

p { margin: 0 0 1rem; }
.lede { color: var(--ink-soft); font-size: 1.03rem; }
.hint-text { font-size: 0.9rem; color: var(--muted); }

.stack { display: flex; flex-direction: column; gap: 0.65rem; margin-bottom: 1.5rem; }
.row { display: flex; gap: 0.6rem; flex-wrap: wrap; }
/* Buttons are full-width by default, which inside a row made every one of them
   take its own line. In a row they share it. */
.row > .button { width: auto; flex: 1 1 auto; }

/* --- Buttons ------------------------------------------------------------- */

.button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: var(--tap);
  padding: 0.85rem 1.25rem;
  border: 1.5px solid transparent;
  border-radius: var(--radius);
  font: inherit;
  font-weight: 650;
  letter-spacing: -0.005em;
  text-align: center;
  cursor: pointer;
  transition: transform 120ms cubic-bezier(0.2, 0, 0, 1),
              box-shadow 160ms ease, background-color 140ms ease;
}

.button.primary {
  background: var(--accent);
  color: var(--accent-ink);
  /* A shadow tinted with the button's own hue, rather than grey. It's the
     cheapest thing that separates a designed control from a coloured box. */
  box-shadow: var(--shadow-accent);
}
.button.primary:hover { transform: translateY(-1px); }
.button.primary:active { transform: translateY(1px); box-shadow: var(--shadow-1); }

.button.secondary {
  background: var(--surface);
  color: var(--ink);
  border-color: var(--line);
  box-shadow: var(--shadow-1);
}
.button.secondary:hover { border-color: var(--muted); }
.button.secondary:active { transform: translateY(1px); }

.button.quiet { background: transparent; color: var(--muted); }
.button.quiet:hover { color: var(--ink); background: var(--sunk); }

.button.small {
  width: auto;
  flex: 0 0 auto;
  padding: 0.55rem 1.1rem;
  font-size: 0.94rem;
  background: var(--accent);
  color: var(--accent-ink);
  box-shadow: var(--shadow-1);
}
.button.small.quiet {
  background: transparent;
  color: var(--muted);
  border-color: var(--line);
  box-shadow: none;
}

/* Used on a <button> in the app header and an <a> in the parent header. A
 * button centres its own content; an anchor does not, so `min-height` alone
 * left "Back to the app" sitting at the top of a 48px box and visibly higher
 * than the wordmark beside it. Centring explicitly makes the two agree. */
.text-button {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding: 0 0.25rem;
  background: none;
  border: none;
  color: var(--muted);
  font: inherit;
  font-size: 0.9rem;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.student-button {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 60px;
  padding: 0.9rem 1.2rem;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-1);
  color: var(--ink);
  font: inherit;
  font-size: 1.15rem;
  font-weight: 650;
  text-align: left;
  cursor: pointer;
  transition: transform 120ms ease, box-shadow 160ms ease;
}
.student-button:hover { transform: translateY(-1px); box-shadow: var(--shadow-2); }

/* --- Choosing between problems on one page ------------------------------- */

/* The maths is the thing being chosen between, so it gets the size and the
   weight; how much work is under it is the tiebreaker underneath. */
.problem-button {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.3rem;
  width: 100%;
  min-height: var(--tap);
  padding: 0.85rem 1.1rem;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-1);
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: transform 120ms ease, box-shadow 160ms ease;
}
.problem-button:hover { transform: translateY(-1px); box-shadow: var(--shadow-2); }
.problem-button .math { font-size: 1.15rem; }

.problem-count {
  font-size: 0.85rem;
  color: var(--muted);
}

/* --- Forms --------------------------------------------------------------- */

.add-form {
  margin-bottom: 1.25rem;
  padding: 1rem;
  background: var(--surface);
  border: 1.5px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
}
.add-form label {
  display: block;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.45rem;
}
.add-form input {
  width: 100%;
  min-height: var(--tap);
  margin-bottom: 0.75rem;
  padding: 0.7rem 0.9rem;
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--ink);
  font: inherit;
}
.add-form input:focus { border-color: var(--accent); }

/* --- Camera -------------------------------------------------------------- */

.camera { margin-bottom: 1rem; }
.camera video {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  background: var(--sunk);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
}

/* --- Math ---------------------------------------------------------------- */

/* Set the way a textbook sets it. Monospace gave every space a full character
   width and every glyph the same box, so an equation came out gappy and read
   as a line of code — the opposite of the page the student is working from. */
.math, .math-block {
  font-family: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia,
    "Times New Roman", serif;
  font-size: 1.1rem;
  font-variant-numeric: lining-nums tabular-nums;
  letter-spacing: 0;
}

/* Variables italic, operators and numbers upright. */
.math i { font-style: italic; }

.math-block {
  background: var(--surface);
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  padding: 1rem 1.1rem;
}

/* --- Cards --------------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1.5px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
  padding: 1.1rem 1.2rem;
  margin-bottom: 0.85rem;
}

.card-title {
  margin: 0 0 0.45rem;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}
.card p { margin: 0; }

.callout {
  margin: 0.7rem 0 0;
  padding: 0.75rem 0.95rem;
  background: var(--attention-bg);
  color: var(--attention);
  border-radius: var(--radius);
  font-size: 0.94rem;
  font-weight: 550;
}

/* --- Banner -------------------------------------------------------------- */

.banner {
  border-radius: var(--radius-lg);
  padding: 1.05rem 1.2rem;
  margin-bottom: 1.35rem;
  font-weight: 600;
  line-height: 1.45;
  box-shadow: var(--shadow-1);
}
.banner[data-tone="ok"] { background: var(--ok-bg); color: var(--ok); }
.banner[data-tone="attention"] { background: var(--attention-bg); color: var(--attention); }

/* Sits directly above the verdict banner and is always good news, so it is
   deliberately flatter than one: a filled, shadowed box here would either
   read as a second verdict or, when the banner below is also "ok", stack two
   identical green slabs. The rule down the left ties it to the banner it
   introduces rather than floating free of it. */
.fixed-note {
  background: none;
  box-shadow: none;
  color: var(--ok);
  border-left: 3px solid var(--ok);
  border-radius: 0;
  padding: 0.15rem 0 0.15rem 0.85rem;
  margin-bottom: 0.9rem;
  font-size: 0.95rem;
}

/* Asked once for the whole page, immediately under the banner it belongs to. */
.confirm-panel { margin: -0.6rem 0 1.35rem; }
.confirm-question {
  margin: 0 0 0.7rem;
  font-size: 1.05rem;
  font-weight: 650;
}
.confirm-panel .row > .button { flex: 1 1 auto; }

/* --- Steps --------------------------------------------------------------- */

.steps {
  list-style: none;
  margin: 0 0 1.5rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}

.step {
  position: relative;
  padding: 0.95rem 1.1rem 0.95rem 1.25rem;
  background: var(--surface);
  border: 1.5px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
  overflow: hidden;
}

/* The rail. A pseudo-element rather than a thick border so it can be inset
   and rounded — a full-height square edge looks like a table, not a mark. */
.step::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.7rem;
  bottom: 0.7rem;
  width: 4px;
  border-radius: 0 var(--radius-full) var(--radius-full) 0;
  background: var(--line);
}

/* Badge, then a column: tiny label over the math. Side by side, the label
   competed with the line it was labelling and the math started mid-row. */
.step-head { display: flex; align-items: flex-start; gap: 0.7rem; }

/* A step that checks out collapses to one row. It is reassurance, not the
   thing the student opened the app for, and at full height a run of correct
   work buried the single line that needed reading. */
.step.compact { padding: 0.55rem 1rem 0.55rem 1.15rem; box-shadow: none; }
.step.compact .step-head { align-items: center; }
.step.compact .line-row { flex-direction: row; align-items: baseline; gap: 0.6rem; }
.step.compact .line-meta { flex: 0 0 auto; }
.step.compact .step-text { font-size: 1.06rem; color: var(--ink-soft); }
.step.compact .mark { width: 22px; height: 22px; font-size: 0.78rem; }
/* Hidden from the eye, not from a screen reader: the ✓ carries the meaning
   visually, but "Checks out" still has to be readable aloud. */
.step.compact .step-tag {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The verdict badge: a filled disc, not a loose character. */
.mark {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: var(--radius-full);
  font-size: 0.9rem;
  font-weight: 800;
  line-height: 1;
}

.line-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.45rem 0.6rem;
}

.step-tag {
  display: inline-block;
  padding: 0.2rem 0.55rem;
  border-radius: var(--radius-full);
  font-size: 0.63rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.step[data-kind="valid"]::before { background: var(--ok); }
.step[data-kind="valid"] .mark { background: var(--ok-bg); color: var(--ok); }
.step[data-kind="valid"] .step-tag { background: var(--ok-bg); color: var(--ok); }

.step[data-kind="misconception"]::before { background: var(--attention); }
.step[data-kind="misconception"] { background: var(--attention-bg); }
.step[data-kind="misconception"] .mark { background: var(--surface); color: var(--attention); }
.step[data-kind="misconception"] .step-tag { background: var(--surface); color: var(--attention); }

.step[data-kind="slip"]::before { background: var(--slip); }
.step[data-kind="slip"] { background: var(--slip-bg); }
.step[data-kind="slip"] .mark { background: var(--surface); color: var(--slip); }
.step[data-kind="slip"] .step-tag { background: var(--surface); color: var(--slip); }

.step[data-kind="unsure"]::before { background: var(--unsure); }
.step[data-kind="unsure"] .mark { background: var(--unsure-bg); color: var(--unsure); }
.step[data-kind="unsure"] .step-tag { background: var(--unsure-bg); color: var(--unsure); }

/* Flagged, but the student hasn't vouched for the reading yet. Neutral on
   purpose: nothing is called wrong until they confirm we read it right. */
.step[data-kind="pending"]::before { background: var(--muted); }
.step[data-kind="pending"] .mark { background: var(--unsure-bg); color: var(--muted); }
.step[data-kind="pending"] .step-tag { background: var(--unsure-bg); color: var(--unsure); }

.step[data-kind="unreadable"]::before { background: var(--attention); }
.step[data-kind="unreadable"] .step-tag { background: var(--attention-bg); color: var(--attention); }

/* The reference line, not a disabled one. A sunk panel read as greyed out. */
.step.problem { background: var(--surface); border-style: dashed; box-shadow: none; }
.step.problem::before { background: var(--ink-soft); }

/* Actions inside a step, left-aligned and sized to their text — a full-width
   centred button in the middle of a card reads as an orphan. */
.step-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.8rem;
}
.step-actions .button { width: auto; flex: 0 0 auto; }

.step-detail { margin: 0.7rem 0 0; }
.step-detail .label { font-weight: 700; }
.step-rule { margin: 0.5rem 0 0; font-size: 0.93rem; color: var(--muted); }

.step-confirm { margin-top: 0.85rem; }
.step-confirm p { margin: 0 0 0.6rem; font-weight: 650; }

/* --- The corrected step -------------------------------------------------- */
/* The teaching payload, and the one thing on the page a student most needs to
   read. Given the accent so it reads as the answer to the flag above it. */

.corrected {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.35rem 0.75rem;
  margin: 0.85rem 0 0;
  padding: 0.85rem 1rem;
  background: var(--accent-soft);
  border-radius: var(--radius);
}

.corrected-label {
  flex: 0 0 auto;
  font-size: 0.65rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
}

.corrected .math { font-size: 1.12rem; font-weight: 600; color: var(--ink); }

.why { margin-top: 0.8rem; }
.why > summary { font-size: 0.93rem; font-weight: 600; }
.why > p { margin: 0.55rem 0 0; font-size: 0.93rem; color: var(--ink-soft); }

/* --- Editing a line ------------------------------------------------------ */

.line-row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.2rem;
  flex: 1 1 auto;
  min-width: 0;
}

.line-label {
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The math is the content. Everything else on the card is furniture. */
.step-text {
  font-size: 1.32rem;
  line-height: 1.35;
}

.line-input {
  flex: 1 1 auto;
  min-width: 0;
  min-height: var(--tap);
  padding: 0.55rem 0.8rem;
  border: 1.5px solid var(--accent);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--ink);
  /* Same face as the rendered line: what you type should look like what you
     were just shown, or the edit feels like it lands somewhere else. */
  font-family: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia,
    "Times New Roman", serif;
  font-size: 1.2rem;
}

.editor-controls { margin-top: 0.8rem; }
.editor-instruction {
  margin: 0 0 0.6rem;
  font-size: 0.93rem;
  color: var(--ink-soft);
}

/* --- Photo --------------------------------------------------------------- */

.photo-details { margin: 0 0 1.35rem; }

/* The crop lives here, as an `aspect-ratio` and a negative `margin-top` set
   from JS. Both are percentages of this box's width, so the whole thing
   resolves without measuring anything. With no script it is an ordinary
   full-height photo, which is what it was before. */
.photo-frame {
  overflow: hidden;
  margin-top: 0.65rem;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
}
.photo {
  display: block;
  width: 100%;
}

/* Disclosures are controls, not links. Underlined text floating full-width in
   the middle of a card reads as neither — it looks like a stray anchor. */
details > summary {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  min-height: var(--tap);
  width: auto;
  padding: 0.45rem 0.95rem;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-full);
  background: var(--surface);
  color: var(--ink-soft);
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}
details > summary::-webkit-details-marker { display: none; }
details > summary::after {
  content: "▾";
  margin-left: auto;
  align-self: center;
  font-size: 0.75em;
  color: var(--muted);
  transition: transform 160ms ease;
}
details[open] > summary::after { transform: rotate(180deg); }
details > summary:hover { border-color: var(--muted); }

.diagnostics {
  margin: 0 0 0.5rem;
  padding: 0.75rem 0.9rem;
  overflow-x: auto;
  background: var(--sunk);
  border-radius: var(--radius);
  color: var(--ink);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.8rem;
  line-height: 1.55;
  white-space: pre;
}

/* --- Parent view --------------------------------------------------------- */

main.wide { max-width: 44rem; }
/* The header has to share the content's measure or it sits visibly inboard. */
.app-header.wide { max-width: 44rem; }

.student {
  margin: 2rem 0 0;
  padding-top: 1.75rem;
  border-top: 1.5px solid var(--line);
}
.student:first-of-type { border-top: none; padding-top: 0; }

.student-name {
  font-size: 1.5rem;
  letter-spacing: -0.02em;
  margin: 0 0 1rem;
}

.section-title {
  margin: 1.75rem 0 0.75rem;
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 0.7rem;
  margin: 0;
}
.stats > div {
  padding: 0.85rem 1rem;
  background: var(--surface);
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
}
.stats dt {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.stats dd { margin: 0.3rem 0 0; font-size: 1.5rem; font-weight: 650; }
.dt-note {
  display: block;
  margin-top: 0.15rem;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  font-size: 0.72rem;
  opacity: 0.8;
}

.misconceptions, .recent {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}

.misconception {
  padding: 0.9rem 1.05rem;
  background: var(--surface);
  border: 1.5px solid var(--line);
  border-left-width: 5px;
  border-radius: var(--radius);
}
/* Repetition is the signal. Only a pattern gets the emphasis. */
.misconception[data-pattern] {
  border-left-color: var(--attention);
  background: var(--attention-bg);
}

.misconception-head { display: flex; align-items: baseline; gap: 0.7rem; }
.count {
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  color: var(--muted);
}
.misconception[data-pattern] .count { color: var(--attention); }
.misconception-name { font-weight: 600; }

.pattern-note {
  margin: 0.5rem 0 0;
  font-size: 0.93rem;
  color: var(--attention);
  font-weight: 600;
}
.misconception details { margin-top: 0.7rem; }
.misconception details > p { margin: 0.55rem 0 0; font-size: 0.93rem; color: var(--ink-soft); }

.recent-item {
  padding: 0.85rem 1.05rem;
  background: var(--surface);
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
}
.recent-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
}
.recent-when { flex: 0 0 auto; font-size: 0.8rem; color: var(--muted); }
.recent-outcome { margin: 0.45rem 0 0; font-size: 0.95rem; }
.recent-outcome[data-tone="ok"] { color: var(--ok); }
.recent-outcome[data-tone="attention"] { color: var(--attention); }
.recent-note { margin: 0.35rem 0 0; font-size: 0.86rem; color: var(--muted); }

.empty { color: var(--muted); font-size: 0.95rem; }

/* --- Spinner ------------------------------------------------------------- */

/* Left, not centred. It sits directly above a left-aligned heading, and
   `margin: auto` put it in the middle of the column with the words starting at
   the edge — two alignments in the only screen with two elements on it. */
.spinner {
  width: 2.4rem;
  height: 2.4rem;
  margin: 0 0 1.5rem;
  border: 3px solid var(--line);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.85s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* Results arrive as a set; staggering them lets the eye land on the first
   line before the rest settle. Purely decorative — removed below. */
@keyframes rise {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

.steps > .step {
  animation: rise 260ms cubic-bezier(0.2, 0, 0, 1) backwards;
}
.steps > .step:nth-child(1) { animation-delay: 0ms; }
.steps > .step:nth-child(2) { animation-delay: 45ms; }
.steps > .step:nth-child(3) { animation-delay: 90ms; }
.steps > .step:nth-child(4) { animation-delay: 135ms; }
.steps > .step:nth-child(n+5) { animation-delay: 180ms; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    animation-delay: 0ms !important;
    transition-duration: 0.01ms !important;
  }
  .spinner { animation: none; border-top-color: var(--line); }
  .button:hover, .button:active,
  .student-button:hover, .problem-button:hover { transform: none; }
}

@media (forced-colors: active) {
  .button, .card, .step, .line-input, .math-block,
  .problem-button { border: 1px solid CanvasText; }
  .step::before, .mark { forced-color-adjust: none; }
}

/* --- Word problems ------------------------------------------------------- */

/* Prose, not maths: system font, comfortable reading size. */
.word-problem-text {
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-size: 1rem;
  line-height: 1.5;
}

/* --- Setup note ---------------------------------------------------------- */

/* Neutral by default — the LLM translation might be wrong, so the card
   stays neutral until the engine has confirmed equivalence. */
.setup-card {
  background: var(--unsure-bg);
  border-color: var(--unsure-bg);
}
.setup-card[data-tone="ok"] {
  background: var(--ok-bg);
  border-color: var(--ok-bg);
}
.setup-card[data-tone="ok"] .setup-label { color: var(--ok); }
.setup-card[data-tone="ok"] p { color: var(--ok); }
.setup-card p { color: var(--unsure); }

.setup-label {
  margin: 0 0 0.45rem;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--unsure);
}

.footnote {
  margin: 0.75rem 0 0;
  font-size: 0.86rem;
  color: var(--muted);
}

/* The tap target, not the text, is what has to clear 48px. The margin above
   is reduced by the same amount the padding adds, so the gap is unchanged. */
.footnote a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
}
