/* The practice sheet.
 *
 * Paper, not a dashboard. There is no rule in this file for a score, a streak,
 * a progress bar or a point total, because there is nothing in the markup to
 * style (constitution, Principle I).
 *
 * ——— the paper, and why these exact numbers ———
 *
 * figures.js draws every figure at 1:1 on a 20px grid (`U = 20`) so its axes
 * fall on lines the paper already has. That only works if the paper's rule is
 * also 20px — 1.25rem at a 16px root. Change one and the figures sit half a
 * square off.
 *
 * The values below are the ones prototypes/question-review.html uses, so the
 * same item drawn in both places looks the same. That is quickstart check 5,
 * and it is the reason the renderer was extracted in the first place.
 */

/* Only the two the paper owns. Every other token — colors, fonts, shadows —
 * comes from shell.css, which takes them from the landing page so the app and
 * the marketing site cannot drift. shell.css loads last, so nothing here may
 * redefine a shared name and expect to win. */
:root {
  /* The grid, printed in the same navy as every other printed thing on the
   * sheet. Measured off a 1440 screenshot: a line reads at 1.29:1 against the
   * open paper, which is the faint blue ruling of a real quadrille pad. */
  --rule: rgb(30 58 95 / 14%);

  /* The margin rule, printed darker than the grid so it reads as the margin
   * and not as one more line of the quadrille. */
  --margin-rule: rgb(30 58 95 / 40%);
  --square: 1.25rem;      /* = 20px = U in figures.js. Load-bearing. */

  /* ——— paper under prose ———
   *
   * The grid is a design decision and it is not free: ruled lines running
   * behind a paragraph cost some legibility, and a reading passage is the
   * worst case because it is the most text on any sheet. So a text block lays
   * a wash over the paper, and the grid shows through it: calmer under the
   * words, still the same sheet.
   *
   * This value is measured, not felt (T60, 2026-10-26). The wash was 86%,
   * and under it a grid line came out three values of 255 from the paper
   * around it, a 1.03:1 contrast the eye cannot see: the comment above it
   * claimed the grid was "still visible through it" and it was not, so every
   * text block was in fact a plain card on the paper. At 50% a line under a
   * block measures 1.13:1 against the block's own ground (open paper: 1.29:1),
   * which is visibly ruled and still well under what competes with type.
   * Graphite on the ruled line stays above 13:1. Do not raise this without
   * measuring the line again off a screenshot. */
  --wash: rgb(252 252 249 / 50%);

  /* The answer is green, and nothing that is not the answer may be.
   * Same value and same rule as the lesson whiteboard (walkthroughs/demo/
   * stage.css): a learner meeting both surfaces should not have to learn two
   * colour languages. The red pencil stays the wrong answer.
   *
   * Darkened from #15803d on 2026-09-19, in both files together. The
   * read-back page prints the right answer in green on the page's own pale
   * green ground, where #15803d measured 4.1:1. This one measures 5.4:1
   * there and 6.4:1 on the sheet. */
  --correct: #146b33;

  /* The width of the sheet's own margin: where the correcting hand writes.
   * The question sheet leaves it empty and prints a rule down it, the way a
   * page of notebook paper does. The result sheet fills it with the labels
   * that do the teaching. */
  --sheet-margin: 7.5rem;
}

* { box-sizing: border-box; }

