/* Stratford Maths — interface.
 *
 * Calm, professional, academic. Hierarchy comes from typography, spacing,
 * borders and restrained colour.
 *
 * MOTION IS FEEDBACK, NEVER DECORATION. The rule was once "no motion at all",
 * and the spirit of it holds: nothing drifts, pulses, bounces or celebrates,
 * and no state a learner needs to READ is announced by movement. What was
 * added is the half-frame of response a finger expects from a thing it has
 * just pressed — a card lifting under the thumb, a button settling when tapped.
 * Absent that, a slow phone reads as a broken one and the child taps again.
 * Every such rule is inside a `prefers-reduced-motion` guard, and every state
 * change is still carried by border, background, an icon and a word.
 *
 * DEPTH IS DRAWN, NOT DOWNLOADED. The shadows, the paper grain, the glow behind
 * the crest and the gold hairlines are all CSS gradients — no images, no extra
 * requests, and a few hundred bytes over the wire once gzipped.
 *
 * NO WEB FONTS. System fonts only, so the interface costs zero extra requests
 * and renders immediately on a slow connection.
 *
 * COLOUR IS NEVER THE ONLY SIGNAL. Every correct/incorrect state carries a mark
 * (✓ / ✗) and a word alongside the colour.
 */

:root {
  /* Stratford brand */
  --navy:        #061a3a;
  --navy-mid:    #0b2d5c;
  --gold:        #8a6d1f;   /* brand gold darkened to 4.9:1 on white — the
                               12px uppercase labels are not "large text", so
                               they need full AA, not the 3:1 large-text bar */
  --gold-line:   #c9a646;
  --cream:       #f5f0e6;
  --paper:       #ffffff;

  --ink:         #172033;
  --ink-soft:    #4a5468;
  --muted:       #626b7d;   /* 5.2:1 on white */
  --line:        #ded7c8;
  --line-soft:   #ebe5d9;
  /* The soft shade at the edge of anything that scrolls sideways. */
  --edge-shade:  rgba(23, 32, 51, 0.17);

  --correct:     #1a5c33;
  --correct-bg:  #eef5f0;
  --wrong:       #8f2027;
  --wrong-bg:    #fbf0f0;

  --serif: Georgia, 'Times New Roman', Times, serif;
  --sans: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;

  --tap: 44px;
  --radius: 4px;
  --gap: 16px;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.55;
}

h1, h2, h3 { font-family: var(--serif); font-weight: 700; line-height: 1.2; margin: 0; }
h1 { font-size: 21px; }
h2 { font-size: 25px; color: var(--navy); }
h3 { font-size: 19px; color: var(--navy); }

p { margin: 0 0 12px; }
p:last-child { margin-bottom: 0; }

/* ------------------------------------------------------------- structure -- */

.masthead {
  background: var(--navy);
  color: #fff;
  border-bottom: 3px solid var(--gold-line);
  padding: 14px 20px;
}
.masthead-inner {
  max-width: 720px; margin: 0 auto;
  display: flex; align-items: baseline; justify-content: space-between;
  flex-wrap: wrap; gap: 4px 16px;
}
.masthead h1 { color: #fff; letter-spacing: 0.2px; }

/* THE STAFF PAGES PIN THE BAR. A teacher's dashboard is one long page and
   the bar is how they change rooms (Teacher / Build work / Import / Reports),
   so it stays on screen there. Learner pages leave it unpinned: on a small
   phone every pinned pixel is a question the learner cannot see.
   z-index clears the sticky table headers further down (z-index 5). */
.masthead--pin {
  position: sticky;
  top: 0;
  z-index: 40;
}
/* Anchor jumps must land below the pinned bar, not underneath it. The bar
   wraps to two rows on a narrow screen, so the clearance is measured from
   the taller shape first and relaxed where one row is certain. */
html:has(.masthead--pin) { scroll-padding-top: 150px; }
@media (min-width: 900px) {
  html:has(.masthead--pin) { scroll-padding-top: 100px; }
}
.masthead .descriptor {
  font-size: 12px; text-transform: uppercase; letter-spacing: 1.4px;
  color: var(--gold-line); font-weight: 700;
}

main { max-width: 720px; margin: 0 auto; padding: 20px 16px 64px; }

.stack > * + * { margin-top: var(--gap); }
.stack-lg > * + * { margin-top: 24px; }

/* ----------------------------------------------------------------- cards -- */

.card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 20px;
}
.card--accent { border-top: 3px solid var(--gold-line); }
.card--quiet { background: transparent; border-style: dashed; }

.card-label {
  font-size: 12px; text-transform: uppercase; letter-spacing: 1.3px;
  font-weight: 700; color: var(--gold); margin-bottom: 8px;
}
.card-note { font-size: 15px; color: var(--muted); }
.question-reports-page .card-note,
.question-reports-page dd,
.question-reports-page td {
  min-width: 0;
  overflow-wrap: anywhere;
  word-break: break-word;
}

/* ------------------------------------------------------------- controls --- */

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--tap); padding: 11px 20px;
  font-family: var(--sans); font-size: 16px; font-weight: 600;
  background: var(--navy); color: #fff;
  border: 1px solid var(--navy); border-radius: var(--radius);
  cursor: pointer; text-align: center;
  /* A LABEL MUST STAY INSIDE ITS BUTTON, IN EVERY LANGUAGE.
     English writes "Teacher login" as two short words with a space the
     browser can wrap at. Afrikaans writes it "Onderwyseraanmelding" — ONE
     word, twenty characters, with nowhere to break — and Setswana compounds
     the same way. With no wrapping rule the word simply could not fit, so it
     spilled out of both sides of the gold box and the page looked broken:
     "Skool- / departementshoofaanmelding" ran clear through its own border.
     It was invisible in English and invisible at a desktop width, which is
     why it survived — the two-column portal grid between 440px and 760px is
     where the columns are narrowest, and a learner or teacher who has turned
     up the text size hits it at ANY width.
     hyphens: auto breaks the compound where the language actually allows and
     shows a hyphen for it, so the wrap reads as typesetting rather than as a
     word snapped in half; lang is set on <html> for every language, which is
     what makes that correct rather than guesswork. overflow-wrap is the
     guarantee underneath it, for a word no dictionary knows.
     `anywhere` RATHER THAN `break-word`, which is the whole fix and is easy
     to get wrong: the two behave identically once a line is being laid out,
     but only `anywhere` also shrinks the text's intrinsic MIN-CONTENT width.
     A .btn is a flex container, so its label is an anonymous flex item with
     min-width:auto and will not shrink below min-content — with `break-word`
     that min-content is still the whole unbroken compound, and the label
     goes on overflowing no matter what the line-breaking rule says. Measured:
     `break-word` alone fixed "Onderwyseraanmelding" and left
     "Skool- / departementshoofaanmelding" hanging 21px outside its border. */
  overflow-wrap: anywhere;
  hyphens: auto;
}
.btn:hover { background: var(--navy-mid); border-color: var(--navy-mid); }
.btn[disabled] { background: #9aa2b1; border-color: #9aa2b1; cursor: not-allowed; }
.btn--secondary { background: var(--paper); color: var(--navy); border-color: var(--line); }
.btn--secondary:hover { background: var(--cream); border-color: var(--navy); }
.btn--block { display: flex; width: 100%; }

.btn-row { display: flex; gap: 10px; flex-wrap: wrap; }
.btn-row .btn { flex: 1 1 auto; }

:where(a, button, input, select, [tabindex]):focus-visible {
  outline: 3px solid var(--gold);
  outline-offset: 2px;
}

/* --------------------------------------------------------------- choices -- */

.choice-grid { display: grid; gap: 8px; }
.choice-grid--wide { grid-template-columns: 1fr; }
@media (min-width: 520px) {
  .choice-grid--cols { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
}

.choice {
  display: flex; align-items: center; gap: 10px;
  min-height: var(--tap); width: 100%;
  padding: 11px 14px;
  font-family: var(--sans); font-size: 16px; text-align: left;
  background: var(--paper); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius);
  cursor: pointer;
}
.choice:hover { border-color: var(--navy); background: #fbfaf7; }
.choice[aria-pressed='true'] { border-color: var(--navy); border-width: 2px; padding: 10px 13px; }
.choice[disabled] { cursor: default; }

.choice-mark {
  flex: 0 0 auto; width: 24px; height: 24px;
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 15px;
  border: 1px solid var(--line); border-radius: 50%;
  color: var(--muted); background: var(--cream);
}
.choice-text { flex: 1 1 auto; }

.choice.is-correct { border-color: var(--correct); border-width: 2px; padding: 10px 13px; background: var(--correct-bg); }
.choice.is-correct .choice-mark { background: var(--correct); border-color: var(--correct); color: #fff; }
.choice.is-wrong { border-color: var(--wrong); border-width: 2px; padding: 10px 13px; background: var(--wrong-bg); }
.choice.is-wrong .choice-mark { background: var(--wrong); border-color: var(--wrong); color: #fff; }

/* ------------------------------------------------------------- question --- */

.question {
  font-size: 20px; line-height: 1.5; color: var(--ink);
  margin: 4px 0 18px;
}
.question .num, .question strong { font-weight: 700; }
.question sup { font-size: 0.7em; }

.typed-row { display: flex; gap: 8px; flex-wrap: wrap; }
.typed-input {
  flex: 1 1 180px; min-height: var(--tap);
  padding: 10px 14px; font-size: 17px; font-family: var(--sans);
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--paper); color: var(--ink);
}
.typed-input:focus { border-color: var(--navy); }

/* ------------------------------------------------------------- maths pad ---
   Shown only where a teacher set the paper to written answers. Five columns
   inside the card, every key at least var(--tap) so a child's aim is enough.
   See web/keypad.js for why these keys and not others. */
.padgrid { margin-top: 10px; display: flex; flex-direction: column; gap: 6px; }
.padgrid.is-hidden { display: none; }
.padrow {
  display: grid;
  /* minmax(0, 1fr), not 1fr: a plain fr will not shrink below its own
     content, so a wide unit key like "cm3" would push the row past a
     375px screen and take the page sideways with it. */
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 6px;
}
.padkey {
  min-height: var(--tap); padding: 6px 4px;
  font-family: var(--sans); font-size: 19px; font-weight: 600;
  color: var(--ink); background: var(--paper);
  border: 1px solid var(--line); border-radius: var(--radius);
  cursor: pointer;
}
.padkey:hover { background: var(--cream); }
.padkey:active { background: var(--line-soft); }
.padkey:focus-visible { outline: 2px solid var(--navy); outline-offset: 1px; }
/* The keys that are not a value: delete, the space, the keyboard swap. */
.padkey--quiet { font-size: 15px; color: var(--muted); background: var(--cream); }
/* A unit is a word, so it is given room rather than squeezed to a digit's width. */
.padkey--wide { font-size: 16px; }


/* -------------------------------------------------------------- verdict --- */

.verdict { border-left: 4px solid var(--line); padding: 14px 16px; background: var(--cream); border-radius: 0 var(--radius) var(--radius) 0; }
.verdict--correct { border-left-color: var(--correct); background: var(--correct-bg); }
.verdict--wrong { border-left-color: var(--wrong); background: var(--wrong-bg); }
.verdict-head { display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: 17px; margin-bottom: 6px; }
.verdict--correct .verdict-head { color: var(--correct); }
.verdict--wrong .verdict-head { color: var(--wrong); }
.verdict-body { font-size: 16px; color: var(--ink-soft); }
.verdict-body ol { margin: 8px 0 0; padding-left: 20px; }
.verdict-body li { margin-bottom: 4px; }
/* The one line on the panel that is about the learner rather than the
   question: what THEIR answer was, and what it came from. Set apart so it is
   not read as more of the working. */
.verdict-why {
  margin: 8px 0 0;
  padding: 8px 10px;
  border-radius: var(--radius);
  background: var(--paper);
  border-left: 3px solid var(--wrong);
  color: var(--ink);
}

/* The lesson, offered after three wrong on one topic in a row. Deliberately
   the same shape as .topic-teach — it is the same offer, arriving at the
   moment a learner most needs it rather than only from the topic list. A
   44px target, because this is tapped by a nine-year-old on a shared phone. */
.verdict-teach {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 0 16px; margin-top: 10px;
  font: inherit; font-size: 14px; font-weight: 600; text-decoration: none;
  color: var(--accent); background: var(--accent-soft);
  border: 1px solid transparent; border-radius: var(--grade-radius, 8px);
}
.verdict-teach:hover { border-color: var(--accent); }
.verdict-teach:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* ----------------------------------------------------------------- meta --- */

.pill {
  display: inline-block; padding: 3px 10px;
  font-size: 12px; font-weight: 700; letter-spacing: 0.6px; text-transform: uppercase;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--cream); color: var(--ink-soft);
}
.pill--level { border-color: var(--gold-line); color: var(--gold); background: #fdfaf2; }
.pill--verified { border-color: var(--correct); color: var(--correct); background: var(--correct-bg); }

.meta-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.meta-row .spacer { flex: 1 1 auto; }
.counter { font-size: 14px; color: var(--muted); font-variant-numeric: tabular-nums; }

/* progress: a static bar, redrawn on change, never animated */
.meter { height: 6px; background: var(--line-soft); border-radius: 999px; overflow: hidden; }
.meter-fill { height: 100%; background: var(--navy); }

/* ---------------------------------------------------------------- lists --- */

.topic-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.topic-loading, .formula-loading {
  min-height: 96px;
  display: grid;
  align-content: center;
  gap: 8px;
  color: var(--navy);
  border-left: 4px solid var(--gold);
}
.topic-loading::before, .formula-loading::before {
  content: '';
  width: 36px;
  height: 4px;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--gold) 0 45%, var(--line) 45% 100%);
  background-size: 200% 100%;
  animation: loading-bar 1.2s ease-in-out infinite;
}
@keyframes loading-bar { to { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) {
  .topic-loading::before, .formula-loading::before { animation: none; }
}
.topic-btn {
  display: flex; align-items: center; gap: 12px; width: 100%;
  min-height: var(--tap); padding: 12px 14px;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius);
  font-family: var(--sans); font-size: 16px; text-align: left; color: var(--ink);
  cursor: pointer;
}
.topic-btn:hover { border-color: var(--navy); background: #fbfaf7; }
.topic-btn .topic-name { flex: 1 1 auto; font-weight: 600; }
.topic-btn .topic-state { flex: 0 0 auto; }

/* minmax(0, ...) so a long value — a topic name, a date, a rand figure —
   cannot refuse to shrink and shove its neighbour off a 375px screen. The
   content here is short today; the guard costs nothing and does not depend on
   it staying short. */
.stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 16px; }
@media (min-width: 520px) { .stats { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.stat-label { font-size: 12px; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); font-weight: 700; }
.stat-value { font-family: var(--serif); font-size: 22px; color: var(--navy); }
.stat-value--small { font-size: 16px; font-family: var(--sans); font-weight: 600; }

.reason {
  border-left: 3px solid var(--gold-line);
  padding: 2px 0 2px 14px;
  color: var(--ink-soft); font-size: 16px;
}

.evidence {
  margin-top: 12px; font-size: 14px; color: var(--muted);
  border-top: 1px solid var(--line-soft); padding-top: 10px;
}
.evidence dl { display: grid; grid-template-columns: auto 1fr; gap: 2px 12px; margin: 0; }
.evidence dt { font-weight: 600; color: var(--ink-soft); }
.evidence dd { margin: 0; font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------- states --- */

.status-line { font-size: 15px; color: var(--muted); }
.status-line[data-tone='error'] { color: var(--wrong); font-weight: 600; }

.field { display: block; }
.field-label { display: block; font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin-bottom: 6px; }
select.field-input {
  width: 100%; min-height: var(--tap); padding: 10px 12px;
  font-size: 16px; font-family: var(--sans); color: var(--ink);
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius);
}
/* Native file controls have a wide intrinsic button/filename layout. Let that
   internal control shrink with its grid cell instead of widening a phone page. */
input[type='file'].field-input {
  width: 100%; max-width: 100%; min-width: 0;
}

.hidden { display: none !important; }

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

.footnote { font-size: 13px; color: var(--muted); text-align: center; padding-top: 8px; }

@media (max-width: 380px) {
  body { font-size: 16px; }
  .question { font-size: 18px; }
  main { padding: 16px 12px 48px; }
  .card { padding: 16px; }
}

/* ==========================================================================
   TEACHER DASHBOARD
   Same rules as everything else: no motion, no web fonts, colour never alone.
   ========================================================================== */

/* Demo notice. Deliberately loud — a screen full of fictional children must
   never be mistaken for a real class. */
.demo-banner {
  background: #4a3a08;
  color: #fff;
  border-bottom: 2px solid var(--gold-line);
  padding: 10px 20px;
  font-size: 14px;
  font-weight: 600;
}
.demo-banner-inner { max-width: 980px; margin: 0 auto; }
.demo-banner strong { color: var(--gold-line); letter-spacing: 0.5px; }

.wide { max-width: 980px; }

.classbar {
  display: flex; flex-wrap: wrap; gap: 8px 20px; align-items: baseline;
  padding-bottom: 4px;
}
.classbar .class-name { font-family: var(--serif); font-size: 22px; color: var(--navy); font-weight: 700; }
.classbar .class-meta { font-size: 15px; color: var(--muted); }

.tally { display: flex; flex-wrap: wrap; gap: 10px; }
.tally-item {
  flex: 1 1 110px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--paper); padding: 12px 14px;
}
.tally-value { font-family: var(--serif); font-size: 26px; color: var(--navy); line-height: 1.1; }
.tally-label { font-size: 12px; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); font-weight: 700; }

