/* One place the colours live.
 *
 * Thirty-four of the platform's hex values were written in two or more
 * stylesheets, seven of them in FOUR — the Warm Parchment core. Changing a
 * brand colour meant editing four files and hoping, and question-render.css
 * carried a comment admitting as much: "Keep the values in step with
 * player.css's :root if either changes." A rule kept by hand is a rule that
 * eventually is not.
 *
 * Linked FIRST by every app, so an app sheet can still override any of these
 * for itself — this is a floor, not a ceiling.
 *
 * TWO PALETTES, ON PURPOSE, and they must not be merged. Warm Parchment is
 * Bing's Academy: the doorway, the portal, the results conversation. The exam
 * chrome is a faithful copy of the real NSW test and answers to that paper,
 * not to our brand — its navy is not our navy, and if the brand shifts the
 * exam must not move with it. See the brand decision recorded for prestart
 * and time-up: brand at the doorway, the exam room stays the exam room.
 */
/* ── The two things the browser needs told, once, for every app ─────────────
 *
 * COLOUR-SCHEME. This platform is light by design — warm parchment on both
 * sides, and the exam room's own navy chrome is painted, not inherited. Saying
 * so is not a formality: the native controls we DO use (every <select>, the
 * scrollbars, a date field) are drawn by the operating system, and on a tutor
 * whose Mac is in dark mode they come out dark grey on a cream page. Declared
 * rather than left blank, so the browser stops guessing.
 *
 * TOUCH-ACTION. Removes the double-tap-to-zoom gesture, and with it the delay
 * the browser holds every tap for while it waits to find out whether a second
 * one is coming. It matters most in the one place tapping is fast and timed:
 * a child moving through thirty-five options with a clock running. Pinch-zoom
 * is UNAFFECTED — that is `touch-action: none`, which would be an
 * accessibility failure, and is deliberately not what this is.
 */
:root {
  color-scheme: light;
}

html {
  touch-action: manipulation;
}