.sheet {
  --pad: clamp(1.4rem, 3.5vw, 2.2rem);
  /* Where the printed margin rule falls: just past the margin column, so the
   * typed labels sit outside it and the work sits inside it, which is the
   * whole geometry of a marked page. */
  --rule-at: calc(var(--pad) + var(--sheet-margin) + 0.7rem);

  max-width: 52rem;
  min-height: 26rem;          /* a leaf of paper, not a card with a question on it */
  margin: 0 auto;
  padding: var(--pad);
  background:
    /* The printed margin: one hairline down the page, in the same ink the
     * grid is printed in, 3.4rem from the edge. It is part of the paper, not
     * a stripe on a card — it sits inside the sheet and never on its border
     * (the accent spine is banned; see developer.md). */
    linear-gradient(90deg, transparent 0 var(--rule-at), var(--margin-rule) var(--rule-at) calc(var(--rule-at) + 1.5px), transparent calc(var(--rule-at) + 1.5px)),
    repeating-linear-gradient(var(--rule) 0 1px, transparent 1px var(--square)),
    repeating-linear-gradient(90deg, var(--rule) 0 1px, transparent 1px var(--square)),
    var(--sheet);
  border: 1px solid var(--hair-hard);
  border-radius: 0.2rem;
  box-shadow: 0 10px 26px rgb(32 35 30 / 10%);

  /* ——— digits must sit on the baseline ———
   *
   * Scoped to the sheet, not the page: the chrome is Instrument Sans like the
   * landing page, and only the questions use this serif.
   *
   * Georgia is deliberately absent from this stack. Its figures are old-style
   * (text figures): 3, 4, 7 and 9 descend below the baseline while 0, 1 and 2
   * sit on it. That is handsome in prose and wrong for math — "7n + 3 + 4m"
   * reads as though the variables float above their coefficients, and the .2
   * in "40.2" looks raised. Georgia ships no lining alternate, so the fault
   * cannot be styled away; the font has to go.
   *
   * Every family below defaults to lining figures. lining-nums makes it
   * explicit for any that offer both, and tabular-nums keeps digits on a fixed
   * width so a column of numbers lines up.
   */
  font-family: Charter, "Bitstream Charter", Cambria, "Times New Roman", Times, serif;
  font-variant-numeric: lining-nums tabular-nums;
  font-feature-settings: "lnum" 1, "tnum" 1;
  color: var(--graphite);
}

/* ——— the margin, on every sheet ———
 *
 * A question sheet and a marked sheet are the same page: two columns, the
 * printed rule between them, the typed labels outside it and the work inside.
 * Scoped to sheets that carry a topic, because the legal pages and the error
 * ledger are also .sheet and are prose and a table, not paperwork. */
.sheet:has(> .topic) {
  display: grid;
  grid-template-columns: minmax(0, var(--sheet-margin)) minmax(0, 1fr);
  column-gap: 1.4rem;
  align-content: start;
}

.sheet:has(> .topic) > * { grid-column: 2; min-width: 0; }

/* An annotation spans both columns and lays its own margin out on the same
 * track, so a typed label beside the answer lines up with the topic above it. */
.sheet:has(> .topic) > .annot { grid-column: 1 / -1; }

.sheet > .topic {
  grid-column: 1;
  grid-row: 1;
  margin: 0.35rem 0 0;
  text-align: right;
  /* Balanced, like the base rule: `pretty` here left "Adjectives & /
   * Adverbs" and "Phrases & / Clauses" with a word alone on the second line
   * of the margin (wrapcheck at 768 and 1024). */
  text-wrap: balance;
}

/* A topic the margin cannot hold comes out of it and runs across the top of
 * the sheet instead (CurriculumHelper#topic_class counts the lines). In the
 * margin it shattered: "Percentages," / "rates," / "statistics and" /
 * "probability", three lines of one word. The name line moves down a row to
 * make space, which is the arrangement a phone already gets. */
.sheet > .topic.topic-wide {
  grid-column: 1 / -1;
  grid-row: 1;
  text-align: left;
  margin: 0 0 1rem;
  text-wrap: balance;
}

.sheet:has(> .topic-wide) > .nameline { grid-row: 2; }

/* ——— the name line ———
 *
 * Every worksheet anybody has been handed starts with one. It sits on the
 * sheet's first row opposite the topic, and the stem starts on the row under
 * it, so the paper reads top-down the way paper does. */
.nameline {
  grid-column: 2;
  grid-row: 1;
  justify-self: end;
  display: inline-flex;
  align-items: baseline;
  gap: 0.5rem;
  margin: 0.1rem 0 0;
  white-space: nowrap;
}

.nameline-label {
  font-family: var(--f-type);
  font-size: 0.64rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--graphite-3);
}

/* Written on the rule, in the hand the answers are written in. */
.nameline-name {
  min-width: 5.5rem;
  padding: 0 0.4rem 0.1rem;
  font-size: 1.02rem;
  color: var(--graphite);
  border-bottom: 1px solid var(--graphite-3);
}

.nameline-date {
  font-family: var(--f-type);
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  color: var(--graphite-3);
}

/* On a phone the margin would eat a third of the line, so the page is torn
 * off at the margin and the rule goes with it. */
@media (max-width: 42rem) {
  .sheet { min-height: 0; background-image:
    repeating-linear-gradient(var(--rule) 0 1px, transparent 1px var(--square)),
    repeating-linear-gradient(90deg, var(--rule) 0 1px, transparent 1px var(--square)); }
  .sheet:has(> .topic) { grid-template-columns: minmax(0, 1fr); }
  .sheet:has(> .topic) > *, .sheet > .topic { grid-column: 1; }
  .sheet > .topic { grid-row: auto; text-align: left; margin: 0 0 0.9rem; }
  .nameline { grid-column: 1; grid-row: auto; justify-self: start; margin: 0 0 1rem; }
}