/* ------------------------------------------------------------- alerts --- */

.alert {
  background: var(--paper);
  border: 1px solid var(--line);
  border-left: 4px solid var(--muted);
  border-radius: var(--radius);
  padding: 18px 20px;
}
/* Severity is shown by a WORD in the tag as well as the edge colour. */
.alert[data-tone='act-now']  { border-left-color: var(--wrong); }
.alert[data-tone='soon']     { border-left-color: var(--gold); }
.alert[data-tone='when-you-can'] { border-left-color: var(--navy-mid); }
.alert[data-tone='good-news'] { border-left-color: var(--correct); }

.alert-head { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; margin-bottom: 6px; }
.alert-tag {
  font-size: 11px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase;
  padding: 3px 8px; border-radius: 3px; border: 1px solid var(--line);
  color: var(--ink-soft); background: var(--cream); white-space: nowrap;
}
.alert[data-tone='act-now'] .alert-tag { color: var(--wrong); border-color: var(--wrong); background: var(--wrong-bg); }
.alert[data-tone='good-news'] .alert-tag { color: var(--correct); border-color: var(--correct); background: var(--correct-bg); }
.alert-title { font-family: var(--serif); font-size: 19px; color: var(--navy); font-weight: 700; flex: 1 1 auto; }
.alert-why { color: var(--ink-soft); font-size: 16px; margin-bottom: 12px; }

.who { border-top: 1px solid var(--line-soft); padding-top: 10px; }
.who-head { font-size: 12px; text-transform: uppercase; letter-spacing: 1px; font-weight: 700; color: var(--muted); margin-bottom: 6px; }
.who-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 3px; }
.who-list li { display: flex; gap: 12px; flex-wrap: wrap; font-size: 15px; }
.who-name { flex: 0 0 190px; font-weight: 600; color: var(--ink); }
.who-evidence { flex: 1 1 200px; color: var(--muted); font-variant-numeric: tabular-nums; }

/* Still reads as a link, still a 44px target. A "Show all" that a thumb
   misses on a shared classroom tablet is not a minor styling detail. */
.linkish {
  display: inline-flex; align-items: center;
  background: none; border: 0; padding: 0; min-height: var(--tap);
  color: var(--navy); font-family: var(--sans); font-size: 14px; font-weight: 600;
  text-decoration: underline; cursor: pointer;
}

.proposal {
  margin-top: 12px; padding: 12px 14px;
  background: var(--cream); border: 1px solid var(--line); border-radius: var(--radius);
}
.proposal-grid { display: grid; grid-template-columns: auto 1fr; gap: 4px 14px; font-size: 15px; }
.proposal-grid dt { font-weight: 700; color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: 0.8px; }
.proposal-grid dd { margin: 0; color: var(--ink); }

.adjust { display: grid; gap: 10px; margin-top: 12px; }
@media (min-width: 620px) { .adjust { grid-template-columns: repeat(3, 1fr); } }
.adjust label { display: block; }

.preview-list { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 8px; }
.preview-list li {
  padding: 10px 12px; background: var(--cream);
  border: 1px solid var(--line-soft); border-radius: var(--radius); font-size: 15px;
}
.preview-q { color: var(--ink); }
.preview-a { color: var(--muted); font-size: 14px; margin-top: 4px; }
.marking-contract {
  margin-top: 10px;
  padding: 10px 12px;
  border-left: 3px solid var(--gold);
  background: var(--paper-warm, #faf7ef);
  border-radius: 4px;
}
.marking-step-list { margin: 6px 0 0 20px; padding: 0; }
.marking-step-list li { padding: 2px 0; }

/* ------------------------------------------------ assessment marking ----- */
.review-page {
  padding-top: 28px;
  padding-bottom: 48px;
  min-height: calc(100vh - 180px);
}
.review-page > .status-line:first-child { min-height: 1.5em; }
.action-centre-filters {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
  align-items: end;
}
.action-centre-filters .field { margin: 0; min-width: 0; }
.action-centre-filters .field-input { width: 100%; }
.action-search { grid-column: 1 / -1; }
.action-filter-reset { justify-self: start; }

.bulk-action-bar {
  display: flex;
  align-items: center;
  gap: 10px 16px;
  flex-wrap: wrap;
  padding: 12px;
  border: 1px solid var(--line);
  background: var(--cream);
}
.bulk-select {
  min-height: var(--tap);
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
  cursor: pointer;
}
.bulk-select input, .queue-item-select input { width: 20px; height: 20px; accent-color: var(--navy); }
.bulk-action-bar .btn-row { margin-left: auto; }
.btn.is-disabled, .btn[aria-disabled='true'] {
  color: var(--muted);
  border-color: var(--line);
  background: var(--paper);
  box-shadow: none;
  cursor: not-allowed;
  text-decoration: line-through;
}

.action-queues { display: grid; gap: 14px; }
.action-queue {
  border: 1px solid var(--line);
  border-top: 3px solid var(--navy);
  background: var(--paper);
}
.action-queue-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 14px;
  background: var(--cream);
  border-bottom: 1px solid var(--line);
}
.queue-count {
  min-width: 30px;
  padding: 2px 8px;
  border: 1px solid var(--navy);
  border-radius: 999px;
  color: var(--navy);
  text-align: center;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}
.queue-empty { padding: 14px; color: var(--muted); }
.queue-item {
  display: grid;
  grid-template-columns: var(--tap) minmax(0, 1fr) minmax(150px, auto);
  align-items: start;
  gap: 10px;
  padding: 13px 14px;
  border-bottom: 1px solid var(--line-soft);
}
.queue-item:last-child { border-bottom: 0; }
.queue-item-select {
  width: var(--tap);
  min-height: var(--tap);
  display: grid;
  place-items: center;
  cursor: pointer;
}
.queue-item-select:has(input:focus-visible) { outline: 3px solid var(--gold-line); outline-offset: 1px; }
.queue-item-body { min-width: 0; }
.queue-item-body h4 { margin: 0; color: var(--navy); font: 700 17px/1.25 var(--sans); }
.queue-item-meta { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0; }
.queue-reason, .queue-evidence { margin: 4px 0 0; font-size: 14px; }
.queue-evidence { color: var(--muted); }
.queue-item-actions { display: grid; gap: 6px; align-self: center; }
.queue-item-actions .btn { width: 100%; }
.confidence-badge {
  display: inline-flex;
  align-items: center;
  min-height: 26px;
  padding: 2px 8px;
  border: 1px solid var(--line);
  font-size: 12px;
  font-weight: 750;
}
.confidence-badge--confirmed { border-color: var(--correct); background: var(--correct-bg); color: var(--correct); }
.confidence-badge--probable { border-color: var(--gold); background: #fff8df; color: #6d5313; }
.confidence-badge--emerging { border-color: var(--muted); color: var(--ink-soft); }
.calibration-summary {
  padding: 10px 12px;
  border: 1px solid var(--gold-line);
  border-radius: 8px;
  background: #fffaf0;
  color: var(--navy);
  font-weight: 700;
}
.diagnostic-moderation-actions { display: grid; gap: 6px; margin-top: 4px; }

.diagnostic-card, .assignment-review-card {
  border: 1px solid var(--line);
  border-left: 4px solid var(--gold-line);
  background: var(--paper);
  padding: 16px;
}
.diagnostic-summary {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 8px;
  margin: 12px 0;
}
.mastery-state { display: grid; gap: 2px; padding: 9px; border: 1px solid var(--line-soft); }
.mastery-state--needs-attention { border-left: 3px solid var(--wrong); }
.mastery-state--developing { border-left: 3px solid var(--gold-line); }
.mastery-state--proficient, .mastery-state--mastered { border-left: 3px solid var(--correct); }
.mastery-label { display: inline-block; color: var(--navy); margin-bottom: 4px; }
.improvement-note { color: var(--correct); font-weight: 700; }
.script-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line-soft);
}
.script-row:last-child { border-bottom: 0; }
.sitting-review { border-top: 3px solid var(--navy); padding-top: 16px; }
.question-review { overflow-wrap: anywhere; }
.learner-answer, .correct-answer, .learner-working {
  padding: 9px 11px;
  background: #f7f4ed;
  border: 1px solid var(--line-soft);
}
.question-mark { display: flex; align-items: baseline; flex-wrap: wrap; gap: 10px; }
.question-mark > strong { color: var(--navy); font-size: 24px; }
.teacher-adjustment { border: 1px solid var(--gold-line); border-left: 4px solid var(--gold); background: var(--paper-warm); padding: 12px; }
.teacher-adjustment > * { margin: 0 0 6px; }
.teacher-adjustment > *:last-child { margin-bottom: 0; }
.teacher-adjustment-reason { font-style: italic; color: var(--ink); }
.mark-step { border: 1px solid var(--line); border-left: 4px solid var(--line); padding: 11px; }
.mark-step--correct { border-left-color: var(--correct); background: var(--correct-bg); }
.mark-step--incorrect, .mark-step--origin { border-left-color: var(--wrong); background: var(--wrong-bg); }
.mark-step--review { border-left-color: var(--gold-line); }
.mark-step-head { display: flex; justify-content: space-between; gap: 12px; }
.error-origin { display: block; color: var(--wrong); font-size: 13px; letter-spacing: .06em; }
.follow-through { display: block; color: var(--correct); font-weight: 700; }
.marking-reason { font-weight: 650; }
.submitted-working, .diagnostic-list { margin: 4px 0 8px 22px; }
.override-form {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 3fr) auto;
  gap: 8px;
  align-items: start;
  border-top: 1px solid var(--line);
  padding-top: 12px;
}
@media (max-width: 620px) {
  .script-row, .override-form { grid-template-columns: 1fr; }
  .action-centre-filters { grid-template-columns: 1fr; }
  .action-search { grid-column: auto; }
  .action-filter-reset { width: 100%; }
  .bulk-action-bar { align-items: stretch; }
  .bulk-action-bar .btn-row { width: 100%; margin-left: 0; }
  .bulk-action-bar .btn { flex-basis: 100%; }
  .queue-item { grid-template-columns: var(--tap) minmax(0, 1fr); }
  .queue-item-actions { grid-column: 1 / -1; grid-template-columns: 1fr; }
}

.alert-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; }

/* -------------------------------------------------------------- table --- */

/* POSITION MATTERS HERE. An absolutely positioned descendant is only clipped
   by an overflow ancestor that is itself positioned. Without this, an .sr-only
   column heading inside a wide table escaped the horizontal scroller and made
   the whole PAGE scroll sideways. */
/* A WIDE TABLE ON A PHONE.
 *
 * A dashboard table is 550 to 730px wide and a phone gives it 318. The
 * wrapper scrolls, which keeps the page itself from dragging sideways — but
 * scrolling it was invisible. The table simply stopped at the right edge,
 * mid-word, with four hundred pixels of columns behind it and nothing to say
 * so. A teacher checking a class on their phone would never know the marks
 * column existed.
 *
 * These are scroll shadows. The two `local` layers are painted in the paper
 * colour and travel WITH the content, so at each end they cover the shadow
 * behind them; the two `scroll` layers stay pinned to the edges. The result
 * is a soft shade that appears only on the side that has more to show, and
 * disappears when you reach the end of it — the affordance a native scroll
 * area has and an overflow container does not.
 *
 * No JavaScript, so it works on every one of these tables at once and cannot
 * fall out of step with the markup. */
.table-wrap {
  overflow-x: auto;
  position: relative;
  -webkit-overflow-scrolling: touch;

  background-image:
    linear-gradient(to right, var(--paper) 40%, transparent),
    linear-gradient(to left, var(--paper) 40%, transparent),
    linear-gradient(to right, var(--edge-shade), transparent),
    linear-gradient(to left, var(--edge-shade), transparent);
  background-position: left center, right center, left center, right center;
  background-repeat: no-repeat;
  background-size: 36px 100%, 36px 100%, 16px 100%, 16px 100%;
  background-attachment: local, local, scroll, scroll;
}