:root {
  /* ── Warm Parchment: the Bing's Academy surface ── */
  --parchment:   #FAF6EE;
  --warm-white:  #FFFEF9;
  --sand:        #EDE0C8;
  --deep-ink:    #2E2318;
  --brown-ink:   #5A4020;
  --warm-stone:  #C8B898;   /* quote marks, muted accents, secondary data bars */
  --warm-tan:    #7A5A30;
  --text-primary:   #2E2318;
  --text-secondary: #6A5A48;
  --text-muted:     #8A7A68;
  --border:         #DDD0B8;
  --border-card:    #D4C4A0;
  --radius-sm: 6px;
  --radius-md: 12px;
  --radius-lg: 16px;
  --radius-pill: 50px;
  --sidebar-width: 216px;
  /* The exam's own face, used by the authoring PREVIEW too so a question is
     drafted in the type it will be sat in. */
  --exam-font: -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;

  /* ── The exam paper: answers to the real test, not to us ── */
  --navy:          #1d2a5c;
  --navy-dark:     #14204a;
  --chrome-center: #dce1ea;
  --pane-border:   #c9ced8;

  /* ── Marking, shared by the exam and the review screens ── */
  --correct-bg:      #E2F0E4;
  --correct-border:  #4F8A5B;
  --correct-text:    #2E6B3A;
  --incorrect-bg:    #FBE1DD;
  --incorrect-border:#C0392B;
  --flag-gold:       #E0B84D;
  /* "Started but incomplete" is navy-tinted, not yellow: gold is reserved for
     flagging, and the two states used to be near-identical pale yellows. */
  --partial-bg:      #E4E9F2;
  --partial-border:  #8F9DBC;

  /* ── STATE TINTS, Aug 2026 ───────────────────────────────────────────────
     The design port introduced one idea more often than any other: a row or a
     chip tinted by what it needs from you. The three values below were living
     as literals at ~27 call sites across a 3,000-line stylesheet, so changing
     what "needs attention" looks like meant a find-and-replace over three
     different hexes. They are the same family as --correct-* above and belong
     beside it.

     ATTENTION is the quietest: something to look at, nothing broken.
     WARN is a decision waiting on a person.
     BAD is something a student would actually hit. */
  --state-attention-bg:     #FFFCF2;
  --state-warn-bg:          #FFF7E6;
  --state-warn-border:      #E0B84D;   /* same value as --flag-gold, named for the state */
  --state-warn-text:        #7A5A00;
  --state-bad-bg:           #FBF1EE;
  --state-bad-border:       #E0BFB4;
  --state-bad-text:         #8A3E28;
  /* The hover ink for the dark primary button, which was a literal at every
     call site that had one. Named after what it is rather than after the
     button, because the tint under a dark surface is the same wherever the
     surface appears. */
  --ink-hover:              #4A3020;
  /* A BAR FILL, against the --sand track those bars sit in. --flag-gold on
     sand is about 1.4:1, well under the 3:1 a graphical object needs, so gold
     stays the FLAG colour and bars use this darker partner. It measures about
     2:1, which is still under — a deliberate call rather than an oversight:
     pushing it to 3:1 needs a muddy brown that reads as a different meaning,
     and every bar in this app has its figure written in text beside it. The
     bar is a second reading of a number, never the carrier. If a bar ever
     becomes the only place a figure appears, this fails and must change. */
  --bar-warn:               #C79A2E;

  /* ═══ TYPE — Aug 2026 ═══════════════════════════════════════════════════
     Additions only; nothing above this line changed.

     The old pairing was Plus Jakarta Sans over Epilogue, and it was not a
     pairing: two geometric sans faces with near-identical letterforms, tall
     x-heights and the same cool tone. Set one above the other and a heading
     and its paragraph differ only in weight, which is why the dense screens
     read as flat however the spacing is tuned — there was no typographic
     contrast available to grade them with.

     Three faults, in the order they cost the most:

       1. NO CONTRAST BETWEEN DISPLAY AND TEXT. A display face has to do
          something the body face cannot. These are cousins.
       2. EPILOGUE IS A DISPLAY GROTESQUE CARRYING LONG PROSE. Tight
          apertures, uniform stroke, made for headlines — and this platform is
          unusually full of long explanatory prose (the Honest Exam Mode
          notes, the review rules, the marking descriptors), read by a
          ten-year-old on one side and by someone six hours into a marking
          session on the other.
       3. PLUS JAKARTA SANS READS AS GENERIC SAAS — part of why the platform
          looked like a competent internal tool rather than a finished one.

     THE REPLACEMENT SPLITS BY JOB, NOT BY SIZE:

       --font-reading  LITERATA. Everything a person READS — headings, student
                       copy, prompts, essays, explanatory prose. Built for
                       long-form reading on screen, legible from 11px to 60px,
                       and it belongs on parchment.
       --font-ui       SOURCE SANS 3. Everything that is a CONTROL or a
                       MEASUREMENT — labels, buttons, chips, table headers,
                       figures. Humanist sans with genuine tabular figures,
                       which the tables need and a serif should not be asked
                       for.

     Serif to read, sans to operate.

     THE EXAM ROOM IS UNTOUCHED. --exam-font above is the CBT's own stack and
     stays exactly as it is. Nothing here reaches the exam chrome in
     player.css — see that file's header.

     TO REVERT: put 'Plus Jakarta Sans' back on --font-display and 'Epilogue'
     on --font-body, and swap the Google Fonts <link> in the four HTML files.
     Since Aug 2026 no stylesheet names a face directly, so those two lines
     are the whole switch — before this, the stacks were written out by hand
     in SIX stylesheets. */
  --font-reading: 'Literata', Georgia, 'Times New Roman', serif;
  --font-ui:      'Source Sans 3', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-body:    var(--font-ui);       /* alias — existing rules keep working */
  --font-display: var(--font-reading);  /* alias — headings are the reading face */

  /* SURFACES between parchment and sand. --hairline is the rule under a group
     heading and between done rows: lighter than --border, which is a card
     edge and too heavy for a line inside a list. */
  --sand-soft: #F3ECDD;
  --hairline:  #E8DCC4;
  --ink-faint: #B6A88F;   /* a score that was never revealed; a dashed edge */

  /* PASTEL ACCENTS from the style guide, used at the doorway only. Sage is
     the "new material has landed" tint the New pill already uses. */
  --sage:  #C8DCC0;
  --lilac: #D7CBE2;

  /* THE FOUR PAPERS, for the "what you've been sitting" bar only. Quiet on
     purpose and in no order of merit: these separate four subjects, they do
     not rank them, so none of them may read as better than another. Never use
     these for a result — marking has its own colours above. */
  --paper-reading:  #C8B898;
  --paper-maths:    #C8DCC0;
  --paper-thinking: #E3D4B8;
  --paper-writing:  #D7CBE2;

  /* ── TYPE SCALE ──────────────────────────────────────────────────────────
     Twenty-one distinct sizes were in use across 362 declarations, and the
     census says they were never twenty-one decisions — they are five clusters
     with rounding noise inside each:

        10, 10.5, 11, 11.5   52 uses   labels, chips, column headers
        12, 12.5             103       secondary lines, notes, meta
        13, 13.5             105       the workhorse: table cells, states
        14, 14.5, 15, 15.5   75        row titles, names, prose
        16, 17               14        panel headings
        19-27                11        page headings

     Nobody chose 13 over 13.5; whoever wrote the rule picked whichever looked
     right that afternoon, and the difference is invisible alone and grubby in
     aggregate. Six steps, named for the job:

     APPLIED SO FAR only where the source is already single — the shared table
     bones, the state chip, panel headings. The remaining ~200 call sites are
     deliberately NOT swept: each needs a judgement about whether its value was
     considered or accidental, and doing that in bulk is how a product acquires
     a thousand two-pixel regressions. The fingerprint harness makes that sweep
     safe to do incrementally, which is the point of naming these now.

     SWEPT ON THE STUDENT SIDE, 17 Aug 2026. The four student stylesheets —
     shell, portal, access, try — now take every size in the 11-28 range from
     these tokens. Two steps were ADDED rather than invented at call sites,
     because the census above was taken on the authoring app and the student
     screens lean on sizes it barely uses:

       --text-base  14px is the single most-used size a family ever reads
                    (28 declarations), and it sat in the gap between md and
                    lg with no name, so every use of it was a literal.
       --text-3xl   28px is the page HERO — the test name on the door, the
                    child's name on their page. `.panel h1` was 22px, 23px,
                    25px and 28px in four different files, for the same role
                    on four screens a family crosses in a minute.

     Nothing was renamed: --text-2xl is still 22px, so the authoring app is
     untouched by this. The remaining literals are deliberate — 18px (four
     distinct roles), 24px/30px (the result ring's two display states), and
     the exam room, which owns its own scale for CBT fidelity. */
  --text-xs:   11px;    /* uppercase labels, chips, column headers */
  --text-sm:   12.5px;  /* secondary lines, notes, the meta under a name */
  --text-md:   13.5px;  /* dense UI default: table cells, state words */
  --text-base: 14px;    /* student-side body prose — the most-read size here */
  --text-lg:   15px;    /* a row's title, a student's name */
  --text-xl:   16px;    /* panel heading, form inputs */
  --text-2xl:  22px;    /* secondary page heading; the hero, stepped down */
  --text-3xl:  28px;    /* the page hero */

  /* ── BREAKPOINTS ────────────────────────────────────────────────────────
     NOT tokens, and they cannot be: a media query cannot read a custom
     property. Written here anyway because they are part of the system and a
     value invented at a call site is how eleven of them accumulated —
     520, 560, 640, 700, 720, 860, 900, 1000, 1080, 1100, 1180 — with no two
     screens agreeing on when a table should stop being a table.

     Four steps, and each says what it is FOR:

       1200px  A MULTI-COLUMN TABLE BECOMES STACKED ROWS.
               Measured, not chosen: a table needs about 200px for the name it
               is sorted by. At 1120 the Tests name column was 113px and
               wrapping to two lines while still drawn as five columns. Tests
               needs ~1207, Students ~1180, the rest less — so one value above
               all of them, and no table ever stacks while its neighbour
               stays tabular.
       1000px  TWO PANES BECOME ONE COLUMN.
               Content side by side survives narrower than a five-column table
               does: the passage editor, the trajectory and its bars, the
               question editor and its preview.
        700px  THE APP CHROME GOES NARROW — the sidebar and the tag bar.
        560px  PHONE. Single column, full-width actions, 44px minimum targets.

     Component-level queries below these (modal padding at 640, and so on) are
     deliberately left alone: they are about one component's own contents, not
     about the shape of the page. */

  /* SPACING. A four-step rhythm, so the vertical gaps come from a scale
     rather than from whatever looked right in the moment. */
  --space-1:  4px;
  --space-2:  8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 44px;
}