/* The skill this question came from. It was set at 65% of navy, which on a
 * ruled background read as a smudge rather than a label. */
.topic {
  margin: 0 0 1.4rem;
  font-size: 0.72rem;
  font-family: var(--f-type);
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--navy);
  /* A two-line topic in a 7.5rem margin balances its lines ("Data and / line
   * graphs"), rather than leaving "graphs" alone on the second (wrapcheck). */
  text-wrap: balance;
}

.stem {
  margin: 0 0 1.6rem;
  font-size: clamp(1.28rem, 1.05rem + 1.1vw, 1.75rem);
  font-weight: 600;
  line-height: 1.32;
  letter-spacing: -0.012em;
  text-wrap: pretty;
}

/* On the result sheet the question is restated so the explanation has
 * something to be about. Quieter than the live question, and no longer at 70%
 * opacity: it still has to be readable, because it is the thing being
 * explained. */
.stem.restated {
  font-size: 1.05rem;
  font-weight: 500;
  color: var(--graphite-2);
}

/* A figure is drawn at 1:1 so its lines fall on lines the paper already has.
 * It must still not push the page sideways on a narrow screen. */
.figure {
  margin: 0 0 1.75rem;
  max-width: 100%;
  overflow-x: auto;
}

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

/* ——— a stacked fraction on the sheet ———
 *
 * Same markup and same class names the lesson player uses
 * (walkthroughs/demo/walkthrough-demo.html, visuals.js), because a fraction a
 * learner meets in a walkthrough has to be the fraction they meet in the
 * question. Anything that changes here changes there.
 *
 * A stack is two half-height rows, so at the body size it reads smaller than
 * the digits beside it. Derick, 2026-10-26: "we need to make sure that
 * they're big enough to read because we lose a little bit of size on those...
 * maybe with extra leading on those pages that have fractions." So the stack
 * is set above its line, and any line that got one is given the room for it
 * rather than letting the two rows crowd the text above and below.
 */
.frac {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  vertical-align: middle;
  margin: 0 0.2em;
  font-size: 1.15em;
}
.frac .num,
.frac .den { line-height: 1.1; padding: 0 0.1em; }
.frac .bar {
  width: 100%;
  min-width: 1.4em;
  height: 2px;
  background: currentColor;
  margin: 3px 0;
}

/* ——— hover a small number and it grows ———
 *
 * Derick, 2026-10-26, answering the fraction question: "maybe you can hover
 * over a fraction. Or, for any subscript/superscript type thing that's a
 * little bit small, you can hover on anything, and it'll make it a little bit
 * bigger for you."
 *
 * A transform rather than a font-size, so nothing around it moves and the
 * line it sits on does not reflow under the pointer. Only where there is a
 * real pointer: on a phone there is no hover, and a :hover rule there sticks
 * after a tap. A sup or sub is an inline box and cannot be transformed, so it
 * is made inline-block in the same breath. Its line-height is pinned to 1 so
 * the box is the glyph and nothing else: 0 collapsed it to no height at all,
 * which transforms fine and cannot be hovered with a real pointer, and the
 * inherited value would have made it taller than the line it sits in.
 */
sup, sub { display: inline-block; line-height: 1; }

@media (hover: hover) and (pointer: fine) {
  .frac, sup, sub {
    transition: transform 0.12s ease;
    transform-origin: center;
  }
  .frac:hover, sup:hover, sub:hover {
    transform: scale(1.4);
    position: relative;
    z-index: 2;
  }
}

@media (prefers-reduced-motion: reduce) {
  .frac, sup, sub { transition: none; }
}

/* A fraction and the punctuation it ran into are one unbreakable run, so a
 * question mark cannot start the next line and a period cannot float a fifth
 * of an em away from the number it ends. */
.frac-run { white-space: nowrap; }
.frac-run > .frac { margin-right: 0; }
.frac-run > .frac:not(:first-child) { margin-left: 0; }

/* The leading, only where a fraction actually landed. */
.stem:has(.frac),
.working:has(.frac),
.annot:has(.frac),
.verdict:has(.frac),
.misconception:has(.frac),
.review-answer:has(.frac),
.options label:has(.frac) { line-height: 2; }