/* The header row sits above the shadow so it is never dimmed by it. */
.table-wrap table.data th { position: relative; }
table.data { width: 100%; border-collapse: collapse; font-size: 15px; }
table.data th, table.data td { text-align: left; padding: 9px 12px; border-bottom: 1px solid var(--line-soft); }
table.data th {
  font-size: 12px; text-transform: uppercase; letter-spacing: 1px;
  color: var(--muted); font-weight: 700; border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
table.data td.num { font-variant-numeric: tabular-nums; white-space: nowrap; }
table.data tr:last-child td { border-bottom: 0; }
.skill-note { font-size: 13px; color: var(--muted); }

/* ------------------------------------------------------------- groups --- */

.group-grid { display: grid; gap: 12px; }
@media (min-width: 700px) { .group-grid { grid-template-columns: 1fr 1fr; } }
.group {
  border: 1px solid var(--line); border-top: 3px solid var(--gold-line);
  border-radius: var(--radius); background: var(--paper); padding: 16px;
}
.group-head { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; margin-bottom: 4px; }
.group-name { font-family: var(--serif); font-size: 17px; color: var(--navy); font-weight: 700; }
.group-count { font-size: 13px; color: var(--muted); font-weight: 700; white-space: nowrap; }
.group-blurb { font-size: 14px; color: var(--muted); margin-bottom: 10px; }

.tabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 12px; }
.tab {
  min-height: var(--tap); padding: 8px 14px;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius);
  font-family: var(--sans); font-size: 15px; color: var(--ink); cursor: pointer;
}
.tab[aria-selected='true'] { background: var(--navy); border-color: var(--navy); color: #fff; font-weight: 600; }

/* THE NAV SCROLLS RATHER THAN WRAPS.
   Five destinations do not fit across 375px. Wrapping broke them mid-item —
   "Build work" became "Build" over "work", which reads as two links and as a
   layout that has come apart. Swiping a row of links sideways is the ordinary
   phone pattern and keeps every label whole.
   `-webkit-overflow-scrolling` keeps the momentum flick on iOS Safari. */
.navlinks {
  /* TIGHT ENOUGH THAT REPORTS FITS. At 375px the five labels wanted 381px in a
     335px bar, so the last one sat off the edge and had to be found by
     swiping. Closing the gap and the padding recovers about 50px — enough for
     the whole row — without wrapping, which the note above rejects, and
     without shrinking the text. The scroll below stays as the fallback for a
     narrower phone. */
  display: flex; gap: 3px;
  /* WRAPS RATHER THAN HIDES. The note above rejected wrapping because
     "Build work" split into two lines and read as two links — but that was
     text wrapping INSIDE a label, and `white-space: nowrap` on the links
     below now prevents it. With that in place a wrap moves whole labels to a
     second row and every one stays intact.
     It only wraps when it has to. A teacher sees five links on one line; a
     school administrator, who also gets "My school", sees six across two
     rows instead of one row with Reports hidden past the edge. */
  flex-wrap: wrap;
  row-gap: 0;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.navlinks::-webkit-scrollbar { display: none; }
.navlinks a { flex: 0 0 auto; white-space: nowrap; }
.navlinks a {
  display: inline-flex; align-items: center;
  min-height: var(--tap); padding: 0 6px;
  color: var(--gold-line); font-size: 14px; font-weight: 600; text-decoration: none;
}
.navlinks a:hover { text-decoration: underline; }
.navlinks a[aria-current='page'] { color: #fff; text-decoration: underline; }

/* ==========================================================================
   ASSIGNMENT / TEST BUILDER
   ========================================================================== */

.step { counter-increment: step; }
.step-head { display: flex; gap: 10px; align-items: baseline; margin-bottom: 4px; }
.step-num {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--navy); color: #fff; font-size: 13px; font-weight: 700;
}
.step-title { font-family: var(--serif); font-size: 19px; color: var(--navy); font-weight: 700; }

.chip-grid { display: grid; gap: 8px; grid-template-columns: 1fr; }
@media (min-width: 560px) { .chip-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 860px) { .chip-grid--3 { grid-template-columns: 1fr 1fr 1fr; } }

.chip {
  display: block; width: 100%; text-align: left;
  min-height: var(--tap); padding: 10px 14px;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius);
  font-family: var(--sans); font-size: 15px; color: var(--ink); cursor: pointer;
}
.chip:hover { border-color: var(--navy); background: #fbfaf7; }
.chip[aria-pressed='true'] { border-color: var(--navy); border-width: 2px; padding: 9px 13px; background: #f7f9fc; }
.chip-name { font-weight: 600; display: block; }
.chip-note { font-size: 13px; color: var(--muted); display: block; }
.chip[aria-pressed='true'] .chip-name::before { content: '✓ '; color: var(--navy); }

/* Role entry points -------------------------------------------------------
   These are deliberately cards rather than a role dropdown. A teacher, HOD,
   sponsor and Stratford administrator have different jobs, and each person
   should recognise their destination before being asked for credentials. */
.portal-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
}
@media (min-width: 680px) { .portal-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }
.portal-choice {
  display: block;
  min-height: 86px;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink);
  text-decoration: none;
}
.portal-choice:hover,
.portal-choice:focus-visible { border-color: var(--navy); background: #fbfaf7; }
.portal-choice[aria-current='page'] {
  border: 2px solid var(--navy);
  padding: 13px 15px;
  background: #f7f9fc;
  box-shadow: inset 4px 0 0 var(--gold-line);
}
.portal-choice-title {
  display: block;
  color: var(--navy);
  font-family: var(--serif);
  font-size: 18px;
  font-weight: 700;
}
.portal-choice-note { display: block; margin-top: 4px; color: var(--muted); font-size: 14px; }
.portal-entry { border-top: 4px solid var(--gold-line); }
.portal-entry + #panel-signed-in,
.portal-entry + #panel-signed-in + #panel-signin { margin-top: 18px; }

/* ONE COLUMN ON A PHONE.
 *
 * This was the only grid on the dashboard that went two-up at ANY width —
 * every other one waits for a breakpoint. On a 375px screen a select and its
 * label do not fit in half of it, and `1fr` will not shrink a track below its
 * content, so one field took 341px of a 318px row and pushed the next one
 * clean off the edge. The whole page then scrolled sideways.
 *
 * minmax(0, 1fr) lets a track go narrower than the thing inside it, which is
 * what stops a long option from doing this again at any width. */
.rules-grid { display: grid; gap: 12px; grid-template-columns: minmax(0, 1fr); }
.rules-grid > * { min-width: 0; }
@media (min-width: 560px) { .rules-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 700px) { .rules-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* --------------------------------------------------------- blueprint --- */

.blueprint { border-top: 3px solid var(--navy); }
.bp-headline {
  display: flex; flex-wrap: wrap; gap: 6px 18px; align-items: baseline;
  padding-bottom: 12px; border-bottom: 1px solid var(--line-soft); margin-bottom: 14px;
}
.bp-figure { font-family: var(--serif); font-size: 24px; color: var(--navy); font-weight: 700; }
.bp-figure small { font-family: var(--sans); font-size: 12px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: 1px; display: block; }

.bp-section { margin-top: 16px; }
.bp-section h3 {
  margin: 0 0 8px; font-size: 12px; text-transform: uppercase; letter-spacing: 1px;
  color: var(--muted); font-weight: 700;
}

.bp-row { display: flex; align-items: center; gap: 10px; font-size: 14px; padding: 3px 0; }
.bp-row-label { flex: 0 0 40%; color: var(--ink); }
.bp-row-bar { flex: 1 1 auto; height: 10px; background: var(--line-soft); border-radius: 2px; overflow: hidden; }
.bp-row-fill { height: 100%; background: var(--navy-mid); }
.bp-row-fill[data-state='over'] { background: var(--gold); }
.bp-row-fill[data-state='under'] { background: #8fa0bb; }
.bp-row-value { flex: 0 0 92px; text-align: right; color: var(--muted); font-variant-numeric: tabular-nums; }

.bp-unavailable {
  padding: 12px 14px; background: var(--cream);
  border: 1px dashed var(--line); border-radius: var(--radius);
  font-size: 14px; color: var(--ink-soft);
}

.warn-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.warn-list li {
  display: flex; gap: 10px; font-size: 14px; padding: 8px 10px;
  border-radius: var(--radius); border: 1px solid var(--line-soft); background: var(--cream);
}
.warn-list li[data-severity='high'] { border-color: var(--wrong); background: var(--wrong-bg); color: var(--wrong); }
.warn-list li[data-severity='medium'] { border-color: var(--gold-line); background: #fdfaf2; }
.warn-sev { flex: 0 0 auto; font-weight: 700; text-transform: uppercase; font-size: 11px; letter-spacing: 1px; }

.seed-note {
  font-size: 13px; color: var(--muted); font-family: ui-monospace, Menlo, Consolas, monospace;
  border-top: 1px solid var(--line-soft); padding-top: 10px; margin-top: 14px;
}

/* ==========================================================================
   PAPER IMPORT — review and verify
   ========================================================================== */

.paste-area {
  width: 100%; min-height: 190px; padding: 12px 14px;
  font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 14px; line-height: 1.5;
  color: var(--ink); background: var(--paper);
  border: 1px solid var(--line); border-radius: var(--radius); resize: vertical;
}
.paste-area:focus { border-color: var(--navy); }

.progress-bar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 12px 14px; background: var(--cream);
  border: 1px solid var(--line); border-radius: var(--radius);
}
.progress-count { font-family: var(--serif); font-size: 20px; color: var(--navy); font-weight: 700; }
.progress-note { font-size: 14px; color: var(--muted); flex: 1 1 200px; }

.item {
  background: var(--paper); border: 1px solid var(--line);
  border-left: 4px solid var(--muted); border-radius: var(--radius); padding: 16px;
}
.item[data-status='verified'] { border-left-color: var(--correct); }
.item[data-status='edited']   { border-left-color: var(--correct); }
.item[data-status='rejected'] { border-left-color: var(--line); opacity: 0.6; }
.item[data-status='unverified'][data-flagged='true'] { border-left-color: var(--gold); }

.item-head { display: flex; gap: 10px; align-items: baseline; margin-bottom: 8px; }
.item-num {
  flex: 0 0 auto; font-family: var(--serif); font-weight: 700;
  color: var(--navy); font-size: 17px; min-width: 28px;
}
.item-status {
  margin-left: auto; font-size: 11px; font-weight: 700; letter-spacing: 1px;
  text-transform: uppercase; padding: 3px 8px; border-radius: 3px;
  border: 1px solid var(--line); background: var(--cream); color: var(--ink-soft);
  white-space: nowrap;
}
.item[data-status='verified'] .item-status,
.item[data-status='edited'] .item-status { border-color: var(--correct); color: var(--correct); background: var(--correct-bg); }
.item[data-status='unverified'][data-flagged='true'] .item-status { border-color: var(--gold); color: var(--gold); background: #fdfaf2; }

.item-text {
  width: 100%; min-height: 52px; padding: 8px 10px; font-size: 16px;
  font-family: var(--sans); color: var(--ink); line-height: 1.45;
  border: 1px solid var(--line-soft); border-radius: var(--radius); resize: vertical;
}

.item-fields { display: grid; gap: 10px; margin-top: 10px; }
@media (min-width: 700px) { .item-fields { grid-template-columns: 2fr 1.4fr 0.8fr 1fr 1.2fr; } }
.marking-editor { margin-top: 12px; border: 1px solid var(--line-soft); padding: 10px; }
.marking-editor > summary { cursor: pointer; color: var(--navy); font-weight: 700; }
.marking-editor[open] > summary { margin-bottom: 10px; }

.confidence {
  display: inline-block; font-size: 12px; font-weight: 700;
  padding: 2px 7px; border-radius: 3px; border: 1px solid var(--line);
  background: var(--cream); color: var(--ink-soft); margin-left: 6px;
}
.confidence[data-band='low'] { border-color: var(--wrong); color: var(--wrong); background: var(--wrong-bg); }
.confidence[data-band='mid'] { border-color: var(--gold-line); color: var(--gold); background: #fdfaf2; }
.confidence[data-band='high'] { border-color: var(--correct); color: var(--correct); background: var(--correct-bg); }

.flag-list { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 4px; }
.flag-list li {
  font-size: 13px; padding: 6px 10px; border-radius: var(--radius);
  background: #fdfaf2; border: 1px solid var(--gold-line); color: var(--ink-soft);
}
.item-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.item-actions .btn { min-width: 110px; }

/* ==========================================================================
   REPORTS
   ========================================================================== */

.action-line {
  display: flex; gap: 10px; align-items: flex-start;
  margin-top: 12px; padding: 10px 12px;
  background: var(--cream); border-left: 3px solid var(--navy);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 15px; color: var(--ink);
}
.action-line::before {
  content: 'DO'; flex: 0 0 auto;
  font-size: 11px; font-weight: 700; letter-spacing: 1px;
  color: var(--navy); padding-top: 2px;
}
/* Labels drawn by CSS sit outside the translation walker's reach, so the
   language attribute it sets on <html> carries them instead. */
html[lang='af'] .action-line::before { content: 'DOEN'; }
html[lang='tn-ZA'] .action-line::before { content: 'DIRA'; }

/* attainment matrix ------------------------------------------------------- */

.matrix-wrap { overflow-x: auto; }
table.matrix { border-collapse: collapse; font-size: 14px; min-width: 100%; }
table.matrix th, table.matrix td { padding: 6px 8px; border-bottom: 1px solid var(--line-soft); }
table.matrix thead th {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.8px;
  color: var(--muted); font-weight: 700; text-align: center;
  border-bottom: 1px solid var(--line); vertical-align: bottom;
}
table.matrix thead th.name-col, table.matrix td.name-col {
  text-align: left; white-space: nowrap; position: sticky; left: 0;
  background: var(--paper); border-right: 1px solid var(--line-soft);
}
table.matrix td.cell { text-align: center; }

/* The letter carries the meaning; the tint only reinforces it. */
.mark {
  display: inline-block; min-width: 30px; padding: 3px 6px;
  border-radius: 3px; font-weight: 700; font-size: 13px;
  border: 1px solid var(--line); background: var(--paper); color: var(--muted);
}
.mark[data-state='verified']    { border-color: var(--correct); background: var(--correct-bg); color: var(--correct); }
.mark[data-state='partial']     { border-color: var(--gold-line); background: #fdfaf2; color: var(--gold); }
.mark[data-state='attempted']   { border-color: var(--wrong); background: var(--wrong-bg); color: var(--wrong); }
.mark[data-state='not-started'] { border-style: dashed; color: var(--muted); }

.legend { display: flex; flex-wrap: wrap; gap: 14px; font-size: 13px; color: var(--muted); margin-bottom: 12px; }
.legend span { display: inline-flex; align-items: center; gap: 6px; }

.verdict-pill {
  display: inline-block; padding: 3px 10px; border-radius: 999px;
  font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.8px;
  border: 1px solid var(--line); background: var(--cream); color: var(--ink-soft);
}
.verdict-pill[data-v='worked']    { border-color: var(--correct); color: var(--correct); background: var(--correct-bg); }
.verdict-pill[data-v='partial']   { border-color: var(--gold-line); color: var(--gold); background: #fdfaf2; }
.verdict-pill[data-v='no-change'] { border-color: var(--wrong); color: var(--wrong); background: var(--wrong-bg); }

.state-pill {
  display: inline-block; padding: 2px 8px; border-radius: 3px;
  font-size: 12px; font-weight: 700; border: 1px solid var(--line);
  background: var(--cream); color: var(--ink-soft);
}
.state-pill[data-s='stopped'], .state-pill[data-s='never-started'] {
  border-color: var(--wrong); color: var(--wrong); background: var(--wrong-bg);
}
.state-pill[data-s='low'] { border-color: var(--gold-line); color: var(--gold); background: #fdfaf2; }

/* printing — a teacher's moderation file ---------------------------------- */
@media print {
  .masthead, .demo-banner, .navlinks, .btn, .btn-row { display: none !important; }
  body { background: #fff; font-size: 11pt; }
  main { max-width: none; padding: 0; }
  .card { border: 1px solid #999; break-inside: avoid; page-break-inside: avoid; }
  .matrix-wrap { overflow: visible; }
  table.matrix { font-size: 9pt; }
  .action-line { background: #f2f2f2; }
  h2 { page-break-after: avoid; }
}

/* ==========================================================================
   GRADE THEMES

   §18 rules out game worlds, creatures and animation — and none of that is
   here. What IS here is the observation that a nine-year-old and a
   fifteen-year-old are not the same reader.

   So the interface MATURES with the learner. Across Grades 4 to 9 the accent
   cools from warm orange to slate, corners tighten from 10px to 3px, and body
   text steps down from 18px to 16px. A Grade 4 screen is warm, round and
   generous; a Grade 9 screen is sober and exam-like. Nothing is added for
   either — the same components simply carry different tokens.

   Every accent clears 4.5:1 against white, because these carry text.
   Set with data-grade="N" on <html>. Costs nothing: no images, no fonts.
   ========================================================================== */

:root {
  --accent:       var(--navy);
  --accent-soft:  var(--cream);
  --accent-line:  var(--line);
  --grade-radius: var(--radius);
  --grade-body:   17px;
  --grade-lead:   1.55;
}

/* Grade 4 — warm, round, generous. Terracotta. */
[data-grade='4'] {
  --accent: #b03d16; --accent-soft: #fff4ed; --accent-line: #f0c4ad;
  --grade-radius: 10px; --grade-body: 18px; --grade-lead: 1.65;
}
/* Grade 5 — growing. Forest green. */
[data-grade='5'] {
  --accent: #15703b; --accent-soft: #f0fbf3; --accent-line: #b8e0c4;
  --grade-radius: 9px; --grade-body: 18px; --grade-lead: 1.62;
}
/* Grade 6 — exploring. Teal. */
[data-grade='6'] {
  --accent: #0f6f68; --accent-soft: #eefaf8; --accent-line: #a9dcd7;
  --grade-radius: 8px; --grade-body: 17px; --grade-lead: 1.58;
}
/* Grade 7 — senior phase begins. Indigo. */
[data-grade='7'] {
  --accent: #4338ca; --accent-soft: #f4f3ff; --accent-line: #c3bff2;
  --grade-radius: 6px; --grade-body: 17px; --grade-lead: 1.55;
}
/* Grade 8 — the Stratford navy. Grown-up. */
[data-grade='8'] {
  --accent: #0b2d5c; --accent-soft: #f1f5fb; --accent-line: #bccbdf;
  --grade-radius: 5px; --grade-body: 16px; --grade-lead: 1.52;
}
/* Grade 9 — exam year. Slate, tight, serious. */
[data-grade='9'] {
  --accent: #334155; --accent-soft: #f6f8fa; --accent-line: #c3ccd6;
  --grade-radius: 3px; --grade-body: 16px; --grade-lead: 1.5;
}

[data-grade] body { font-size: var(--grade-body); line-height: var(--grade-lead); }
[data-grade] .card,
[data-grade] .btn,
[data-grade] .choice,
[data-grade] .field-input,
[data-grade] .typed-input { border-radius: var(--grade-radius); }

[data-grade] .card--accent { border-top-color: var(--accent); }
[data-grade] .btn { background: var(--accent); border-color: var(--accent); }
[data-grade] .btn:hover { background: var(--ink); border-color: var(--ink); }
[data-grade] .btn--secondary { background: var(--paper); color: var(--accent); border-color: var(--accent-line); }
[data-grade] .btn--secondary:hover { background: var(--accent-soft); border-color: var(--accent); }
[data-grade] .card-label { color: var(--accent); }
[data-grade] .masthead { background: var(--accent); border-bottom-color: var(--accent-line); }
[data-grade] .masthead .descriptor,
[data-grade] .navlinks a { color: #fff; }
[data-grade] h2, [data-grade] h3, [data-grade] .step-title { color: var(--accent); }
[data-grade] .reason { border-left-color: var(--accent); }
[data-grade] .pill--level { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
[data-grade] .choice[aria-pressed='true'] { border-color: var(--accent); }
[data-grade] .choice:hover { border-color: var(--accent); background: var(--accent-soft); }
[data-grade] .meter-fill { background: var(--accent); }
[data-grade] :where(a, button, input, select, [tabindex]):focus-visible { outline-color: var(--accent); }

/* ==========================================================================
   LANDING
   ========================================================================== */

.hero {
  background: var(--navy);
  color: #fff;
  border-bottom: 3px solid var(--gold-line);
  padding: 40px 20px 44px;
}
.hero-inner { max-width: 980px; margin: 0 auto; }
.hero .eyebrow {
  font-size: 12px; letter-spacing: 2px; text-transform: uppercase;
  color: var(--gold-line); font-weight: 700; margin-bottom: 10px;
}
.hero h1 {
  font-family: var(--serif); font-size: clamp(30px, 5vw, 46px);
  line-height: 1.1; color: #fff; margin-bottom: 14px; font-weight: 700;
}
.hero p { font-size: 18px; line-height: 1.55; color: #d7e0ee; max-width: 60ch; }
.hero-facts {
  display: flex; flex-wrap: wrap; gap: 10px 28px; margin-top: 22px;
  padding-top: 18px; border-top: 1px solid rgba(255,255,255,.16);
}
.hero-fact { font-size: 14px; color: #c6d2e4; }
.hero-fact strong { display: block; font-family: var(--serif); font-size: 21px; color: var(--gold-line); }

.section-head { max-width: 980px; margin: 0 auto 4px; }
.section-head h2 { font-size: 24px; }
.section-head p { color: var(--muted); font-size: 16px; margin-top: 6px; }

/* the six grade cards ---------------------------------------------------- */

.grade-grid {
  display: grid; gap: 14px; grid-template-columns: 1fr;
}
@media (min-width: 560px) { .grade-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 900px) { .grade-grid { grid-template-columns: repeat(3, 1fr); } }

.grade-card {
  display: block; width: 100%; text-align: left; cursor: pointer;
  padding: 20px; text-decoration: none;
  background: var(--g-soft);
  border: 1px solid var(--g-line);
  border-top: 4px solid var(--g-accent);
  border-radius: var(--g-radius);
  color: var(--ink);
  font-family: var(--sans);
}
.grade-card:hover { border-color: var(--g-accent); background: var(--paper); }
.grade-card:focus-visible { outline: 3px solid var(--g-accent); outline-offset: 2px; }

.grade-card-top { display: flex; align-items: flex-start; gap: 14px; }
.grade-numeral {
  flex: 0 0 auto; width: 52px; height: 52px;
  display: flex; align-items: center; justify-content: center;
  background: var(--g-accent); color: #fff;
  border-radius: var(--g-radius);
  font-family: var(--serif); font-size: 26px; font-weight: 700; line-height: 1;
}
.grade-card-titles { flex: 1 1 auto; min-width: 0; }
.grade-card h3 { font-size: 20px; color: var(--g-accent); margin-bottom: 2px; }
.grade-card .phase {
  font-size: 12px; text-transform: uppercase; letter-spacing: 1.2px;
  font-weight: 700; color: var(--muted);
}
.grade-card .blurb { font-size: 15px; color: var(--ink-soft); margin-top: 12px; line-height: 1.5; }
.grade-card .topics {
  margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--g-line);
  font-size: 13px; color: var(--muted); display: flex; justify-content: space-between; gap: 10px;
}
.grade-card .go { color: var(--g-accent); font-weight: 700; }

/* educator card ---------------------------------------------------------- */

.educator {
  display: grid; gap: 18px; align-items: center;
  background: var(--navy); color: #fff;
  border-radius: var(--radius); border-top: 4px solid var(--gold-line);
  padding: 24px;
}
@media (min-width: 760px) { .educator { grid-template-columns: 1.5fr 1fr; } }
.educator h3 { color: #fff; font-size: 22px; margin-bottom: 8px; }
.educator p { color: #d7e0ee; font-size: 16px; }
.educator ul { margin: 12px 0 0; padding-left: 18px; color: #c6d2e4; font-size: 15px; }
.educator li { margin-bottom: 4px; }
.educator .btn { background: var(--gold-line); color: var(--navy); border-color: var(--gold-line); }
.educator .btn:hover { background: #fff; border-color: #fff; }
/* ONE COLUMN, AT EVERY WIDTH.
   These three links used to sit two-abreast between 440px and 760px and
   again above 1040px. In English that is fine — "Teacher login" is two short
   words. In Afrikaans the same three read "Onderwyseraanmelding",
   "Skool- / departementshoofaanmelding" and "Borgaanmelding", and no half
   column holds them: they spilled clean through their own gold borders.

   Wrapping alone does not rescue it. The browser ships no Afrikaans
   hyphenation dictionary, so the only break available is mid-word, and
   "Onderwyseraanme / lding" reads as a fault rather than as typesetting.

   Nor is this only a phone problem, which is what made it worth measuring
   rather than reasoning about: the panel sits in a narrow sidebar, so at a
   1280px DESKTOP the two columns came out 170px each — narrower than the
   same two columns on a 450px phone. There is no width at which the second
   column fits the longest label.

   One column gives each button the whole panel, every label lands on one
   line in all three languages, and the buttons finally look like a set. */
.portal-home-links { display: grid; grid-template-columns: 1fr; gap: 9px; }
.portal-home-links .footnote { grid-column: 1 / -1; }

/* ==========================================================================
   UNLOCK / PRICING
   ========================================================================== */

.price-grid { display: grid; gap: 14px; grid-template-columns: 1fr; }
@media (min-width: 700px) { .price-grid { grid-template-columns: 1fr 1fr; } }

.price {
  position: relative;
  border: 1px solid var(--line); border-radius: var(--grade-radius);
  background: var(--paper); padding: 22px;
}
.price--best { border-color: var(--accent); border-width: 2px; padding: 21px; }
.price-tag {
  position: absolute; top: -11px; right: 16px;
  background: var(--accent); color: #fff;
  font-size: 11px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase;
  padding: 4px 10px; border-radius: 999px;
}
.price h3 { font-size: 17px; color: var(--ink); margin-bottom: 8px; }
.price .amount {
  font-family: var(--serif); font-size: 40px; color: var(--accent);
  line-height: 1; font-weight: 700;
}
.price .per { font-size: 15px; color: var(--muted); font-family: var(--sans); font-weight: 400; }
.price .note { font-size: 14px; color: var(--muted); margin-top: 10px; min-height: 40px; }
.price .btn { margin-top: 14px; }

.price-list { list-style: none; margin: 14px 0 0; padding: 0; font-size: 15px; }
.price-list li { padding: 5px 0 5px 22px; position: relative; color: var(--ink-soft); }
.price-list li::before {
  content: '✓'; position: absolute; left: 0; color: var(--accent); font-weight: 700;
}

.code-panel {
  border: 1px dashed var(--accent-line); border-radius: var(--grade-radius);
  background: var(--accent-soft); padding: 20px;
}
.code-row { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 12px; }
.code-input {
  flex: 1 1 200px; min-height: var(--tap); padding: 10px 14px;
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 17px; letter-spacing: 2px; text-transform: uppercase;
  border: 1px solid var(--line); border-radius: var(--grade-radius);
  background: var(--paper); color: var(--ink);
}
.code-input:focus { border-color: var(--accent); }

.locked-note {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 14px 16px; border-radius: var(--grade-radius);
  background: var(--accent-soft); border: 1px solid var(--accent-line);
  font-size: 15px; color: var(--ink-soft);
}

.tabs-lg { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 18px; }
.tab-lg {
  flex: 1 1 160px; min-height: var(--tap); padding: 12px 16px;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--grade-radius); cursor: pointer;
  font-family: var(--sans); font-size: 16px; font-weight: 600; color: var(--ink-soft);
}
.tab-lg[aria-selected='true'] {
  background: var(--accent); border-color: var(--accent); color: #fff;
}

/* ==========================================================================
   THE CREST
   Drawn as vector (web/crest.svg, 837 bytes gzipped). Gold on transparent, so
   the single file sits correctly on the navy hero and on all six grade
   mastheads without a recoloured variant.
   ========================================================================== */

/* The lockup is a link home, so it carries the 44px minimum like any other
   target — on small screens the motto hides and the crest shrinks, which had
   left it at 32px. */
.brand-lockup {
  display: flex; align-items: center; gap: 12px; min-width: 0;
  min-height: var(--tap); text-decoration: none; color: inherit;
}
.crest { flex: 0 0 auto; height: 38px; width: auto; display: block; }
.brand-lockup h1 { line-height: 1.05; }
.brand-motto {
  display: block; font-family: var(--sans);
  font-size: 9px; letter-spacing: 1.6px; text-transform: uppercase;
  color: var(--gold-line); font-weight: 700; margin-top: 2px;
}
[data-grade] .brand-motto { color: rgba(255,255,255,.72); }

@media (max-width: 420px) {
  .crest { height: 32px; }
  .brand-motto { display: none; }
}

/* the crest as the hero's own mark */
.hero-crest { display: flex; align-items: center; gap: 26px; flex-wrap: wrap; }
/* The hero carries the full lockup — shield, ribbon, STRATFORD ACADEMY and the
   ornament — which is tall and narrow (0.57). It needs real height or the
   wordmark under the shield turns to mush, so it runs taller than a bare mark
   would and the copy beside it is centred against it. */
.hero-crest .crest-large { flex: 0 0 auto; height: 170px; width: auto; }
.hero-crest .hero-copy { flex: 1 1 320px; min-width: 0; }
.hero-motto {
  font-family: var(--serif); font-style: italic;
  font-size: 15px; color: var(--gold-line); letter-spacing: .4px;
  margin-top: 14px;
}
@media (max-width: 560px) {
  .hero-crest .crest-large { height: 130px; }
}

/* ==========================================================================
   CLASS BANNER — the learner's own class, on their home page
   ========================================================================== */

.class-banner {
  display: flex; flex-wrap: wrap; align-items: center; gap: 14px 22px;
  padding: 16px 18px;
  background: var(--accent-soft);
  border: 1px solid var(--accent-line);
  border-left: 4px solid var(--accent);
  border-radius: var(--grade-radius);
}
.class-banner-main { flex: 1 1 220px; min-width: 0; }
.class-banner .who { font-family: var(--serif); font-size: 19px; color: var(--accent); font-weight: 700; }
.class-banner .where { font-size: 14px; color: var(--muted); margin-top: 2px; }

.class-code {
  flex: 0 0 auto; text-align: right;
}
.class-code .label {
  display: block; font-size: 11px; text-transform: uppercase; letter-spacing: 1.2px;
  color: var(--muted); font-weight: 700;
}
.class-code .value {
  display: inline-block; margin-top: 4px; padding: 6px 12px;
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 18px; letter-spacing: 2px; font-weight: 700;
  color: var(--accent); background: var(--paper);
  border: 1px dashed var(--accent-line); border-radius: var(--grade-radius);
}

/* ==========================================================================
   MY CLASSES — a teacher with more than one class
   ==========================================================================
   The dashboard used to assume one class and offered a Grade dropdown and a
   Term dropdown, which between them could not name "8B". This strip shows
   every class at once, ordered by need, so the first thing on screen is the
   thing that needs doing.
   ========================================================================== */

.panel-title { font-size: 19px; margin: 0; }

.field-input--lg { font-size: 17px; padding: 12px 14px; min-height: 48px; }

.class-strip {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 12px;
}
/* Reserve the first card row while class summaries arrive so the rest of the
   dashboard does not jump after paint. The grid wraps for any class count. */
.class-strip:empty:not(.is-ready) { min-height: 136px; }

.class-card {
  display: flex; flex-direction: column; gap: 4px;
  text-align: left; padding: 14px;
  border: 1px solid var(--line); border-radius: var(--grade-radius, 8px);
  background: var(--surface); cursor: pointer;
  font: inherit; color: inherit;
  min-height: 44px;
}
.class-card:hover { border-color: var(--accent); }
.class-card[aria-pressed='true'] {
  border-color: var(--accent); border-width: 2px; padding: 13px;
  background: var(--accent-soft);
}
.class-card:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

.class-card-name { font-weight: 700; font-size: 17px; }
.class-card-meta { font-size: 12px; color: var(--muted); }
.class-card-headline { font-size: 13px; margin-top: 4px; }

/* The word carries the meaning; the colour only reinforces it. */
.class-card-flag {
  align-self: flex-start;
  font-size: 11px; font-weight: 700; letter-spacing: .4px;
  text-transform: uppercase;
  padding: 3px 8px; border-radius: 999px; margin-bottom: 2px;
}
.class-card-flag[data-tone='act-now']   { background: #fdecea; color: #8a1c11; }
.class-card-flag[data-tone='soon']      { background: #fff4e5; color: #7a4a06; }
.class-card-flag[data-tone='good-news'] { background: #e9f6ec; color: #14532d; }

/* ==========================================================================
   WHAT TO SET NEXT — recommendations that show their reasoning
   ========================================================================== */

.rec-card {
  border: 1px solid var(--line); border-left: 4px solid var(--accent);
  border-radius: var(--grade-radius, 8px);
  padding: 16px; background: var(--surface);
  display: flex; flex-direction: column; gap: 8px;
}
.rec-level {
  align-self: flex-start;
  font-size: 11px; font-weight: 700; letter-spacing: .4px; text-transform: uppercase;
  color: var(--accent); background: var(--accent-soft);
  padding: 3px 8px; border-radius: 999px;
}
.rec-what { margin: 0; font-size: 17px; }
.rec-who  { margin: 0; font-size: 13px; color: var(--muted); }
/* The evidence. Never optional: a recommendation a teacher cannot check is
   one they work around rather than trust. */
.rec-why  {
  margin: 0; font-size: 14px; line-height: 1.55;
  padding: 10px 12px; background: var(--accent-soft);
  border-radius: 6px;
}
.rec-card .btn { align-self: flex-start; margin-top: 2px; }

@media (max-width: 560px) {
  .class-strip { grid-template-columns: 1fr; }
  .class-strip:empty:not(.is-ready) { min-height: 580px; }
}

/* ==========================================================================
   LEVEL 1 IS A LESSON
   ==========================================================================
   Written for a learner who has not understood this topic yet, may be two
   grades behind in reading, and may have an intellectual disability. So:
   generous line height, short measure, one idea per block, and nothing that
   depends on colour alone.
   ========================================================================== */

.lesson-head { border-left: 5px solid var(--accent); }

.lesson-badge {
  display: inline-block;
  font-size: 11px; font-weight: 700; letter-spacing: .5px; text-transform: uppercase;
  color: var(--accent); background: var(--accent-soft);
  padding: 4px 10px; border-radius: 999px; margin-bottom: 10px;
}
.lesson-title { margin: 0 0 10px; font-size: 26px; }

/* The whole topic in one sentence. Deliberately the largest thing here. */
.lesson-big-idea {
  margin: 0 0 8px; font-size: 20px; line-height: 1.55; font-weight: 600;
  max-width: 34em;
}
.lesson-goal { margin: 0; font-size: 15px; color: var(--muted); }

.lesson-section-title { margin: 0; font-size: 18px; }

.word-list { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 8px 16px; }
.word-list dt { font-weight: 700; color: var(--accent); }
.word-list dd { margin: 0; line-height: 1.6; }

/* ---- the walkthrough ---- */

.walkthrough { margin: 0; padding-left: 0; list-style: none; display: grid; gap: 18px; }

.walk-step {
  padding: 14px 16px;
  border-left: 3px solid var(--accent-soft);
  background: var(--surface);
}
.walk-say  { margin: 0; font-size: 18px; line-height: 1.6; }
.walk-then { margin: 8px 0 0; font-size: 16px; line-height: 1.6; color: var(--muted); }
/* The drawing reclaims the step's own padding, the way the working figure
   reclaims its list indents. A walkthrough step is padded 16px each side, so
   a figure sized to the card was being scaled to 0.95 and taking its labels
   down with it. */
.walk-figure { margin: 12px -16px 4px; }
.walk-figure svg { max-width: 100%; height: auto; }

.walk-ask {
  margin-top: 14px; padding: 14px;
  background: var(--accent-soft); border-radius: var(--grade-radius, 8px);
}
.walk-ask-q { margin: 0 0 10px; font-weight: 700; font-size: 17px; }
.walk-ask-options { display: flex; flex-wrap: wrap; gap: 10px; }
/* Tap targets never below 44px — a child on a shared phone in a noisy room. */
.walk-ask-options .option { min-height: 48px; min-width: 88px; font-size: 17px; }
.walk-ask-verdict { margin: 10px 0 0; font-size: 16px; font-weight: 600; }
.walk-ask-verdict[data-tone='correct'] { color: #14532d; }
.walk-ask-verdict[data-tone='wrong']   { color: #8a1c11; }

/* ---- worked examples ---- */

.worked-example {
  padding: 14px 16px; border: 1px solid var(--line);
  border-radius: var(--grade-radius, 8px); background: var(--surface);
}
.worked-prompt { margin: 0 0 10px; font-weight: 700; font-size: 17px; }
.worked-steps { margin: 0 0 10px; padding-left: 22px; display: grid; gap: 6px; }
.worked-steps li { line-height: 1.6; font-size: 16px; }
.worked-answer {
  margin: 0; font-weight: 700; color: var(--accent);
  padding-top: 8px; border-top: 1px solid var(--line);
}

/* ---- what goes wrong ---- */

.watch-out {
  padding: 14px 16px; border-left: 4px solid var(--gold);
  background: var(--surface); border-radius: 0 var(--grade-radius, 8px) var(--grade-radius, 8px) 0;
}
.watch-mistake { margin: 0 0 6px; font-weight: 700; font-size: 17px; }
.watch-why { margin: 0 0 6px; font-size: 15px; color: var(--muted); line-height: 1.6; }
.watch-fix { margin: 0; font-size: 16px; line-height: 1.6; }
.watch-fix::before { content: 'Do this: '; font-weight: 700; color: var(--accent); }
html[lang='af'] .watch-fix::before { content: 'Doen dit: '; }
html[lang='tn-ZA'] .watch-fix::before { content: 'Dira seno: '; }

/* ---- a figure attached to a question ---- */

.question-figure { margin: 0 0 16px; }
.question-figure svg { max-width: 100%; height: auto; }

@media (max-width: 560px) {
  .lesson-title { font-size: 22px; }
  .lesson-big-idea { font-size: 18px; }
  .word-list { grid-template-columns: 1fr; gap: 2px 0; }
  .word-list dd { margin-bottom: 8px; }
}

/* "Teach me this" sits beside every topic, so the Level 1 lesson is always
   reachable by choice. A learner who knows they are lost should not have to
   fail a set of questions first to be taught. */
/* THE LEVEL LADDER on each topic.
   Quiet on purpose: Stratford has no badges or streaks anywhere, and five
   numbered squares should not be the first thing that looks like a game. A
   verified level wears the same navy the app uses for every other verified
   claim; a locked one is plainly shut and says why on focus or hover. */
.topic-levels {
  display: flex;
  gap: 6px;
  margin: 6px 0 0 0;
  padding: 0 2px;
}

.level-step {
  width: 44px;
  height: 44px;
  flex: 0 0 auto;
  border: 1px solid var(--line, #d8d2c4);
  border-radius: 6px;
  background: #fff;
  color: var(--ink, #172033);
  font: 600 14px/1 Georgia, 'Times New Roman', serif;
  cursor: pointer;
}

.level-step:hover { border-color: var(--navy, #0b2d5c); }
.level-step:focus-visible { outline: 3px solid var(--accent, #c9a646); outline-offset: 2px; }

.level-step--verified {
  background: var(--navy, #0b2d5c);
  border-color: var(--navy, #0b2d5c);
  color: #fff;
}

.level-step--locked {
  background: #f3f1ec;
  border-style: dashed;
  color: #9a958a;
  cursor: not-allowed;
}
.level-step--locked:hover { border-color: var(--line, #d8d2c4); }

/* The lock is drawn rather than written, so it needs no translation. */
.level-step--locked::after {
  content: '\00a0\1F512';
  font-size: 10px;
}

.topic-teach {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 0 16px; margin-top: 6px;
  font: inherit; font-size: 14px; font-weight: 600;
  color: var(--accent); background: var(--accent-soft);
  border: 1px solid transparent; border-radius: var(--grade-radius, 8px);
  cursor: pointer;
}
.topic-teach:hover { border-color: var(--accent); }
.topic-teach:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* After a walkthrough tap, the correct option must be visible — the learner
   needs to SEE which one was right, not only read that it was. Marked with a
   symbol as well as a colour. */
.walk-ask-options .option[data-state='correct'] {
  border-color: #14532d; background: #e9f6ec; color: #14532d;
  font-weight: 700; opacity: 1;
}
.walk-ask-options .option[data-state='correct']::after {
  content: ' ✓'; font-weight: 700;
}
.walk-ask-options .option[data-state='wrong'] {
  border-color: #8a1c11; background: #fdecea; color: #8a1c11; opacity: 1;
}
.walk-ask-options .option[data-state='wrong']::after {
  content: ' ✕'; font-weight: 700;
}
.walk-ask-options .option:disabled { opacity: .55; }
.walk-ask-options .option[data-state]:disabled { opacity: 1; }

/* A flat list of 34 names buries the two group chips a teacher usually wants,
   so the individuals sit under their own heading. */
.audience-divider {
  grid-column: 1 / -1;
  margin: 10px 0 2px; font-size: 12px; font-weight: 700;
  letter-spacing: .5px; text-transform: uppercase; color: var(--muted);
}

/* ---- the term ahead ---- */
.cell-note { font-size: 12px; color: var(--muted); margin-top: 3px; }
/* A topic that cannot honestly support five levels says so where the teacher
   is choosing a level, not in a footnote nobody reads. */
.cell-warn {
  font-size: 12px; color: #7a4a06; background: #fff4e5;
  padding: 4px 8px; border-radius: 4px; margin-top: 5px; display: inline-block;
}
.cell-why { font-size: 13px; max-width: 34ch; line-height: 1.5; }
/* 44px, not 40. "Small" describes the type and the padding, never the tap
   target — the hand using it is the same hand. */
.btn--small { min-height: var(--tap); padding: 0 14px; font-size: 14px; }

/* Whether the learner's work has reached their teacher. Deliberately quiet:
   working offline is the expected case in a South African school, not a
   failure, so it must not look like one. */
.sync-note {
  max-width: var(--measure, 62ch);
  margin: 10px auto 0; padding: 0 20px;
  font-size: 13px; color: var(--muted);
}

/* ==========================================================================
   WHO NEEDS YOU TODAY — escalation cards
   ==========================================================================
   A dashboard can always say a learner is struggling. What a teacher cannot
   get anywhere else is what was already tried and what the mistake actually
   is, so those two lines are given the most weight on the card.
   ========================================================================== */

.esc-card {
  border: 1px solid var(--line); border-left: 5px solid #8a1c11;
  border-radius: var(--grade-radius, 8px);
  padding: 16px; background: var(--surface);
  display: flex; flex-direction: column; gap: 8px;
}
.esc-headline { margin: 0; font-size: 18px; line-height: 1.4; }
.esc-tried { margin: 0; font-size: 13px; color: var(--muted); }
/* The mathematics, named. The reason this feature exists. */
.esc-maths {
  margin: 0; font-size: 15px; font-weight: 600;
  padding: 10px 12px; background: #fdecea; color: #8a1c11; border-radius: 6px;
}
.esc-do { margin: 0; font-size: 16px; line-height: 1.55; font-weight: 600; }
.esc-why { margin: 0; font-size: 13px; color: var(--muted); line-height: 1.55; }
.esc-card .btn-row { margin-top: 2px; }

/* Real classes, as against the demonstration banner. Green rather than gold,
   and it says whose work it is. */
.live-banner {
  background: #14532d; color: #e9f6ec;
  border-top: 1px solid #0d3b1f; border-bottom: 1px solid #0d3b1f;
  /* 20px each side, the SAME gutter as .demo-banner directly above. This was
     `10px 0`, so on a 375px phone the words started hard against the glass and
     the first one was cut by the screen's own curvature. The two banners share
     an inner element and must share the gutter that goes with it. */
  padding: 10px 20px; font-size: 13px;
}
.live-banner strong { color: #b7e4c7; letter-spacing: .5px; }

/* ==========================================================================
   TEACHER SIGN IN
   ========================================================================== */

.password-row { display: flex; gap: 8px; align-items: stretch; }
.password-row .field-input { flex: 1 1 auto; min-width: 0; }
/* The reveal button must match the input's height, or it reads as a separate
   control that happens to sit nearby. */
.password-row .btn { flex: 0 0 auto; align-self: stretch; }

#panel-signin .field-input { font-size: 16px; }   /* 16px stops iOS zooming in */

/* Whose dashboard this is, and the way out. A staffroom computer is the normal
   case in a South African school, so this is always visible rather than tucked
   inside a menu. */
.whoami {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  flex: 1 0 100%; justify-content: flex-end;
  font-size: 13px; color: var(--cream, #f5f0e6);
}
.whoami-name { opacity: .9; }
.whoami-out {
  font: inherit; font-size: 13px; font-weight: 600;
  color: var(--cream, #f5f0e6);
  background: transparent;
  border: 1px solid rgba(245, 240, 230, .45);
  border-radius: 999px;
  min-height: 44px; padding: 0 16px;
  cursor: pointer;
}
.whoami-out:hover { border-color: var(--cream, #f5f0e6); }
.whoami-out:focus-visible { outline: 3px solid var(--gold, #c9a646); outline-offset: 2px; }

@media (max-width: 620px) {
  .whoami { width: 100%; justify-content: space-between; margin-top: 8px; }
}

/* The staff code, shown exactly once. Given room, because a teacher has to
   copy it onto paper before it is gone. */
.staff-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 24px; font-weight: 700; letter-spacing: 2px;
  text-align: center; margin: 4px 0;
  padding: 18px 12px; border-radius: var(--grade-radius, 8px);
  background: var(--accent-soft); color: var(--accent);
  border: 2px dashed var(--accent);
  overflow-wrap: anywhere;
}

.fieldset { border: 0; padding: 0; margin: 0; }
.field-hint { display: block; margin-top: 6px; font-size: 12px; color: var(--muted); }

/* ==========================================================================
   CREATING A CLASS
   ========================================================================== */

.panel-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
}

/* An empty dashboard should say what to do next, not look broken. */
.empty-state {
  text-align: center; padding: 28px 20px;
  border: 2px dashed var(--line); border-radius: var(--grade-radius, 8px);
  background: var(--surface);
}
.empty-state h3 { margin: 0 0 8px; font-size: 20px; }
.empty-state p {
  margin: 0 auto 16px; max-width: 46ch;
  font-size: 15px; line-height: 1.6; color: var(--muted);
}

.new-class {
  padding: 16px; border: 1px solid var(--accent);
  border-radius: var(--grade-radius, 8px); background: var(--accent-soft);
}
.class-created { margin: 4px 0 12px; }

/* A button that reads as a link. Used where an action sits inside a sentence —
   "Forgotten your password? Use a reset code" — where a full button would
   break the line and a real link would need a URL it does not have. */
.linkish {
  font: inherit; color: var(--accent); background: none; border: 0;
  padding: 0; text-decoration: underline; cursor: pointer;
}
.linkish:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* A member of staff who has registered but made no class. Marked, not greyed
   out: they are the row an administrator most needs to look at, so the tint
   draws the eye rather than pushing them into the background. */
table.data tr.row-quiet td { background: var(--cream); }
table.data td .pill { margin-left: 8px; vertical-align: 2px; }

/* Removing somebody ends their access, so the control that does it looks
   nothing like the one beside it. Outlined rather than filled: the button that
   ends an account should not be the loudest thing on a staff list. */
.btn--danger { background: var(--paper); color: var(--wrong); border-color: var(--wrong); }
.btn--danger:hover { background: var(--wrong-bg); border-color: var(--wrong); }
.btn-row .btn--danger { background: var(--wrong); color: #fff; border-color: var(--wrong); }
.btn-row .btn--danger:hover { background: #75181e; border-color: #75181e; }

table.data td.row-actions { white-space: nowrap; }
table.data td.row-actions .btn + .btn { margin-left: 6px; }

/* ------------------------------------------------- getting ready for a paper */

/* One evening's work, or one line of a mark breakdown. Stacked on a phone,
   which is what most of these learners have, and the reason WHY sits under the
   topic rather than beside it — a learner who is told to revise something is
   owed the reason in the same glance. */
.topic-item {
  display: grid; gap: 8px; padding: 12px 14px;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius);
}
.topic-main { display: grid; gap: 3px; }
.topic-title { font-weight: 600; }
.topic-note { font-size: 14px; color: var(--ink-soft); }
@media (min-width: 560px) {
  .topic-item { grid-template-columns: 1fr auto auto; align-items: center; gap: 14px; }
}

/* A choice with a line of explanation under it. */
.choice-sub {
  display: block; width: 100%; margin-top: 2px;
  font-size: 13px; color: var(--ink-soft);
}
.choice { flex-wrap: wrap; }
.choice.is-picked {
  border-color: var(--navy); border-width: 2px; padding: 10px 13px;
  background: #fbfaf7;
}
.choice.is-picked .choice-mark { background: var(--navy); border-color: var(--navy); color: #fff; }
.choice.is-picked .choice-mark::after { content: '\2713'; }

/* The whole plan, one line an evening. */
.plan-days { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.plan-day {
  padding: 9px 12px; border-left: 3px solid var(--line);
  background: var(--cream); border-radius: 0 var(--radius) var(--radius) 0;
}
.plan-day.is-today { border-left-color: var(--gold); background: #fdfaf2; }
.plan-day-head { font-weight: 600; font-size: 14px; }
.plan-day-topics { font-size: 14px; color: var(--ink-soft); }

/* The clock, during a mock. Never red until it matters, and never the loudest
   thing on the screen — a learner watching the clock is not doing the paper. */
.pill--clock { font-variant-numeric: tabular-nums; letter-spacing: 0; }
.pill--clock.is-low { border-color: var(--wrong); color: var(--wrong); background: var(--wrong-bg); }
/* A topic where most of the marks went. Marked, not shouted at — a learner
   reading this has just had a hard result and does not need a wall of red. */
.topic-item.is-weak { border-left: 3px solid var(--wrong); background: var(--wrong-bg); }

/* The teacher's topic picker for a test scope. Two columns where there is
   room, one where there is not — a teacher does this on a phone as often as
   on a laptop. */
.topic-picker { display: grid; gap: 6px; margin-top: 8px; }
@media (min-width: 620px) { .topic-picker { grid-template-columns: 1fr 1fr; } }
.topic-tick {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; min-height: var(--tap);
  border: 1px solid var(--line-soft); border-radius: var(--radius);
  font-size: 15px; cursor: pointer;
}
.topic-tick:hover { border-color: var(--navy); background: var(--cream); }
.topic-tick input { width: 20px; height: 20px; flex: 0 0 auto; }

/* ===================================================== HOW TO TEACH THIS ====
   Read on a phone, held, at arm's length, in front of a class. Every rule here
   exists because of that: the type is large, the step fills the screen, and
   the controls are pinned to the bottom under the thumb. Nothing scrolls that
   does not have to. */

.lesson-body { background: var(--cream); }

.lesson-bar {
  position: sticky; top: 0; z-index: 5;
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px; min-height: 52px;
  background: var(--navy); color: #fff;
}
.lesson-back, .lesson-watch {
  flex: 0 0 auto; min-height: var(--tap); padding: 0 12px;
  background: transparent; color: #fff;
  border: 1px solid rgba(255,255,255,0.35); border-radius: var(--radius);
  font-family: var(--sans); font-size: 15px; font-weight: 600; cursor: pointer;
}
.lesson-back { font-size: 20px; line-height: 1; padding: 0 14px; }
.lesson-watch:hover, .lesson-back:hover { background: rgba(255,255,255,0.12); }
.lesson-bar .language-picker { flex: 0 0 auto; }
.lesson-bar .lang-toggle { min-width: 0; }
.lesson-where { flex: 1 1 auto; min-width: 0; display: grid; }
.lesson-topic {
  font-weight: 600; font-size: 15px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lesson-part { font-size: 12px; opacity: 0.8; text-transform: uppercase; letter-spacing: 1px; }

@media (max-width: 620px) {
  .lesson-bar { gap: 6px; padding-inline: 8px; }
  .lesson-bar .lang-toggle { padding-inline: 7px; letter-spacing: 0; }
  .lesson-bar .language-menu {
    position: fixed;
    top: 60px;
    right: 8px;
  }
  .lesson-watch { padding-inline: 8px; }
}

.lesson-meter { height: 3px; background: var(--line-soft); }
.lesson-meter-fill { height: 100%; background: var(--gold); transition: width 0.2s ease; }

.lesson-main { max-width: 620px; margin: 0 auto; padding: 16px 14px 120px; }

/* ---- the front ---- */
.lesson-eyebrow {
  font-size: 12px; text-transform: uppercase; letter-spacing: 1.2px;
  color: var(--muted); font-weight: 700; margin: 0 0 4px;
}
.lesson-front h1 { font-size: 26px; margin: 0 0 14px; }
.lesson-bigidea {
  font-family: var(--serif); font-size: 21px; line-height: 1.35;
  padding: 14px 16px; margin-bottom: 16px;
  background: var(--paper); border-left: 4px solid var(--gold);
  border-radius: 0 var(--radius) var(--radius) 0;
}
.lesson-lead { color: var(--ink-soft); font-size: 15px; margin: 0 0 16px; }

.lesson-parts { list-style: none; margin: 0 0 18px; padding: 0; display: grid; gap: 6px; }
.lesson-parts li {
  display: flex; align-items: center; gap: 12px;
  padding: 13px 14px; min-height: var(--tap);
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius); cursor: pointer;
}
.lesson-parts li:hover { border-color: var(--navy); }
.lesson-part-name { flex: 1 1 auto; font-weight: 600; }
.lesson-part-meta { flex: 0 0 auto; font-size: 13px; color: var(--muted); }

/* ---- one step ---- */
.lesson-card {
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 18px 16px;
}
.lesson-kind {
  font-size: 12px; text-transform: uppercase; letter-spacing: 1.2px;
  font-weight: 700; color: var(--muted); margin: 0 0 10px;
}

/* THE WORDS THEMSELVES, as large as they will go. A teacher glancing down
   mid-sentence must not have to hunt for them. */
.lesson-text { font-size: 22px; line-height: 1.4; }

/* Board steps look like a board, because "copy this exactly" and "say this in
   your own words" are different instructions and must not look the same. */
.lesson-card[data-kind='board'] { background: var(--navy); border-color: var(--navy); }
.lesson-card[data-kind='board'] .lesson-kind { color: var(--gold); }
.lesson-card[data-kind='board'] .lesson-text {
  color: #fff; font-family: var(--serif); font-size: 25px;
}
.lesson-card[data-kind='board'] .lesson-note { color: rgba(255,255,255,0.75); }

.lesson-card[data-kind='say'] .lesson-text { font-family: var(--serif); }
.lesson-card[data-kind='ask'] { border-color: var(--gold-line); background: #fdfaf2; }
.lesson-card[data-kind='do'] .lesson-text { font-size: 19px; }

.lesson-note {
  margin: 14px 0 0; font-size: 14px; color: var(--ink-soft); line-height: 1.45;
}

.lesson-figure { margin-top: 16px; text-align: center; }
.lesson-figure svg { max-width: 100%; height: auto; }

.lesson-options { list-style: none; margin: 14px 0; padding: 0; display: grid; gap: 6px; }
.lesson-options li {
  padding: 10px 12px; background: var(--paper);
  border: 1px solid var(--line); border-radius: var(--radius); font-size: 17px;
}
.lesson-answer {
  margin: 12px 0 0; padding: 12px 14px; font-size: 19px; font-weight: 600;
  color: var(--correct); background: var(--correct-bg);
  border: 1px solid var(--correct); border-radius: var(--radius);
}

/* ---- what they get wrong ---- */
.lesson-mistake {
  padding: 14px 16px; background: var(--paper);
  border-left: 4px solid var(--wrong); border-radius: 0 var(--radius) var(--radius) 0;
}
.lesson-mistake-what { margin: 0 0 6px; font-size: 18px; font-weight: 600; color: var(--wrong); }
.lesson-mistake-why { margin: 0 0 6px; font-size: 15px; color: var(--ink-soft); }
.lesson-mistake-fix { margin: 0; font-size: 16px; font-weight: 600; }

/* ---- under the thumb ---- */
.lesson-nav {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 6;
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
  background: var(--paper); border-top: 1px solid var(--line);
}
.lesson-prev, .lesson-next {
  flex: 1 1 0; min-height: 54px; padding: 0 18px;
  font-family: var(--sans); font-size: 17px; font-weight: 600;
  border-radius: var(--radius); cursor: pointer;
}
.lesson-prev { flex: 0 0 32%; background: var(--paper); color: var(--navy); border: 1px solid var(--line); }
.lesson-next { background: var(--navy); color: #fff; border: 1px solid var(--navy); }
.lesson-prev[disabled] { opacity: 0.4; cursor: not-allowed; }
.lesson-count {
  flex: 0 0 auto; font-size: 13px; color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* The step sits in the middle of the screen, not stranded at the top. A
   teacher glancing down finds the words where their eye already is. */
#lp-step.lesson-step {
  min-height: calc(100vh - 190px);
  display: flex; flex-direction: column; justify-content: center;
}
#lp-step .lesson-lead { flex: 0 0 auto; }

/* ======================================================= THE IMPACT REPORT ==
   A document, not a dashboard. It is opened when a report is due, read once,
   and saved as a PDF that goes into a verification file. So it reads top to
   bottom in one column, and the print rules are part of the design. */

.report {
  max-width: 780px; margin: 0 auto 40px;
  padding: 34px 34px 26px;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius);
}

.report-head { border-bottom: 2px solid var(--navy); padding-bottom: 18px; margin-bottom: 22px; }
.report-issuer { display: flex; align-items: center; gap: 14px; margin-bottom: 20px; }
.report-crest { flex: 0 0 auto; width: 56px; height: auto; }
.report-org { margin: 0; font-family: var(--serif); font-size: 20px; color: var(--navy); }
.report-org-meta { margin: 3px 0 0; font-size: 12.5px; color: var(--ink-soft); }

.report-title { font-size: 26px; margin: 0 0 6px; }
.report-sub { margin: 0; font-size: 14px; color: var(--ink-soft); }

/* The one sentence the document exists for. */
.report-headline {
  margin: 0 0 26px; padding: 18px 20px;
  background: var(--cream); border-left: 4px solid var(--gold);
  border-radius: 0 var(--radius) var(--radius) 0;
}
.report-headline-text {
  margin: 0 0 8px; font-family: var(--serif); font-size: 21px; line-height: 1.35;
  color: var(--navy);
}
.report-headline-note { margin: 0; font-size: 14.5px; color: var(--ink-soft); line-height: 1.5; }

.report-h2 {
  font-size: 17px; margin: 30px 0 10px;
  padding-bottom: 5px; border-bottom: 1px solid var(--line-soft);
}
.report-note { font-size: 14px; color: var(--ink-soft); line-height: 1.5; margin: 10px 0 0; }
.report-method {
  font-size: 13.5px; color: var(--ink-soft); line-height: 1.6; margin: 8px 0 0;
  padding: 12px 14px; background: var(--cream); border-radius: var(--radius);
}

.tally-note { font-size: 11.5px; color: var(--muted); margin-top: 2px; }
table.data td[data-tone='good'] { color: var(--correct); font-weight: 600; }

.report-foot {
  margin-top: 34px; padding-top: 16px; border-top: 1px solid var(--line-soft);
  font-size: 12.5px; color: var(--muted);
}
.report-foot p { margin: 0 0 3px; }
.report-foot-missing { color: var(--wrong); font-weight: 600; margin-top: 8px; }

/* ---- printed ---- */
@media print {
  .no-print, .masthead { display: none !important; }
  body { background: #fff; }
  .report {
    max-width: none; margin: 0; padding: 0;
    border: 0; border-radius: 0;
  }
  /* A table split across a page break is unreadable in a filed document. */
  table.data, .report-headline, .tally { break-inside: avoid; }
  .report-h2 { break-after: avoid; }
  /* On paper nothing scrolls, so the shadows would be printed smudges. */
  .table-wrap { overflow: visible; background-image: none; }
  a[href]::after { content: ''; }
}

/* --------------------------------------------- a learner's own papers ----- */

/* The run of marks, oldest first, so it reads like a story rather than a
   league table. Bars are relative to the learner's own best, not to 100 — a
   child at 38% comparing themselves with a full mark learns nothing. */
.paper-runs { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.paper-run {
  display: grid; grid-template-columns: 54px 1fr 46px 56px;
  align-items: center; gap: 10px;
  padding: 7px 10px; background: var(--paper);
  border: 1px solid var(--line-soft); border-radius: var(--radius);
  font-size: 14px;
}
.paper-when { color: var(--muted); font-size: 13px; }
.paper-bar { height: 10px; background: var(--line-soft); border-radius: 999px; overflow: hidden; }
.paper-bar-fill { display: block; height: 100%; background: var(--navy); border-radius: 999px; }
.paper-bar-fill.is-best { background: var(--gold); }
.paper-mark { font-weight: 700; font-variant-numeric: tabular-nums; text-align: right; }
.paper-outof { color: var(--muted); font-size: 13px; font-variant-numeric: tabular-nums; }

/* How this paper went against the one before it. */
.result-against {
  margin: 10px 0 0; padding: 11px 14px; font-size: 15px; line-height: 1.45;
  background: var(--cream); border-radius: var(--radius);
}
.result-against[data-tone='good'] {
  background: var(--correct-bg); color: var(--correct); font-weight: 600;
}
.result-against[data-tone='error'] { background: var(--wrong-bg); color: var(--wrong); }

/* ----------------------------------------------------------- certificates -- */

/* Built as a document, because a learner will photograph it and show it to
   somebody. The small print is not decoration: a claim with its evidence
   attached is a different object from a claim without one, and it is the only
   reason this is worth issuing at all. */
.certificate {
  position: relative;
  margin: 0 0 14px; padding: 26px 24px 20px;
  background: var(--paper);
  border: 2px solid var(--navy); border-radius: var(--radius);
  text-align: center;
}
.certificate::after {
  content: ''; position: absolute; inset: 6px;
  border: 1px solid var(--gold-line); border-radius: 4px; pointer-events: none;
}
.certificate.is-new { box-shadow: 0 0 0 4px #fdfaf2; border-color: var(--gold); }

.certificate-crest { width: 56px; height: auto; margin: 0 auto 10px; display: block; }
.certificate-eyebrow {
  margin: 0 0 14px; font-size: 11.5px; font-weight: 700;
  letter-spacing: 1.4px; text-transform: uppercase; color: var(--muted);
}
.certificate-name {
  margin: 0 0 8px; font-family: var(--serif); font-size: 26px; color: var(--navy);
}
.certificate-statement {
  margin: 0 auto 16px; max-width: 34em; font-size: 16.5px; line-height: 1.5;
}
.certificate-basis {
  margin: 0 auto 14px; max-width: 40em; padding-top: 12px;
  border-top: 1px solid var(--line-soft);
  font-size: 12px; line-height: 1.55; color: var(--ink-soft); text-align: left;
}
.certificate-foot {
  margin: 0; font-size: 11px; color: var(--muted); line-height: 1.5;
}

@media print {
  .certificate { break-inside: avoid; page-break-after: always; }
}

/* --------------------------------------------------------- show the working */

/* A result is not an explanation. This is the column method written out, step
   by step, in the words a teacher uses — folded away so a learner who did not
   need it is not made to scroll past it, and complete for the one who did. */
.working { margin: 8px 0 4px; }
.working-open {
  display: inline-block; cursor: pointer;
  padding: 7px 12px; min-height: 36px;
  background: var(--paper); color: var(--navy);
  border: 1px solid var(--navy); border-radius: var(--radius);
  font-size: 14px; font-weight: 600;
}
.working-open:hover { background: var(--cream); }
.working[open] .working-open { margin-bottom: 10px; }

/* Where the digits go is half of what is being taught. */
.working-board {
  display: inline-block; margin: 0 0 12px;
  padding: 12px 16px; background: var(--navy); color: #fff;
  border-radius: var(--radius);
  font-family: ui-monospace, 'Cascadia Mono', 'Courier New', monospace;
  font-size: 19px; line-height: 1.35; white-space: pre;
  font-variant-numeric: tabular-nums;
}

/* The list of solution steps after a wrong answer. A bare <ol> takes the
   browser's 40px indent, and the working panel nests another list inside it —
   between them the drawing was squeezed to 258px in a 318px card. */
.solution-steps { margin: 0; padding-left: 22px; display: grid; gap: 10px; }

.working-steps { margin: 0; padding-left: 20px; display: grid; gap: 10px; }
.working-steps li { line-height: 1.5; }
.working-heading { display: block; font-weight: 600; font-size: 15px; }
.working-detail { display: block; font-size: 14.5px; color: var(--ink-soft); margin-top: 2px; }

/* The sum as it stands at this step, drawn. The words explain it; this is the
   thing a child copies onto paper. */
/* THE DRAWING TAKES THE WHOLE CARD.
 *
 * It is the thing being taught; the list indentation is decoration. Pulling
 * back out of both indents — the solution list and the working panel's own —
 * gives the figure the card's full content width instead of 258px of it, and
 * a figure that has to be scaled down takes its labels below the readable
 * floor with it.
 *
 * The negative margin matches the two paddings above. They are constants in
 * this stylesheet, so the two cannot drift apart without someone editing both
 * within a few lines of each other. */
.working-figure { margin: 8px 0 2px; margin-left: -42px; }

/* Inside the VERDICT box after a wrong answer — which is where a learner
   meets the working most often — the drawing is behind the box's own 16px of
   padding, the solution list at 20px and the working panel's list at 20px.
   That left a 300px figure with 242px. */
.verdict .working-figure { margin-left: -56px; margin-right: -16px; }

/* Inside a lesson's WORKED EXAMPLE the drawing is three levels deep: the
   example's own 16px of padding, the example's step list at 22px, and the
   working panel's own list at 20px. That is 58px in from the left and 16px
   from the right, and it left a 300px figure with 242px — scaled to 0.88,
   labels and all.
   Measured in the browser rather than added up from the stylesheet, because
   the stylesheet is where the mistake would be. */
.worked-example .working-figure { margin-left: -58px; margin-right: -16px; }

.working-figure svg { max-width: 100%; height: auto; }

/* ===========================================================================
 * THE FINISH
 * ===========================================================================
 *
 * Everything above builds the interface. This last section is what makes it
 * feel made rather than assembled: depth, a little grain, and the response a
 * finger expects. It is deliberately last so that any rule here can be read as
 * presentation only — delete the whole block and the product still works,
 * still passes contrast, still fits a 375px screen.
 *
 * Written against tokens, so a grade page in Grade 4 red and a teacher screen
 * in navy both get it without a second copy.
 */

:root {
  /* ELEVATION. Two shadows per level, always: a tight one that reads as the
     edge of the paper, and a wide soft one that reads as the distance from
     the page. One shadow alone looks like a drop-shadow filter and not like
     a thing sitting on a surface. */
  --lift-1: 0 1px 2px rgba(6, 26, 58, 0.06), 0 1px 3px rgba(6, 26, 58, 0.05);
  --lift-2: 0 2px 4px rgba(6, 26, 58, 0.06), 0 6px 16px rgba(6, 26, 58, 0.08);
  --lift-3: 0 4px 8px rgba(6, 26, 58, 0.07), 0 14px 32px rgba(6, 26, 58, 0.11);

  /* The gold hairline used as a finishing edge on dark surfaces. */
  --gold-hair: linear-gradient(90deg,
    rgba(201, 166, 70, 0) 0%,
    rgba(201, 166, 70, 0.55) 18%,
    rgba(201, 166, 70, 0.9) 50%,
    rgba(201, 166, 70, 0.55) 82%,
    rgba(201, 166, 70, 0) 100%);

  --ease: cubic-bezier(0.2, 0, 0.2, 1);
}

/* ---------------------------------------------------------------- surfaces */

/* Paper, not a rectangle. A barely-there vertical gradient stops a large white
   card reading as a hole in the page. */
.card {
  background-image: linear-gradient(180deg, #ffffff 0%, #fdfcfa 100%);
  box-shadow: var(--lift-1);
}

.card--accent { box-shadow: var(--lift-2); }

/* The page itself gets a fine grain, so the cream reads as paper stock rather
   than as a flat fill. Two hairline gradients at 90 degrees — invisible as a
   pattern, visible as texture. */
body {
  background-image:
    linear-gradient(rgba(6, 26, 58, 0.014) 1px, transparent 1px),
    linear-gradient(90deg, rgba(6, 26, 58, 0.014) 1px, transparent 1px);
  background-size: 3px 3px, 3px 3px;
}

/* -------------------------------------------------------------- masthead - */

.masthead {
  box-shadow: 0 1px 0 rgba(201, 166, 70, 0.30), var(--lift-2);
  background-image: linear-gradient(180deg, #08203f 0%, var(--navy) 62%, #04142e 100%);
}

/* ------------------------------------------------------------------ hero - */

/* A light source behind the crest. The hero was a flat navy block; this gives
   it a centre, and makes the gold read as metal rather than as yellow text. */
.hero {
  position: relative;
  background-image:
    radial-gradient(120% 80% at 50% 0%, rgba(201, 166, 70, 0.16) 0%, rgba(201, 166, 70, 0) 62%),
    linear-gradient(180deg, #0a2549 0%, var(--navy) 55%, #04142e 100%);
}

/* The gold hairline that closes the hero off from the page. */
.hero::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: var(--gold-hair);
}

.hero-crest img,
.hero-crest svg { filter: drop-shadow(0 6px 18px rgba(0, 0, 0, 0.38)); }

.hero-fact strong { text-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); }

/* ------------------------------------------------------------ grade cards */

.grade-card {
  box-shadow: var(--lift-1);
  transition: box-shadow 0.18s var(--ease), transform 0.18s var(--ease),
              border-color 0.18s var(--ease);
}

/* The lift is the affordance: it says this is the thing you press, which on a
   page of six near-identical cards is worth more than any label. */
.grade-card:hover {
  box-shadow: var(--lift-3);
  transform: translateY(-2px);
}

.grade-card:active { transform: translateY(0); box-shadow: var(--lift-1); }

/* The arrow leans towards where it is taking you. */
.grade-card .go { transition: transform 0.18s var(--ease); display: inline-block; }
.grade-card:hover .go { transform: translateX(3px); }

/* The number badge gets a light from above and a hairline, so it reads as an
   object on the card rather than a coloured square behind the text. */
.grade-numeral {
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.28),
    inset 0 -1px 0 rgba(0, 0, 0, 0.16),
    0 2px 6px rgba(6, 26, 58, 0.22);
}

/* --------------------------------------------------------------- buttons - */

.btn {
  background-image: linear-gradient(180deg,
    rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0) 52%,
    rgba(0, 0, 0, 0.07) 100%);
  box-shadow: var(--lift-1);
  transition: box-shadow 0.15s var(--ease), transform 0.08s var(--ease),
              filter 0.15s var(--ease);
}

.btn:hover { box-shadow: var(--lift-2); filter: brightness(1.06); }

/* PRESSED MEANS PRESSED. One pixel down and the shadow gone: on a cheap phone
   with a slow network this is the only confirmation the tap registered, and
   without it the child taps a second time and sends the answer twice. */
.btn:active { transform: translateY(1px); box-shadow: none; filter: brightness(0.97); }

.btn[disabled], .btn:disabled {
  box-shadow: none; background-image: none; filter: none; transform: none;
}

/* ---------------------------------------------------------------- inputs - */

.field-input, .typed-input, .code-input {
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}

/* A ring rather than a colour change alone, so the focused field is obvious at
   arm's length and to anyone who does not see the border hue shift. */
.field-input:focus, .typed-input:focus, .code-input:focus {
  box-shadow: 0 0 0 3px rgba(11, 45, 92, 0.13);
}

/* -------------------------------------------- profiles on a shared device -
   A class tablet goes round the room all day. These are the children who have
   signed in on THIS device: tap a name instead of spelling it. Sized as a real
   tap target, because the hands using them are small and often in a hurry. */

.profile-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 6px 0 14px;
}

.profile-chip {
  min-height: var(--tap);
  padding: 10px 16px;
  border: 1.5px solid var(--line);
  border-radius: 999px;
  background: var(--paper);
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  box-shadow: var(--lift-1);
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease),
              transform 0.08s var(--ease);
}

.profile-chip:hover { border-color: var(--accent); box-shadow: var(--lift-2); }
.profile-chip:active { transform: translateY(1px); }

/* CHOSEN IS NOT SHOWN BY COLOUR ALONE. The tick is the signal; the fill is the
   emphasis. A child who cannot tell the navy chip from the white one can still
   see which name has the mark against it. */
.profile-chip.is-picked {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  box-shadow: var(--lift-2);
}

.profile-chip.is-picked::before {
  content: "\2713  ";
  font-weight: 700;
}

/* ---------------------------------------------------------- section heads */

/* A short gold rule under a section title. Cheap, and it does the work three
   extra pixels of margin cannot: it says a new part of the page has started. */
.section-head h2 { position: relative; }

.section-head h2::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -7px;
  width: 34px;
  height: 2px;
  border-radius: 2px;
  background: var(--gold-line);
}

/* ------------------------------------------------------------ the tables - */

/* The scroll shadows already tell a reader there is more sideways. A hairline
   under the header row tells them where the data starts. */
thead th { box-shadow: inset 0 -1px 0 var(--line); }

/* -------------------------------------------------------------- verdicts - */

.verdict { box-shadow: var(--lift-1); }

/* ------------------------------------------------------- reduced motion -- *
 * Everything above degrades to its resting state. Nothing here carries
 * meaning that is lost when the movement is: the lift is an affordance, the
 * press is a confirmation, and both have a static fallback in the shadow and
 * the colour that remain. */
@media (prefers-reduced-motion: reduce) {
  .grade-card,
  .grade-card .go,
  .btn,
  .profile-chip,
  .field-input, .typed-input, .code-input {
    transition: none;
  }
  .grade-card:hover,
  .grade-card:active,
  .btn:active,
  .profile-chip:active,
  .grade-card:hover .go {
    transform: none;
  }
}

/* EVERY figure scales, wherever it is placed.
   The width and height attributes on the SVG carry its intrinsic ratio (they
   must be lengths, not "auto" — see core/figures.js). These two lines are what
   turn that fixed size into a responsive one, and they are global because a
   figure that lands in a context nobody anticipated must still fit the phone
   rather than push the page sideways. */
.figure { max-width: 100%; height: auto; }

/* A drawing inside a previewed or printed paper. Constrained so a figure built
   for a phone does not span a laptop screen, and centred because that is how it
   sits on the page a teacher prints. */
.preview-figure {
  margin: 10px 0 4px;
  max-width: 320px;
}
.preview-figure svg { max-width: 100%; height: auto; }

/* The tap options under a previewed question, lettered the way they print.
   On the memorandum the correct one is ticked and tinted; colour never
   carries the meaning alone — the ✓ travels in the text. */
.preview-options {
  list-style: none;
  margin: 8px 0 2px;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.preview-option {
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-size: 15px;
  color: var(--ink);
  background: var(--paper);
}
.preview-option.is-answer {
  border-color: var(--correct);
  background: var(--correct-bg);
  font-weight: 600;
}
.preview-written {
  margin: 8px 0 2px;
  font-size: 15px;
  font-style: italic;
  color: var(--ink-soft);
}

/* Fast access on the teacher dashboard: real buttons for the jobs that live
   on other pages, quiet jump chips for the sections further down this one. */
.quick-nav { display: grid; gap: 10px; }
.quick-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.quick-jumps { display: flex; flex-wrap: wrap; gap: 6px; }
.quick-jump {
  /* 44px, like every other control — measured at 33 on a phone, which is a
     miss for a teacher moving between lessons. The row still reads as chips;
     there is simply enough of each one to hit. */
  display: inline-flex;
  align-items: center;
  padding: 0 14px;
  min-height: 44px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 14px;
  color: var(--ink-soft);
  text-decoration: none;
  background: var(--paper);
}
.quick-jump:hover, .quick-jump:focus-visible {
  color: var(--ink);
  border-color: var(--ink-soft);
}
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

/* The print header exists only on paper. */
.print-head { display: none; }

/* The moderation pack exists only on paper too. */
.moderation-pack { display: none; }

@media print {
  body.print-pack .masthead,
  body.print-pack .demo-banner,
  body.print-pack .live-banner,
  body.print-pack main > section { display: none !important; }
  body.print-pack .moderation-pack { display: block; }
  .moderation-pack h1 { font-size: 22px; margin: 0 0 4px; }
  .moderation-pack h2 { font-size: 16px; margin: 18px 0 6px; }
  .moderation-pack p { margin: 2px 0; font-size: 13px; }
  .moderation-pack table { width: 100%; border-collapse: collapse; font-size: 12px; }
  .moderation-pack th, .moderation-pack td {
    border: 1px solid #999; padding: 4px 6px; text-align: left;
  }
  .moderation-pack .pack-section { break-inside: avoid; }
}

@media print {
  /* PRINT THE PAPER, NOTHING ELSE. When the teacher presses Print on the
     builder, the seven steps, the chrome and the buttons all disappear and
     what comes out of the printer is a test a learner can write on. */
  body.print-paper .masthead,
  body.print-paper .demo-banner,
  body.print-paper .live-banner,
  body.print-paper .step:not(.step--print),
  body.print-paper .blueprint { display: none !important; }
  body.print-paper .step--print { border: none; box-shadow: none; padding: 0; }
  body.print-paper .step--print .step-head,
  body.print-paper .step--print .btn-row { display: none !important; }
  body.print-paper .proposal { border: none; padding: 0; }
  body.print-paper .print-head { display: block; margin-bottom: 14px; }
  body.print-paper .print-head h1 { font-size: 20px; margin: 0 0 4px; }
  body.print-paper .print-head p { margin: 2px 0; font-size: 13px; }
  body.print-paper .print-lines { margin-top: 10px; font-size: 14px; }
  body.print-paper .preview-list > li { break-inside: avoid; }

  /* Never let a drawing be split across two pages: the half on page two is
     not a diagram, it is a mistake a teacher has to explain to thirty
     children mid-test. */
  .preview-list li { break-inside: avoid; page-break-inside: avoid; }
}

/* ---------------------------------------------- adding learners by hand --
   A teacher entering the register. The PIN column is deliberately large and
   spaced: it gets read off a screen and copied onto thirty slips of paper by
   somebody in a hurry, and 0 against O or 1 against 7 at 13px is a child who
   cannot sign in tomorrow. */
.add-learners { margin-top: var(--gap); }
.add-learners form { margin-top: 12px; }
.add-learners textarea.field-input {
  min-height: 160px;
  font-family: var(--sans);
  line-height: 1.6;
  resize: vertical;
}
.pin-cell {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 0.12em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

@media print {
  /* Printing the PIN list is the point of the Print button: everything that
     is not the list is noise on a page that goes onto a desk. */
  body > *:not(main) { display: none; }
  main > *:not(:has(#al-result)) { display: none; }
  #al-result .btn-row { display: none; }
}

/* ------------------------------------------- not in a class yet ----------
   A learner who registered without a school code. Deliberately quiet: they
   are not doing anything wrong and their work is not at risk — it is waiting.
   An alarm here would tell a child at a kitchen table that they have made a
   mistake by starting. */
.join-later {
  margin: 0 0 var(--gap);
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--gold-line);
  border-radius: var(--radius);
  background: linear-gradient(180deg, #ffffff 0%, #fdfcfa 100%);
  box-shadow: var(--lift-1);
}
.join-later .code-row { margin-top: 10px; }

/* Who is in a group, on the chip that offers it.
   Quieter than the note above it — this is a list to scan for one name, not
   prose to read — and it wraps rather than truncating, because the name a
   teacher is looking for is exactly the one an ellipsis would hide. */
.chip-roll {
  display: block;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--line-soft);
  font-size: 13px;
  line-height: 1.6;
  color: var(--muted);
  white-space: normal;
}

/* ------------------------------------------- editing who receives it -----
   Every learner in the class, in or out. Tap to change. */
#members:empty { display: none; }
#members { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line-soft); }

.members-head {
  display: flex; flex-wrap: wrap; gap: 8px 16px;
  align-items: baseline; justify-content: space-between;
}
.members-title {
  font-size: 12px; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--ink-soft);
}

.member-grid {
  display: grid; gap: 8px; margin-top: 12px;
  grid-template-columns: 1fr;
}
@media (min-width: 620px) { .member-grid { grid-template-columns: 1fr 1fr; } }

.member {
  display: flex; align-items: center; gap: 10px;
  min-height: var(--tap); padding: 8px 12px;
  border: 1.5px solid var(--line); border-radius: var(--radius);
  background: var(--paper); color: var(--muted);
  font: inherit; text-align: left; cursor: pointer;
  transition: border-color 0.15s var(--ease), background 0.15s var(--ease),
              transform 0.08s var(--ease);
}
.member:active { transform: translateY(1px); }

/* IN is carried by the mark and the weight of the name, not by colour alone —
   this control means one thing and it has to survive a cheap screen in a
   bright classroom. */
.member.is-in {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--ink);
}
.member-mark {
  flex: 0 0 auto; width: 22px; height: 22px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1.5px solid var(--line);
  font-size: 13px; font-weight: 700; line-height: 1;
}
.member.is-in .member-mark {
  background: var(--accent); border-color: var(--accent); color: #fff;
}
.member-name { flex: 1 1 auto; min-width: 0; font-size: 15px; }
.member.is-in .member-name { font-weight: 600; }
.member-stat {
  flex: 0 0 auto; font-size: 13px;
  font-variant-numeric: tabular-nums; color: var(--muted);
}

@media (prefers-reduced-motion: reduce) {
  .member { transition: none; }
  .member:active { transform: none; }
}

/* ---------------------------------------------- work the teacher set -----
   Homework on the learner's own screen. LATE is a state, not an alarm: the
   red is a line down the edge, and the word does the talking. A child opening
   the app to fix being late should not be shouted at by it. */
.set-work-item { border-left: 3px solid var(--accent); }
.set-work-item.is-overdue { border-left-color: var(--wrong); }
.set-work-item.is-overdue .card-label { color: var(--wrong); }


/* ------------------------------------------------- taal / language -------
   A compact menu button keeps the masthead calm while its menu is generated
   from the language registry. Future languages do not widen every page. */
.language-picker {
  position: relative;
  flex: 0 0 auto;
  min-width: 0;
}
.lang-toggle {
  min-height: var(--tap);
  padding: 0 12px;
  border: 1px solid rgba(201, 166, 70, 0.55);
  border-radius: var(--radius);
  background: transparent;
  color: var(--gold-line);
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
  cursor: pointer;
}
.lang-toggle:hover,
.lang-toggle[aria-expanded='true'] { background: rgba(201, 166, 70, 0.12); }
.lang-toggle[aria-busy='true'] { cursor: wait; opacity: 0.78; }
.lang-toggle::after { content: ' ▾'; }
.language-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 1000;
  display: grid;
  width: max-content;
  min-width: min(18rem, calc(100vw - 24px));
  max-width: min(22rem, calc(100vw - 24px));
  max-height: min(70vh, 28rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 6px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #fff;
  color: var(--ink);
  box-shadow: 0 12px 30px rgba(6, 26, 58, 0.24);
}
.language-menu[hidden] { display: none; }
.language-option {
  position: relative;
  width: 100%;
  min-height: var(--tap);
  padding: 10px 12px 10px 38px;
  border: 0;
  border-radius: 6px;
  background: #fff;
  color: var(--ink);
  font: inherit;
  font-weight: 700;
  line-height: 1.3;
  text-align: left;
  white-space: normal;
  cursor: pointer;
}
.language-option:hover { background: var(--cream); }
.language-option:focus-visible {
  outline: 3px solid var(--gold);
  outline-offset: -3px;
}
.language-option[aria-checked='true'] { background: var(--gold-soft); }
.language-option[aria-checked='true']::before {
  content: '✓';
  position: absolute;
  left: 13px;
  top: 50%;
  transform: translateY(-50%);
}
@media (max-width: 560px) {
  .language-menu {
    width: max-content;
    max-width: calc(100vw - 24px);
  }
}
/* The space key: a glyph nobody can read is a key nobody presses, and the
   open box is drawn small at the quiet keys' size. */
.padkey--space { font-size: 24px; line-height: 1; }

/* ---------------------------------------------------------------- steps -- */
/* A learner working a question out a line at a time. The lines already
   written stack above the box they are typing in, so the whole calculation
   reads top to bottom the way it would on paper. */
.steps { margin-top: 4px; }
.steps-list {
  list-style: none; margin: 0 0 10px; padding: 0;
  display: flex; flex-direction: column; gap: 6px;
}
.steps-line {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 10px;
  background: var(--cream); border: 1px solid var(--line-soft);
  border-radius: var(--radius);
}
.steps-text {
  flex: 1 1 auto; min-width: 0;
  font-family: var(--sans); font-size: 17px; color: var(--ink);
  /* A long line of working wraps rather than taking the page sideways. */
  overflow-wrap: anywhere;
}
/* Removing a line is a real button at the tap floor, not a small cross. */
.steps-drop {
  flex: 0 0 auto;
  min-width: var(--tap); min-height: var(--tap);
  font-size: 20px; line-height: 1; color: var(--muted);
  background: transparent; border: 0; border-radius: var(--radius);
  cursor: pointer;
}
.steps-drop:hover { background: var(--line-soft); color: var(--ink); }
.steps-drop:disabled { opacity: 0.4; cursor: default; }
.steps-drop:focus-visible { outline: 2px solid var(--navy); outline-offset: 1px; }
.steps-next { margin-top: 10px; width: 100%; }
.steps-note { margin-top: 10px; }

/* --------------------------------------------------- the parts of a solution -- */
/* A worked solution is not a list of equal things. The rule it rests on, the
   arithmetic, the check and the warning are four different kinds of sentence,
   and a learner reading alone has to be able to tell them apart at a glance. */
.sol-step--rule {
  font-weight: 600;
}
/* The check earns its own mark, so it is set apart rather than numbered as
   though it were one more calculation. */
.sol-step--check {
  list-style: none;
  margin-left: -1.1em;
  padding: 6px 10px 6px 30px;
  position: relative;
  background: var(--cream);
  border-radius: var(--radius);
  color: var(--ink);
}
.sol-step--check::before {
  content: '\2713';
  position: absolute;
  left: 10px;
  font-weight: 700;
  color: var(--accent, #0b2d5c);
}
/* The named mistake is a warning, not a step of the method. */
.sol-step--watch {
  list-style: none;
  margin-left: -1.1em;
  padding: 6px 10px 6px 30px;
  position: relative;
  border-left: 3px solid var(--gold, #8a6d1f);
  background: var(--paper);
  color: var(--muted);
}
.sol-step--watch::before {
  content: '\0021';
  position: absolute;
  left: 12px;
  font-weight: 700;
  color: var(--gold, #8a6d1f);
}

/* --------------------------------------------------- the learner's own switch -- */
/* "I worked it out on paper" sits beside Check answer without competing with
   it. Quiet on purpose: it is a choice the learner is entitled to make, not a
   feature being sold to them, and this is not a game. */
.btn--quiet {
  background: transparent;
  color: var(--ink-soft);
  border: 1px solid var(--line);
  font-weight: 600;
}
/* teacher.js is deferred behind the core/teacher bundles. Reserve the signed-in
   identity row before those bundles finish, then release it at init. */
body:not(.teacher-ready) #whoami.hidden {
  display: flex !important;
  visibility: hidden;
}
.btn--quiet:hover, .btn--quiet:focus-visible { color: var(--ink); border-color: var(--ink-soft); }

/* Grade themes colour every generic .btn. Preserve the intentionally quiet
   treatment with enough specificity to prevent dark text on a dark grade
   background. This applies to answer-mode and other secondary learner actions. */
[data-grade] .btn.btn--quiet {
  background: var(--paper);
  color: var(--navy);
  border-color: var(--accent-line);
}
[data-grade] .btn.btn--quiet:hover,
[data-grade] .btn.btn--quiet:focus-visible {
  background: var(--accent-soft);
  color: var(--navy);
  border-color: var(--accent);
}

/* ------------------------------------------------ question fault report -- */

.question-report-control {
  display: flex;
  align-items: center;
  gap: 10px 14px;
  flex-wrap: wrap;
  border-top: 1px solid var(--line-soft);
  padding-top: 12px;
}
.question-report-control .status-line { margin: 0; flex: 1 1 220px; }

/* This action sits inside a grade-themed page. The theme's `[data-grade] .btn`
   selector is intentionally stronger than the generic quiet-button rule, so
   without this component rule it combines a dark grade background with dark
   quiet-button text. Keep the label unambiguously readable in every state. */
#report-question {
  background: var(--navy);
  color: #fff;
  border: 2px solid var(--navy);
  font-weight: 700;
}
#report-question:hover,
#report-question:focus-visible {
  background: var(--navy-mid);
  color: #fff;
  border-color: var(--navy-mid);
}
#report-question:focus-visible {
  outline: 3px solid var(--gold-line);
  outline-offset: 3px;
}
#report-question:active {
  background: var(--navy);
  color: #fff;
  border-color: var(--navy);
}
#report-question:disabled {
  background: #4a5468;
  color: #fff;
  border-color: #4a5468;
  opacity: 1;
}

.dialog-backdrop {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: grid;
  place-items: center;
  padding: 16px;
  background: rgba(4, 22, 48, .72);
  overflow-y: auto;
}
.dialog-card {
  width: min(100%, 620px);
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  padding: clamp(18px, 4vw, 28px);
  border: 2px solid var(--gold-line);
  border-radius: var(--radius);
  background: var(--paper);
  box-shadow: 0 18px 48px rgba(0, 0, 0, .35);
}
.dialog-card legend {
  width: 100%;
  margin-bottom: 8px;
  font-family: var(--serif);
  font-size: clamp(22px, 5vw, 30px);
  font-weight: 700;
  color: var(--navy);
}
.report-choice {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr);
  gap: 10px;
  align-items: start;
  min-height: var(--tap);
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  cursor: pointer;
}
.report-choice:focus-within {
  outline: 3px solid var(--gold-line);
  outline-offset: 2px;
}
.report-choice input { width: 20px; height: 20px; margin: 2px 0 0; }
.report-choice span { overflow-wrap: anywhere; }

body.dialog-open { overflow: hidden; }

/* ======================================================= LEARNER ACCOUNT == */

.nav-button {
  display: inline-flex; align-items: center; min-height: var(--tap); padding: 0 6px;
  border: 0; background: transparent; color: var(--gold-line);
  font: inherit; font-size: 14px; font-weight: 600; cursor: pointer;
}
.nav-button:hover, .nav-button:focus-visible { color: #fff; text-decoration: underline; }

.learner-dashboard {
  max-width: 980px;
  /* Keep the footer below the first viewport while authenticated data loads.
     Without this reservation the initially short loading state lets the footer
     appear, then moves it when the dashboard is revealed (mobile CLS > 0.1). */
  min-height: calc(100vh - 180px);
}
.learner-dashboard > .status-line:first-child { min-height: 1.5em; }
/* Reserve the static card structure while the API response is in flight. It
   remains invisible and outside the accessibility tree until render completes,
   but the finished dashboard no longer pushes the page around on reveal. */
#dashboard-content.hidden {
  display: block !important;
  visibility: hidden;
}
.login-identity {
  display: flex; gap: 10px 14px; align-items: center; flex-wrap: wrap;
  padding: 14px 16px; border: 1px solid var(--gold-line);
  border-radius: var(--radius); background: var(--paper);
}
.login-identity .field-label { flex: 1 0 100%; }
.login-identity strong {
  flex: 1 1 auto; color: var(--navy); font-size: clamp(20px, 4vw, 28px);
  letter-spacing: 0.06em; overflow-wrap: anywhere;
}
.dashboard-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 680px) {
  .dashboard-stats { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.term-progress { display: grid; gap: 12px; }
.term-progress-row {
  display: grid; grid-template-columns: minmax(58px, auto) minmax(100px, 1fr);
  gap: 6px 12px; align-items: center;
}
.term-progress-label { font-weight: 700; color: var(--navy); }
.term-progress-count {
  grid-column: 2; color: var(--muted); font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.focus-grid { display: grid; gap: 12px; }
@media (min-width: 720px) { .focus-grid { grid-template-columns: repeat(3, 1fr); } }
.focus-card {
  display: flex; flex-direction: column; align-items: flex-start; gap: 8px;
  padding: 16px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--cream);
}
.focus-card .btn { margin-top: auto; }
.focus-evidence, .account-current {
  margin: 0; color: var(--ink-soft); font-size: 14px; font-weight: 700;
}

/* A teacher's only enrolment decision: one row, two plain actions. */
.join-requests {
  padding: 16px; border: 1px solid var(--gold-line);
  border-radius: var(--radius); background: #fffaf0;
}
.request-count {
  padding: 4px 9px; border-radius: 999px; background: var(--navy); color: #fff;
  font-size: 13px; font-weight: 700;
}
.join-request-row {
  display: flex; gap: 12px; align-items: center; justify-content: space-between;
  flex-wrap: wrap; padding: 12px 0; border-top: 1px solid var(--line-soft);
}
.join-request-row:first-child { border-top: 0; }
.join-request-row > div:first-child { display: grid; gap: 2px; }
.join-request-login { color: var(--muted); font-size: 14px; letter-spacing: 0.03em; }
.learner-access {
  padding: 14px 16px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--paper);
}
.learner-access summary {
  min-height: var(--tap); display: flex; align-items: center;
  color: var(--navy); font-weight: 700; cursor: pointer;
}
.reset-once-note { display: block; margin-top: 3px; color: var(--wrong); font-size: 12px; }

@media (max-width: 520px) {
  .term-progress-row { grid-template-columns: 1fr; }
  .term-progress-count { grid-column: 1; }
  .join-request-row .btn-row { width: 100%; }
}

/* ======================================================= LEGAL / FOOTER == */

.site-footer {
  margin-top: 38px; padding: 22px 0;
  border-top: 1px solid var(--line);
  background: var(--paper); color: var(--muted);
  font-size: 14px; text-align: center;
}
.site-footer a, .footnote a {
  min-height: 44px; display: inline-flex; align-items: center;
  color: var(--navy); font-weight: 700; vertical-align: middle;
}
.legal-consent-note {
  margin: 0; padding: 12px 14px;
  border-left: 3px solid var(--gold-line);
  background: #fffaf0; color: var(--ink-soft);
  font-size: 14px; line-height: 1.55;
}
.legal-consent-note a { color: var(--navy); font-weight: 700; }

.legal-page { max-width: 920px; }
.legal-page h1 { font-size: clamp(32px, 6vw, 54px); }
.legal-page h2 { font-size: clamp(23px, 4vw, 31px); }
.legal-page h3 { margin-top: 20px; font-size: 19px; }
.legal-page p, .legal-page li { line-height: 1.68; }
.legal-page ul { padding-left: 1.25rem; }
.legal-page li + li { margin-top: 7px; }
.legal-hero {
  padding: clamp(24px, 5vw, 44px);
  border-top: 5px solid var(--gold-line);
  border-radius: var(--radius); background: #fff;
  box-shadow: var(--shadow);
}
.legal-updated { color: var(--muted); font-size: 14px; }
.legal-summary {
  padding: 24px clamp(20px, 4vw, 34px);
  border: 1px solid var(--gold-line); border-radius: var(--radius);
  background: #fffaf0;
}
.legal-summary h2 { margin-bottom: 12px; }
.legal-toc {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px; padding: 18px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--paper);
}
.legal-toc a {
  min-height: 44px; display: flex; align-items: center;
  padding: 8px 10px; border-radius: var(--radius);
  color: var(--navy); font-size: 14px; font-weight: 700;
}
.legal-toc a:hover, .legal-toc a:focus-visible { background: var(--cream); }
.legal-section {
  scroll-margin-top: 20px;
  padding: clamp(22px, 4vw, 34px);
  border: 1px solid var(--line); border-radius: var(--radius);
  background: #fff;
}
.legal-section h2 { margin-bottom: 15px; }
.legal-section p + p, .legal-section ul + p { margin-top: 14px; }
.legal-section p a {
  min-height: 44px; display: inline-flex; align-items: center;
  vertical-align: middle;
}
.legal-important { border: 2px solid var(--gold-line); }
.legal-contact { background: var(--navy); color: #f6f0df; }
.legal-contact h2, .legal-contact a { color: #fff; }
.legal-data-grid { display: grid; gap: 14px; margin: 18px 0 24px; }
.legal-data-grid section {
  padding: 16px; border: 1px solid var(--line-soft);
  border-radius: var(--radius); background: var(--cream);
}
.legal-data-grid h3 { margin: 0 0 6px; }
.legal-data-grid p { margin: 0; font-size: 15px; }
@media (min-width: 720px) {
  .legal-data-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .legal-toc { grid-template-columns: 1fr; }
}
/* ------------------------------------------------ formula / rule book --- */
.formula-page{display:grid;gap:24px;padding-bottom:48px}
.formula-hero{margin-top:24px}
.formula-controls{display:flex;align-items:end;gap:14px;flex-wrap:wrap}
.formula-controls .field{min-width:180px;margin:0}
.term-tabs{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px}
.term-tab{min-height:48px;border:1px solid var(--line);border-radius:8px;background:var(--paper);color:var(--navy);font:inherit;font-weight:800;cursor:pointer}
.term-tab.is-active{background:var(--navy);border-color:var(--navy);color:#fff}
.formula-section-head{align-items:end}
.formula-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}
.formula-card{display:grid;gap:14px;align-content:start;border-top:4px solid var(--gold)}
.formula-visual{margin:0;padding:12px;border:1px solid var(--line-soft);border-radius:10px;background:#fbfaf6;color:var(--navy)}
.formula-visuals{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:12px;margin:0 0 16px}
.formula-visual svg{display:block;width:100%;max-height:210px}
.formula-visual figcaption{margin-top:8px;color:var(--muted);font-size:.9rem;line-height:1.4}
.formula-visual-label{fill:currentColor;font:700 15px system-ui,sans-serif}
.formula-bar{fill:var(--gold);stroke:var(--navy);stroke-width:3}
.formula-expression{padding:18px;border-radius:8px;background:var(--navy);color:#fff;font-family:Georgia,'Times New Roman',serif;font-size:clamp(1.05rem,2.2vw,1.35rem);line-height:1.55;overflow-wrap:anywhere}
.formula-symbols,.formula-mistake{padding:12px 14px;border-radius:8px;background:var(--cream);border:1px solid var(--line-soft)}
.formula-symbols p,.formula-mistake p{margin:5px 0 0;line-height:1.55}
.formula-mistake{border-left:4px solid var(--warning,#a96500)}
.formula-explanation{margin:0;line-height:1.65;color:var(--muted)}
.formula-example{padding:14px 16px;border-left:4px solid var(--gold);background:#f8f5eb;border-radius:4px}
.formula-example p{margin:5px 0 0;line-height:1.55}
.formula-note p{margin:.45rem 0 0;line-height:1.65}
@media (max-width:700px){.formula-grid{grid-template-columns:1fr}.term-tabs{grid-template-columns:repeat(2,1fr)}.formula-print{width:100%}.formula-visuals{grid-template-columns:1fr}.formula-visual svg{max-height:180px}}
@media print{.masthead,.site-footer,.formula-controls,.term-tabs,.formula-note{display:none!important}.formula-page{display:block}.formula-hero{box-shadow:none;border:0;padding:0}.formula-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.formula-card{break-inside:avoid;box-shadow:none;margin:0 0 12px}.formula-expression{background:#fff;color:#000;border:1px solid #333}}

.field-help{display:block;margin-top:6px;color:var(--muted);font-size:.85rem;line-height:1.35}
.auto-count{display:flex;align-items:center;min-height:46px;font-weight:800;background:var(--cream)}
/* Proper fractions -------------------------------------------------------
   Learners type 4/5 on an ordinary phone keyboard; mathematical content is
   painted with the numerator above the denominator. The hidden slash remains
   in the DOM so copying and marking still receive the exact raw value. */
.math-frac {
  display: inline-grid;
  grid-template-rows: auto auto;
  min-width: 1.15em;
  margin: 0 .08em;
  vertical-align: middle;
  text-align: center;
  font-variant-numeric: lining-nums tabular-nums;
  line-height: 1;
}
.math-frac-num,
.math-frac-den {
  display: block;
  padding: .02em .14em;
  line-height: 1.05;
}
.math-frac-num {
  border-bottom: .075em solid currentColor;
}
.math-frac-slash {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
/* A multiplication sign drawn from diagonal strokes cannot become a plus
   when an Android user selects a decorative system font. The underlying ×
   remains in the DOM for copying, marking and assistive technology. */
/* IT SITS ON THE MATHS AXIS, NOT ON THE BASELINE.
   An inline-block rests its BOTTOM on the baseline, so a .72em box put the
   cross's centre .51em above the baseline while this font's own × and + sit
   at .287em — a third of the glyph's height too high, and "2 × 6 = ?" read as
   though the × were an exponent. Reported from a Grade 4 daily challenge.
   `middle` aligns the box's centre with the baseline plus half the x-height,
   which is where an operator belongs; measured, it lands within .037em of the
   font's own axis against .223em before. It is deliberately not a fixed em
   nudge: the whole reason this cross is drawn rather than typed is that an
   Android device may substitute a font, and `middle` follows whatever font
   arrives while a magic constant would not. */
.math-times {
  position: relative;
  display: inline-block;
  width: .72em;
  height: .72em;
  margin: 0 .08em;
  vertical-align: middle;
}
.math-times-source {
  font-size: 0;
  color: transparent;
}
.math-times::before,
.math-times::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: .76em;
  height: .09em;
  min-height: 1.5px;
  border-radius: 999px;
  background: currentColor;
  transform: translate(-50%, -50%) rotate(45deg);
}
.math-times::after {
  transform: translate(-50%, -50%) rotate(-45deg);
}
.math-entry-preview {
  display: flex;
  align-items: center;
  gap: .8rem;
  width: 100%;
  min-height: 3.25rem;
  margin-top: .45rem;
  padding: .55rem .8rem;
  border: 1px solid var(--line, #d9d1c1);
  border-radius: .55rem;
  background: var(--paper-soft, #f7f3ea);
  color: var(--ink, #071a3a);
}
.math-entry-preview.hidden { display: none; }
.math-entry-preview-label {
  color: var(--muted, #667085);
  font-size: .78rem;
  font-weight: 800;
  letter-spacing: .05em;
  text-transform: uppercase;
}
.math-entry-preview-value {
  font-family: Georgia, 'Times New Roman', serif;
  font-size: 1.35rem;
  font-weight: 700;
}

/* THE MOCK MEMORANDUM.
   A paper handed back. Read on a 375-pixel phone, so the mark and the prompt
   stack rather than sitting in columns, and the worked steps indent once and
   no further — nesting a method panel inside a numbered step inside a list
   already costs three levels of indent before any words appear. */
.memo-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.memo-row {
  border-top: 1px solid var(--line, #d9dee3);
  padding: 0.85rem 0;
}

.memo-row:first-child { border-top: 0; }

.memo-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
  margin: 0 0 0.35rem;
}

.memo-mark {
  flex: 0 0 auto;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  border-radius: 0.4rem;
  padding: 0.1rem 0.45rem;
  font-size: 0.9rem;
}

/* Colour is never the only signal: the mark itself reads "3/3" or "0/2", and
   an unanswered question says so in words. */
.memo-mark--full { background: #dff3e4; color: #14532d; }
.memo-mark--part { background: #fdf0d5; color: #7c4a03; }
.memo-mark--none { background: #fbe3e3; color: #7f1d1d; }

.memo-prompt { flex: 1 1 12rem; }

/* The learner's own working lines on a "Show my steps" paper, each with the
   mark it earned and the reason, under the question they belong to. */
.memo-lines {
  margin: 0.35rem 0 0.5rem;
  padding-left: 1.4rem;
  display: grid;
  gap: 0.3rem;
}
.memo-lines li { line-height: 1.4; }
.memo-lines .memo-mark { font-size: 0.8rem; margin-right: 0.35rem; }
.memo-line { font-variant-numeric: tabular-nums; }
.memo-line-why {
  display: block;
  font-size: 0.85rem;
  color: var(--muted, #5b6474);
}

.memo-answer {
  margin: 0 0 0.4rem;
  font-weight: 600;
}

.memo-steps {
  margin: 0;
  padding-left: 1.2rem;
  color: var(--muted, #4a5560);
}

.memo-steps > li { margin: 0.2rem 0; }