/* ═══════════════════════════════════════════════════════════════════════════
   APPEND TO tokens.css — four additions and one change.
   Nothing above the append point moves.

   Written after reading the real file. Six of the ten tokens I proposed from
   the snapshot are RETRACTED because the file already has them or already had
   a reason not to; the retractions are listed in the branch README. What is
   left is what the file genuinely does not have, and each one already has
   literals standing in for it — which is the only evidence worth acting on.
   ═══════════════════════════════════════════════════════════════════════ */

:root {

  /* ── THE RED FAMILY'S MISSING TEXT COLOUR ────────────────────────────────
     --correct-bg / --correct-border / --correct-text all exist. The red family
     stops at --incorrect-border, so every red LABEL in the product has had to
     borrow the border hex or invent its own, and both happened:

       shell.css  .review-mark.is-wrong  color: #8E2A1F
       shell.css  .rr-error              color: #A33B2E

     Two ad-hoc reds for one role, neither of them the border colour, because
     whoever wrote them could feel that #C0392B was too light and guessed a
     replacement. #C0392B on --incorrect-bg measures 4.38:1 — under AA.

     #A5301F is 5.56:1 on --incorrect-bg, which is what --correct-text manages
     on its own background (5.43:1). The two halves of the marking pair now
     read at the same strength, which is the actual point: they are a pair. */
  --incorrect-text: #A5301F;

  /* ── THE QUIET LEVEL THAT WAS NEVER LEGAL ────────────────────────────────
     On --parchment: --text-primary 12.5:1, --text-secondary 6.1:1,
     --text-muted 3.8:1, --ink-faint about 2.4:1.

     Between 6.1 and 3.8 there is nothing, so anything wanting to be one step
     quieter than secondary had two options: use secondary anyway and lose the
     step, or use muted and fail AA. Both happened, and the second one happened
     with its own literals:

       portal.css  .portal-shelf     #8A7B66   ~3.9:1 at 13.5px
       portal.css  .portal-wordmark  #A2917A   ~2.9:1 at 12.5px

     Neither is a decoration. A shelf label names a month; the wordmark is the
     one way off the page.

     4.75:1 on --parchment, legal at any size, and visibly a step down from
     --text-secondary rather than a repaint of it. --text-muted keeps its value
     and its place in the file; what changes is its ROLE — see below. */
  --text-tertiary: #7C6B58;

  /* --text-muted (#8A7A68) IS DECORATION FROM HERE ON.
     Hairlines, chevrons, separator glyphs, a bar fill, a disabled edge. Never a
     word or a figure a person has to read. It is currently carrying meaning at
     eight sites in shell.css alone — the list is in the branch README, and it
     is a mechanical swap to --text-tertiary at each. If test_tokens.py grows
     one more assertion, this is the one worth having. */

  /* ── MEASURE ─────────────────────────────────────────────────────────────
     The band descriptors in the writing room are the hardest reading on the
     platform and they run about 75 characters. .what-next already pins 56ch
     and .open-offers-lead 56ch, both by hand, which is the same decision made
     twice — so it is a token. 62ch for long-form prose a child reads; 56ch is
     kept as the narrower measure the shell already uses for advice lines. */
  --measure-prose: 62ch;

  /* ── ONE CHANGE, NOT AN ADDITION ─────────────────────────────────────────
     --radius-pill: 50px → 999px.

     You asked me to rule on the 22 hard-coded 999px. THE HARD-CODES ARE RIGHT
     AND THE TOKEN IS WRONG. 50px only reads as a pill while the element is
     under 100px tall; above that the sides stop being semicircles and the
     shape reads as a rounded rectangle that has been got slightly wrong. So:

       shell.css   .time-bar, .time-bar-fill, .review-mark   999px
       shell.css   .btn-start, .open-redo, .save-page-btn,
                   .next-steps-link, .btn-ghost-quiet,
                   .share-btn, #rr-send                       50px

     One file, one intent, two values — and the 999s are on the two things that
     are either very short (a chip) or very thin (a bar), where 50px would have
     been visibly wrong immediately. The 50s are on buttons, where at desktop
     padding it happens to look fine and at 44px min-height on a coarse pointer
     it starts not to.

     Changing the token makes all 22 hard-codes correct and lets them be swept
     into it. Deleting the hard-codes first would make two bars and every chip
     visibly wrong. The README has the sweep order.

     (The related measurement from README-FOR-DESIGN: 167 literal radius
     declarations against 146 tokenised. This is one of the two reasons — the
     token being wrong for half its call sites is how a scale gets bypassed.) */
  --radius-pill: 999px;
}