/* When one option in a list is a stack, every option in that list stands as
 * tall as one. A lettered column whose rows come out at two heights reads as
 * two kinds of control rather than one set of answers. The height is the
 * stack's own (two rows of 1.15em at 1.1, the 2px bar and its 3px margins)
 * plus the label's padding, so it follows the type instead of being a number
 * somebody measured once. */
.options:has(.frac) label {
  min-height: calc(2.53em + 8px + 1.5rem);
  align-items: center;
}

.options { list-style: none; margin: 0 0 1.5rem; padding: 0; counter-reset: opt; }

.options li + li { margin-top: 0.45rem; }
.options li { counter-increment: opt; }

/* An option is a line to tick, not a button. The box on the left is the tick
 * box a paper answer sheet has; the line under it is where the pencil goes. */
.options label {
  display: flex;
  gap: 0.8rem;
  align-items: baseline;
  padding: 0.75rem 0.95rem;
  font-size: 1.04rem;
  font-weight: 400;
  line-height: 1.45;
  text-wrap: pretty;
  background: var(--wash);
  border: 1px solid var(--hair);
  border-radius: 3px;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}

/* The bubble off an answer sheet, lettered, and filled in graphite when it is
 * chosen. It replaces the browser's own radio, which was the one control on
 * the sheet that still looked like a form on a website. The input is still
 * there and still does the work: it is moved under the bubble and made
 * transparent, so the hit area, the keyboard behaviour and the accessibility
 * tree are the platform's and only the drawing is ours. */
.options label::before {
  content: counter(opt, upper-alpha);
  display: grid;
  place-items: center;
  flex: none;
  width: 1.6rem;
  height: 1.6rem;
  align-self: center;
  font-family: var(--f-type);
  font-size: 0.78rem;
  line-height: 1;
  color: var(--graphite-3);
  border: 1.5px solid var(--hair-hard);
  border-radius: 50%;
  background: var(--sheet);
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.options input[type="radio"] {
  position: absolute;
  width: 1.6rem;
  height: 1.6rem;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.options label {
  position: relative;
}

.options label:hover::before { border-color: var(--navy); color: var(--navy); }

.options label:has(input:checked)::before,
.options input:checked ~ span ~ .bubble {
  background: var(--graphite);
  border-color: var(--graphite);
  color: var(--sheet);
}

.options label:hover,
.options label:has(input:focus-visible) { border-color: var(--navy); }
.options label:hover { background: var(--navy-wash); }

.options label:has(input:focus-visible) {
  outline: 3px solid color-mix(in srgb, var(--navy) 45%, transparent);
  outline-offset: 2px;
}

.options input:checked + span { font-weight: 600; }

/* An option is a short line of content, so its break is the browser's to
 * choose well: balanced, it breaks at the comma in "Add 13 and 8, then add 3
 * to the sum" instead of after "3 to" (wrapcheck at 390). */
.options label > span { text-wrap: balance; }

.written {
  width: 100%;
  max-width: 14rem;
  padding: 0.55rem 0.2rem;
  font: inherit;
  font-size: 1.3rem;
  background: transparent;
  border: 0;
  border-bottom: 2px solid var(--navy);
  box-sizing: border-box;
}

/* ⚠ The top margin belongs to the answer row, where the Check button sits
 * under a written answer, and to the Next button on the result sheet. It
 * used to be on the bare `.check` class, which is the primary button in the
 * whole app: every Add, Save and Start button in a row of fields was pushed
 * 24px below the fields beside it, which is what Derick saw on the family
 * form ("I don't like that the Add button is dropped down a little bit and
 * not on the same line"). */
.answer-row .check, .next { margin-top: 1.5rem; }

.check, .next {
  padding: 0.6rem 1.4rem;
  font: inherit;
  color: #fff;
  background: var(--navy);
  border: 0;
  border-radius: 3px;
  cursor: pointer;
}

/* ——— the marked-up sheet ———
 *
 * The thesis, drawn. A miss turns the sheet into a graded page: the work stays
 * in the body column and every piece of teaching gets a typed label out in the
 * margin, each with the proofreader's caret pointing at the line it is about.
 *
 * Both grids use --sheet-margin for the first track, so a label in a nested
 * .annot lines up with one written straight into the sheet. No subgrid needed,
 * and nothing to keep in step by hand. */
.result {
  display: grid;
  grid-template-columns: minmax(0, var(--sheet-margin)) minmax(0, 1fr);
  gap: 0 clamp(0.9rem, 2vw, 1.6rem);
  align-items: start;
}

.result > * { grid-column: 2; }

.annot {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: minmax(0, var(--sheet-margin)) minmax(0, 1fr);
  gap: 0 clamp(0.9rem, 2vw, 1.6rem);
  align-items: start;
  margin: 0 0 1.15rem;
}

.annot > :last-child { grid-column: 2; margin: 0; }

/* The hand in the margin. Typed, like every label a worksheet comes with, and
 * the caret because that is the mark this product is named after. */
.hand {
  grid-column: 1;
  margin: 0;
  padding-top: 0.28rem;
  font-family: var(--f-type);
  font-size: 0.68rem;
  letter-spacing: 0.07em;
  line-height: 1.45;
  text-transform: uppercase;
  text-align: right;
  color: var(--navy);
  /* "Put them / in order", never "Put them in / order": a two-line label in
   * the margin balances its lines (wrapcheck at 1024). */
  text-wrap: balance;
}

.hand.is-miss { color: var(--miss); }

.hand .caret {
  display: block;
  width: 0.85rem;
  height: 0.45rem;
  margin: 0 0 0.25rem auto;
}

.hand .caret svg { display: block; width: 100%; height: 100%; overflow: visible; }
.hand .caret path {
  fill: none;
  stroke: currentColor;
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* The teacher's mark at the verdict line: a tick for a right answer, a cross
 * for a miss, drawn in the margin before a word is written beside it. Green
 * and crimson mean exactly this and appear exactly here. */
.hand.is-right { color: var(--correct); }

.hand .mark {
  display: block;
  width: 1.15rem;
  height: 0.9rem;
  margin: 0.15rem 0 0 auto;
}

.hand .mark svg { display: block; width: 100%; height: 100%; overflow: visible; }
.hand .mark path {
  fill: none;
  stroke: currentColor;
  stroke-width: 2.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.annot-verdict { margin-bottom: 1.1rem; }
.annot-verdict .verdict { margin-bottom: 0; }
.annot-verdict .tiles, .annot-verdict .pairs { margin-top: 0.75rem; margin-bottom: 0; }

/* The annotation arrives the way a hand writes it: from the margin, quickly,
 * and it is the only thing on the page allowed the second ease. */
@media (prefers-reduced-motion: no-preference) {
  .result .annot .hand { animation: mark-in 0.42s var(--ease-mark) both; }
  .result .annot:nth-of-type(2) .hand { animation-delay: 0.07s; }
  .result .annot:nth-of-type(3) .hand { animation-delay: 0.14s; }
}

@keyframes mark-in {
  from { opacity: 0; transform: translateX(0.5rem); }
  to   { opacity: 1; transform: none; }
}

@media (max-width: 42rem) {
  .result, .annot { grid-template-columns: minmax(0, 1fr); }
  .result > *, .annot > :last-child { grid-column: 1; }
  .hand { grid-column: 1; text-align: left; padding-top: 0; margin-bottom: 0.3rem; }
  /* Folded above the thing it marks, the caret points along the line rather
   * than down at it, and it sits in the text instead of floating over it. */
  .hand .caret {
    display: inline-block;
    width: 0.5rem;
    height: 0.85rem;
    margin: 0 0.4rem -0.1rem 0;
    transform: rotate(90deg);
  }
  /* The tick or cross stays the shape it is, at the start of the line the
   * verdict follows on. */
  .hand .mark { display: inline-block; width: 1rem; height: 0.8rem; margin: 0; }
  .annot-verdict .hand { margin-bottom: 0.1rem; }
}

.verdict {
  margin: 0 0 1.1rem;
  font-size: 1.16rem;
  font-weight: 600;
  line-height: 1.45;
}

.result.wrong .verdict { color: var(--miss); }
.result.right .verdict { color: var(--correct); }
.verdict strong { color: var(--correct); }

/* The disclosure a right answer keeps its explanation behind. The marker is
 * 1em — as tall as the text beside it, the standing rule for every expander —
 * and the summary row is a real tap target. */
.why { margin: 0 0 0.6rem; }
.why summary {
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 0.45rem;
  min-height: 44px;
  font-weight: 600;
  list-style: none;
}
.why summary::-webkit-details-marker { display: none; }
.why summary::before { content: "\25B8"; font-size: 1em; line-height: 1; }
.why[open] summary::before { content: "\25BE"; }

/* A misconception used to carry a 4px red spine down its left edge, which is
 * the banned accent stripe. The margin label says what it is now, so the block
 * itself only needs its own ground. */
.misconception {
  margin: 0;
  padding: 0.75rem 1rem;
  font-size: 1.01rem;
  line-height: 1.6;
  /* The crimson tint over the same wash every other text block lays on the
   * paper, so the grid reads the same under a miss as under the working. */
  background:
    linear-gradient(color-mix(in srgb, var(--miss) 7%, transparent), color-mix(in srgb, var(--miss) 7%, transparent)),
    var(--wash);
  border: 1px solid color-mix(in srgb, var(--miss) 22%, transparent);
  border-radius: 3px;
}

.misconception-label { display: none; }

.working {
  margin: 0;
  padding: 0.55rem 0.9rem;
  font-size: 1.01rem;
  line-height: 1.68;
  background: var(--wash);
  border-radius: 3px;
  /* The text is a different sentence on every question, so a widow here
   * cannot be fixed by choosing the break: the browser has to avoid it.
   * wrapcheck caught "times." and "cells." alone on a last line. */
  text-wrap: pretty;
}

/* The long way round. Quieter than the one-line version above it and not
 * faded: it is the part a student actually reads after a miss. */
.working.full { color: var(--graphite-2); }

.lesson-offer { margin: 1.5rem 0 0; }

.lesson-offer a { color: var(--navy); }

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

/* The same idea for a password manager: a filled username field present in a
 * change-password form, invisible on screen. Not display:none or
 * type="hidden", which managers skip when deciding what a form is for. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Every interactive element must be reachable and visible by keyboard. In the
 * printing's navy: a crimson ring on a button said "wrong" about a thing that
 * had only been focused. */
:focus-visible {
  outline: 3px solid var(--navy);
  outline-offset: 2px;
}

/* Constitution, Technology & Structure: honor prefers-reduced-motion. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ———————————————————————————————————————————————————————————————————
 * Language arts: the four interactions that are not a radio or a text box.
 *
 * They share the paper, the serif and the rule grid — a sentence to click is
 * still a question on a sheet, not a widget on a dashboard. What is new here
 * is that the answer is a thing on the page rather than a thing in a box, so
 * every one of them has to be operable by keyboard as well as by pointer.
 * ——————————————————————————————————————————————————————————————————— */

/* ——— the reading passage ——— */

/* A reading passage is several hundred words, and several hundred words on
 * ruled paper is where the grid costs the most. It takes the same wash as
 * every other text block (it had a heavier one, 94%, which hid the grid
 * entirely and made the passage a white card on a ruled sheet); the prose
 * wins by its own leading and measure, not by painting over the paper. */
.passage {
  margin: 0 0 1.75rem;
  padding: 1.1rem 1.3rem;
  font-size: 1.04rem;
  line-height: 1.75;
  background: var(--wash);
  /* A full hairline, not a colored spine on the left edge: that stripe is
   * banned (developer.md) and this block had a 4px one. */
  border: 1px solid var(--hair-hard);
  border-radius: 3px;
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 60%);
}

.passage p { margin: 0 0 0.9rem; }
.passage p:last-child { margin-bottom: 0; }

/* On the review page the passage is context for something already answered,
 * so it is smaller. Still not faded: it is prose, and prose has to be read. */
.passage.quiet { font-size: 0.94rem; line-height: 1.6; color: var(--graphite-2); }

/* ——— click the word ——— */

.sentence {
  margin: 0 0 1.5rem;
  padding: 0.85rem 1rem;
  font-size: clamp(1.15rem, 2.3vw, 1.4rem);
  font-weight: 500;
  line-height: 2.5;   /* room for the token padding without the lines colliding */
  background: var(--wash);
  border-radius: 5px;
}

/* A word is a button and reads like part of the sentence until it is hovered.
 * Underlined rather than boxed: a row of boxes stops looking like prose, and
 * the thing being read is a sentence. */
.token {
  display: inline;
  padding: 0.25rem 0.35rem;
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  border-bottom: 2px dashed color-mix(in srgb, var(--navy) 45%, transparent);
  border-radius: 3px;
  cursor: pointer;
}

.token.fixed {
  padding: 0;
  border: 0;
  cursor: default;
}

.token:hover { background: color-mix(in srgb, var(--navy) 8%, transparent); border-bottom-color: var(--navy); }

.token.picked {
  background: color-mix(in srgb, var(--navy) 12%, transparent);
  border-bottom: 2px solid var(--navy);
  font-weight: 600;
}

/* On the result sheet the same sentence comes back marked: green under the
 * word that was the answer, crimson through the one that was clicked in
 * error. A word that is both is simply right, and gets the green. */
.sentence.marked .token { cursor: default; }

.sentence.marked .token.was-picked {
  background: color-mix(in srgb, var(--miss) 10%, transparent);
  border-bottom: 2px solid var(--miss);
}

.sentence.marked .token.was-answer {
  background: rgb(20 107 51 / 14%);
  border-bottom: 2px solid var(--correct);
  color: var(--correct);
  font-weight: 700;
}

/* ——— put them in order ——— */

.tiles {
  margin: 0 0 1.25rem;
  padding: 0;
  list-style: none;
  counter-reset: none;
}

.tiles { position: relative; }

.tile {
  display: flex;
  gap: 0.75rem;
  align-items: center;
  margin-bottom: 0.5rem;
  padding: 0.75rem 0.85rem;
  background: var(--sheet);
  border: 1px solid var(--hair-hard);
  border-radius: 4px;
  cursor: grab;
  /* The browser must not take the gesture to scroll the page, or a drag on a
   * touch screen moves the document instead of the tile. */
  touch-action: none;
  user-select: none;
}

.tile:hover { border-color: var(--navy); }

/* Lifted out of the flow and following the pointer. `.placeholder` is holding
 * its place in the list, which is what makes the drop legible: the tiles part
 * to show the gap the line is going to fall into. */
.tile.dragging {
  position: absolute;
  left: 0;
  right: 0;
  z-index: 5;
  cursor: grabbing;
  border-color: var(--navy);
  box-shadow: 0 12px 28px rgb(32 35 30 / 22%);
  transform: scale(1.015);
}

.tile.placeholder {
  background: color-mix(in srgb, var(--navy) 6%, transparent);
  border: 2px dashed var(--navy);
  box-shadow: none;
}

/* Everything settles into the gap rather than jumping to it. */
.tiles.arranging .tile:not(.dragging) { transition: transform 0.12s var(--ease-settle); }

.tile-rank {
  flex: 0 0 1.7rem;
  height: 1.7rem;
  display: grid;
  place-items: center;
  font-family: var(--f-ui);
  font-size: 0.8rem;
  font-weight: 700;
  color: #fff;
  background: var(--navy);
  border-radius: 3px;
}

.tile-text { flex: 1 1 auto; line-height: 1.5; font-weight: 500; }

/* ——— the arrows are for the keyboard, and only the keyboard ———
 *
 * Dragging cannot be done from a keyboard or a screen reader, so these have to
 * exist. Beside every row, for everyone who is dragging, they are clutter. So
 * they take no space until a tile is focused: `visibility` rather than
 * `display`, because the width has to be reserved or the text reflows the
 * moment a tile is tabbed to. */
.tile-moves {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
  visibility: hidden;
}

.tile:focus-within .tile-moves,
.tile:focus .tile-moves { visibility: visible; }

.nudge {
  width: 1.75rem;
  height: 1.1rem;
  display: grid;
  place-items: center;
  font-size: 0.6rem;
  line-height: 1;
  color: var(--navy);
  background: color-mix(in srgb, var(--navy) 7%, transparent);
  border: 0;
  border-radius: 3px;
  cursor: pointer;
}

.nudge:hover { background: color-mix(in srgb, var(--navy) 16%, transparent); }

/* The correct order on the result sheet: the same tiles, nothing to grab. */
.tiles.shown { counter-reset: shown; }

.tile.still {
  cursor: default;
  background: color-mix(in srgb, var(--correct) 8%, transparent);
  border-color: color-mix(in srgb, var(--correct) 35%, transparent);
}

.tile.still::before {
  counter-increment: shown;
  content: counter(shown);
  flex: 0 0 1.6rem;
  height: 1.6rem;
  display: grid;
  place-items: center;
  font-family: var(--f-ui);
  font-size: 0.8rem;
  font-weight: 700;
  color: #fff;
  background: var(--correct);
  border-radius: 3px;
}

/* ——— sort into piles ——— */

.tray {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  min-height: 3.4rem;
  margin: 0 0 1.25rem;
  padding: 0.65rem;
  list-style: none;
  background: var(--wash);
  border: 2px dashed var(--hair-hard);
  border-radius: 5px;
}

.tray li, .bin-items li { display: block; }

.chip {
  padding: 0.5rem 0.9rem;
  font: inherit;
  font-size: 1.02rem;
  font-weight: 500;
  color: var(--graphite);
  background: var(--sheet);
  border: 1px solid var(--hair-hard);
  border-radius: 4px;
  cursor: pointer;
}

.chip:hover { border-color: var(--navy); }

.chip.held {
  color: #fff;
  background: var(--navy);
  border-color: var(--navy);
}

/* Columns, not space-between: the piles stay the same width whether they hold
 * one word or six, so nothing shifts sideways as words are placed. */
.bins {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 0.75rem;
  margin-bottom: 0.5rem;
}

.bin {
  padding: 0.75rem;
  background: var(--wash);
  border: 1px solid var(--hair-hard);
  border-radius: 5px;
  cursor: pointer;
}

.bin:hover { border-color: var(--navy); }

.bin-name {
  margin: 0 0 0.55rem;
  font-family: var(--f-ui);
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--navy);
}

.bin-items {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  min-height: 2.25rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Where each word belonged, on the result sheet. */
.pairs { margin: 0 0 1.25rem; padding: 0; list-style: none; }

.pairs li {
  display: flex;
  gap: 0.6rem;
  align-items: baseline;
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--hair);
}

.pair-item { font-weight: 600; }

.pair-bin {
  font-family: var(--f-ui);
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--correct);
}

/* The Check button sits in a row with its hint on all of these, and the hint
 * is not an error here — it is a running commentary on what is left to do. */
.selection .answer-row,
.arrange .answer-row,
.sort .answer-row {
  display: flex;
  gap: 1rem;
  align-items: center;
  flex-wrap: wrap;
}

.selection .answer-hint,
.arrange .answer-hint,
.sort .answer-hint {
  margin-top: 1.5rem;
  font-family: var(--f-ui);
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--graphite-2);
}