/* ── FIVE TOKENS THAT ARE REFERENCED AND DO NOT EXIST ──────────────────────
   NOT added here, deliberately. Recorded so the decision is visible.

     --ink            shell.css ×3, try.css ×3, portal.css ×2, authoring.css ×1
     --border-strong  try.css, writing.css, portal.css ×2, 04-lists.css
     --surface        try.css
     --surface-raised try.css, 04-lists.css
     --bad            try.css

   Every reference is a two-argument var(), so every one renders its fallback and
   the page looks correct. Nothing catches it: not review, and not test_tokens.py,
   which can only check tokens that are present.

   WHERE THE HABIT LIVES: all eight --ink references are student-side (shell ×3,
   try ×3, portal ×2). The current tutor app never names it — it uses --ink-faint
   and --ink-hover, both real. Of the five phantoms the tutor app touches only
   two, both in 04-lists.css. So this is a habit on the side a family sees.

   (An earlier draft of this patch claimed --ink fell back to #2E2A24 in the
   tutor app, making "two inks, one name". That came from an authoring.css in an
   older handover folder, not from the current six sheets. Retracted.)

   ADDING THEM WOULD BE THE WRONG FIX. All five are aliases of tokens that
   already exist — --deep-ink, --border-card, --warm-white, --sand-soft, and now
   --incorrect-text — so defining them would double the vocabulary for one role
   each and give every future rule two correct names to choose between. The call
   sites are repointed instead: see the four door and shell patches.

   THE RULE WORTH ADOPTING: no two-argument var() in this codebase. A fallback
   inside a design system converts a missing token from a bug into a working line
   of CSS. writing.css writes var(--font-reading, Georgia, serif) three times on a
   token that does exist — harmless in itself, and exactly the habit that let the
   five above go unnoticed. That one is greppable and enforceable. */