/* Nothing here styles `.check` when it is disabled. shell.css already does it,
 * deliberately: white ground, muted label, not-allowed cursor, so a control
 * that cannot act still says what it is. An `opacity: 0.4` added here stacked
 * on top of that and took the label to 40% of an already-muted grey, which
 * rendered as an empty white box. shell.css loads last and owns this name. */

/* The shell — masthead, footer, decks, the deck builder — lives in shell.css.
 * It used to be appended here, which meant the page chrome and the question
 * paper shared one file and one set of names. They are different surfaces with
 * different rules; keeping them apart is what stopped --pulp meaning two
 * things at once.
 */

/* ——— the acknowledgment a right answer gets ———
 *
 * Fixed to the corner rather than in the flow, because it must not move the
 * question that has just arrived. The run is the thing being protected here:
 * a right answer used to cost a screen and a click to be told nothing.
 */
.toasts {
  position: fixed;
  right: 1.25rem;
  bottom: 1.25rem;
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  pointer-events: none;
}

.toast {
  margin: 0;
  padding: 0.6rem 1.1rem 0.6rem 2.2rem;
  position: relative;
  font-family: var(--f-ui);
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  color: #fff;
  background: var(--correct);
  border-radius: 0.4rem;
  box-shadow: 0 10px 28px rgb(20 107 51 / 28%);
  animation: toast-in 0.18s var(--ease-settle) both;
}