/* ── A FIFTH NAMED STEP: 900 ───────────────────────────────────────────────
   Decision, since you asked me to make it. 900 is used NINE times in the tutor
   app doing the job this file assigns to 1000 — settings-grid, editor-grid,
   marking-grid, horizon-weeks, prep-grid, pr-row, cs-row, paper-row/call-item,
   startpage-page. Nine uses is not a stray; it is an undocumented step.

   Moving those nine to 1000 means re-walking nine working surfaces to prove
   each still holds 100px narrower, for no user-visible gain. Naming 900 costs a
   comment and describes what the code already does.

   And there is a real distinction underneath, which is why the code drifted
   here rather than anywhere else — 1000's definition is doing two jobs:

     1000px  TWO PANES OF CONTENT BECOME ONE COLUMN.
             Both halves are READ: the passage editor, the trajectory and its
             bars, a question and its preview, the student-detail panes.
      900px  A FORM AND ITS COMPANION BECOME ONE COLUMN.
             One half is INPUT and the other is reference: settings and their
             previews, the marking pane and its rubric, prep and its picker.
             A form tolerates less width than prose does before its labels and
             fields stop lining up, so it collapses sooner.

   So: keep 1200, narrow 1000's wording to "panes of content", add 900, keep
   700 and 560. Five steps, each still saying what it is FOR.

   The genuinely component-level widths — 520, 640, 860, 1080, and writing.css's
   780 — stay exempt, as this file already says. Counting them in the fourteen
   makes the problem look bigger than it is: the number of PAGE-SHAPE
   breakpoints in play is six, and five of them now have names. */

/* ── WHAT A BREAKPOINT CHANGES, since the breakpoint itself cannot be a token
   ────────────────────────────────────────────────────────────────────────────
   tokens.css already names four steps and says what each is FOR, which is more
   than most systems manage. The gap is that no stylesheet reads them: the page
   gutter is written out at every surface (.view padding 24px in shell,
   var(--space-5) in portal, 40px/48px on .view-top), so "the phone gutter" is
   not a thing that can be changed.

   This is the smallest version of the fix — one property, reassigned at the
   named steps, so a surface can stop deciding. It does NOT attempt the
   consolidation of the 14 widths; that is a separate pass and the README lists
   the four queries on the student side that disagree with the named steps. */
:root { --gutter: var(--space-5); }          /* 24px */
@media (max-width: 560px) { :root { --gutter: var(--space-4); } }   /* 16px */