/* The check is drawn rather than typed, so it cannot be read aloud as a word
 * by a screen reader that is already being given "Correct". */
.toast::before {
  content: "";
  position: absolute;
  left: 0.95rem;
  top: 50%;
  width: 0.42rem;
  height: 0.75rem;
  margin-top: -0.42rem;
  border: solid #fff;
  border-width: 0 2.5px 2.5px 0;
  transform: rotate(45deg);
}

.toast.going { animation: toast-out 0.35s var(--ease-settle) both; }

@keyframes toast-in {
  from { opacity: 0; transform: translateY(0.5rem); }
  to   { opacity: 1; transform: none; }
}

@keyframes toast-out {
  from { opacity: 1; }
  to   { opacity: 0; transform: translateY(-0.35rem); }
}

/* The label above a wrong answer's reason. An eyebrow rather than a lead-in,
   because the reasons come in two registers and only a label reads right for
   both. See practice/_result.html.erb. */
.misconception-label {
  display: block;
  margin-bottom: 0.15rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--miss);
}

/* A word or phrase the content quoted with backticks. Not monospace: these are
   words from a sentence, and a code font in the middle of a serif question
   reads as a different kind of thing than it is. */
.quoted {
  padding: 0.05em 0.28em;
  border-radius: 0.2rem;
  background: var(--navy-wash);
}
