/* ===========================================================================
 * share-report.css — the shareable report document + its print rules.
 * ===========================================================================
 * Paired with shared-share-report.jsx; read that file's header first. Three
 * things live here and nothing else does:
 *
 *  1. THE PAPER UNIT. `--shr-u` is one PDF POINT expressed in the units the
 *     current print engine lays out in. WebKit's viewPrintFormatter maps 1 CSS
 *     px → 1 PDF pt; Chrome prints at the 96dpi CSS reference, where 1pt is
 *     1.333px. Every size in this file is authored in POINTS as
 *     `calc(N * var(--shr-u))`, so an 7.5pt line is 7.5pt of paper on both.
 *     Never write a bare `pt` or `px` inside .shr-report.
 *
 *  2. COMPACT IS A STYLESHEET, NOT A RENDERER. The questions are the app's own
 *     ItemDispatch tree in review mode; compact reshapes it — inline choices,
 *     no card chrome, the `why` block dropped, figures capped — so there is
 *     exactly one place an item kind can be rendered and exactly one place it
 *     can be wrong. A choice list falls back to one-per-line whenever it
 *     carries figures or display math, because inline is unreadable there.
 *
 *  3. COLOUR IS NEVER THE MARK. A report gets printed, photocopied and read on
 *     a grey office laser. The key carries a ✓ and bold; a wrong pick carries
 *     a strike; the head line says the verdict in a word; the question grid
 *     puts a glyph beside every number. The app's own tints survive underneath
 *     as reinforcement only.
 *
 * `@page` margins are honoured by Chrome and IGNORED by the iOS print
 * formatter, which takes its margins from the plugin's perPageContentInsets —
 * so the number below and `MARGIN_PT` in shared-share-report.jsx must move
 * together.
 * ========================================================================= */

:root {
  /* Web / Chrome print: 1pt of paper. */
  --shr-u: 1pt;
}
:root[data-shr-ppp="1"] {
  /* WKWebView print: 1 CSS px IS 1 PDF pt, so a point of paper is 0.75pt of
     CSS (= 1px). Pre-dividing here is what keeps the authored sizes honest. */
  --shr-u: 0.75pt;
}

/* THE PASSAGE PANE SETS ITS OWN TYPE INLINE (PassagePane.jsx: `fontSize:
   t.passageSize || 16`), and an inline declaration cannot be overridden by a
   stylesheet without !important on every descendant — which would also flatten
   the pane's own hierarchy (heading, line numbers, blockquotes). `zoom` scales
   the whole subtree instead, inline sizes included, and it is honoured by both
   print engines. The ratios are (target pt × one CSS px per --shr-u) / 16px:
   the web lays out at 1pt = 1.333px and WKWebView at 1pt = 1px, so a single
   number cannot serve both. */
:root { --shr-pz-detailed: 0.79; --shr-pz-compact: 0.58; }
:root[data-shr-ppp="1"] { --shr-pz-detailed: 0.59; --shr-pz-compact: 0.44; }

/* --- The stage ----------------------------------------------------------- */
/* DETAILED renders in place. COMPACT renders off-page at column width and the
   packer clones it into .shr-pages — so what was measured is what prints. */
/* THE STUDENT'S READING-COMFORT ZOOM IS NOT THE PAPER'S, AND THE PACKER HAS TO
   MEASURE IN THE CONTEXT THAT PRINTS. `main.stage` carries the textScale
   `zoom` (index.html applyA11y, `--a11y-zoom`). Neutralising it only under
   `@media print` — which is what this file used to do — left the packer
   dealing blocks into columns laid out at 1.1 (or 1.35, or 1.6) and the sheet
   printing at 1, so THE PAGE COUNT DEPENDED ON THE VIEWER'S Aa SETTING AND
   SURFACE: the same SHSAT practice form measured 18 pages from a phone
   (zoom 1), 20 from a desktop (the 2026-09-18 default of 1.1) and 19 at 1.35.
   A printed document may not be a function of who is looking at it.
   So the neutralisation is hoisted here, where it applies ON SCREEN TOO the
   moment a report screen stamps `data-shr` — the preview is then literally the
   sheet, which is what "this is exactly what gets sent" claims.
   The two bits of CHROME on that screen are not the paper and opt back in
   below, so a student who needs larger type still gets it on the controls. */
:root[data-shr] main.stage { zoom: 1; }
:root[data-shr] .mode-headwrap,
:root[data-shr] .mode-header,
:root[data-shr] .shr-controls { zoom: var(--a11y-zoom, 1); }

.shr-stage { display: block; padding: 0 var(--gutter, 16px) 40px; }

/* --- THE PREVIEW ON SCREEN ------------------------------------------------
 * The sheet is 816px of paper and a phone column is 343, so the preview is the
 * sheet SCALED (PaperFit in shared-share-report.jsx sets the width and the
 * transform inline) and never the sheet cropped — it used to run off the right
 * edge with nothing to scroll, which made "this is exactly what gets sent"
 * false for everything past the fold.
 *
 * The paper's own margins are drawn here as the sheet's padding, and the sheet
 * carries a hairline and a shadow: on a cream app an unframed white block at
 * zero gutter reads as the screen going white, not as a page. NONE of it is on
 * paper — the print block below takes it all off again, where `@page` owns the
 * margins and the sheet is the sheet.
 *
 * The 28 is MARGIN_PT (shared-share-report.jsx); the two move together, the
 * same way the `@page` rule at the foot of this file restates it. */
.shr-fit { margin: 0 auto; max-width: var(--content-max, none); }
.shr-fit--on { overflow: hidden; }
.shr-fit-in { transform-origin: top left; }
/* DETAILED is one continuous sheet (the source IS the document); COMPACT is
   the packed page boxes. The source is dressed only when it is the thing on
   screen — while COMPACT packs, it is measured off-page in paper units and a
   padding on it would move every column measurement. */
.shr-fit--on .shr-src { box-sizing: border-box; padding: calc(28 * var(--shr-u)); }
.shr-fit--on .shr-page { padding: calc(28 * var(--shr-u)); }
.shr-fit--on .shr-src,
.shr-fit--on .shr-page {
  background: #fff;
  border: calc(0.75 * var(--shr-u)) solid #ded7c9;
  border-radius: calc(5 * var(--shr-u));
  box-shadow: 0 calc(1 * var(--shr-u)) calc(4 * var(--shr-u)) rgba(20, 17, 13, 0.10);
}

.shr-stage--packing .shr-src {
  position: absolute;
  left: -20000px;
  top: 0;
  /* width is set inline by the screen: the destination column's width. */
}
.shr-pages { display: block; }

/* A page box and its columns. Height is set inline (the printable box), so the
   forced break lands where the packer put it on every engine. */
.shr-page {
  display: block;
  margin: 0 auto calc(12 * var(--shr-u));
  background: #fff;
  /* VISIBLE, NOT HIDDEN, ON PURPOSE. `overflow: hidden` is what turned a
     measurement error into a silently clipped last line in the spike. If the
     packer is ever wrong again the overflow will be visible on the page and
     somebody will fix it. */
  overflow: visible;
}
/* The full-width band at the top of page one: the header and the summary, the
   part a parent reads first. The columns below it take the height that is
   left, which is what lets the body START on the summary sheet. */
.shr-band { display: block; }
.shr-cols {
  display: flex;
  align-items: flex-start;
  gap: 0 calc(12 * var(--shr-u));
  overflow: visible;
}
/* A block no column can hold (a long reading passage). Full width, and its
   height is its own: the print engine breaks it across sheets naturally, and
   the forced break after it returns the next packed page to a fresh sheet. */
.shr-page--wide { display: block; height: auto !important; }
.shr-col { overflow: visible; box-sizing: border-box; }
.shr-col--over { outline: 1px dashed #b3261e; }

/* --- The document -------------------------------------------------------- */
.shr-report {
  color: #14110d;
  background: #fff;
  font-family: Lexend, -apple-system, system-ui, sans-serif;
}
.shr-head {
  border-bottom: calc(1.2 * var(--shr-u)) solid #14110d;
  padding-bottom: calc(4 * var(--shr-u));
  margin-bottom: calc(9 * var(--shr-u));
}
.shr-title { font-size: calc(14 * var(--shr-u)); font-weight: 700; line-height: 1.25; }
.shr-sub { font-size: calc(8 * var(--shr-u)); color: #5f584a; margin-top: calc(2 * var(--shr-u)); }
.shr-foot {
  margin-top: calc(10 * var(--shr-u));
  padding-top: calc(4 * var(--shr-u));
  border-top: calc(0.5 * var(--shr-u)) solid #ded7c9;
  font-size: calc(7 * var(--shr-u));
  color: #6b6355;
}
/* WHERE A BOOKLET STARTS. It replaces the booklet's name on 114 question head
   lines, and it is what makes an unqualified "1." readable — every booklet on
   a real paper numbers its questions from 1. */
.shr-sectionhead {
  font-size: calc(8.5 * var(--shr-u));
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  border-bottom: calc(1 * var(--shr-u)) solid #14110d;
  padding-bottom: calc(2 * var(--shr-u));
  margin: 0 0 calc(5 * var(--shr-u));
}
/* A SECTION HEADING NEEDS AIR ABOVE IT, and only above: it is glued to what
   follows (the packer moves the pair together), so the space is what separates
   it from the question it is NOT part of — without it the heading sat directly
   under the previous question's last choice and read as a fifth option. None
   at the head of a column, where the page edge is the separator. */
.shr-col > .shr-sectionhead:not(:first-child),
.shr-body > .shr-sectionhead:not(:first-child) {
  margin-top: calc(11 * var(--shr-u));
}
.shr-report--compact .shr-sectionhead {
  font-size: calc(7.5 * var(--shr-u));
  margin-bottom: calc(3.5 * var(--shr-u));
}
/* A passage the packer dealt across columns: the continuation repeats the
   passage's rule so the run reads as one thing, and drops the top margin so it
   starts flush with the column. */
.shr-passage.shr-cont { margin-top: 0; }

.shr-blocktitle {
  font-size: calc(9 * var(--shr-u)); font-weight: 700;
  margin: calc(9 * var(--shr-u)) 0 calc(4 * var(--shr-u));
}

/* ---- THE SUMMARY BAND, TIGHTENED FOR COMPACT ----------------------------
   In COMPACT the summary shares page one with the start of the body (the
   packer's band, see pack()), so every point it gives back is two points of
   column under it — and on a 114-question paper a column is eight questions.
   Nothing is dropped, only set at the density the rest of that page is set
   at. DETAILED is untouched: there the summary has the sheet to itself. */
.shr-report--compact .shr-head { padding-bottom: calc(3 * var(--shr-u)); margin-bottom: calc(5 * var(--shr-u)); }
.shr-report--compact .shr-title { font-size: calc(12 * var(--shr-u)); }
.shr-report--compact .shr-blocktitle { margin: calc(5 * var(--shr-u)) 0 calc(2.5 * var(--shr-u)); font-size: calc(8 * var(--shr-u)); }
.shr-report--compact .shr-stats { gap: calc(8 * var(--shr-u)); margin-bottom: calc(5 * var(--shr-u)); }
.shr-report--compact .shr-stat-v { font-size: calc(13 * var(--shr-u)); }
.shr-report--compact .shr-table { margin-bottom: calc(5 * var(--shr-u)); font-size: calc(8 * var(--shr-u)); }
.shr-report--compact .shr-table th,
.shr-report--compact .shr-table td { padding: calc(1.5 * var(--shr-u)) calc(3 * var(--shr-u)); }
.shr-report--compact .shr-note { padding: calc(3 * var(--shr-u)) calc(5 * var(--shr-u)); margin-bottom: calc(4 * var(--shr-u)); }
.shr-report--compact .shr-gridwrap { margin-bottom: calc(5 * var(--shr-u)); }
.shr-report--compact .shr-gridgroup { margin-bottom: calc(3.5 * var(--shr-u)); }
.shr-report--compact .shr-grid { gap: calc(1.5 * var(--shr-u)); }
.shr-report--compact .shr-cell {
  min-width: calc(14 * var(--shr-u));
  padding: calc(0.5 * var(--shr-u)) calc(2 * var(--shr-u));
  font-size: calc(6.5 * var(--shr-u));
}
.shr-report--compact .shr-topic { line-height: 1.35; padding: calc(0.75 * var(--shr-u)) 0; font-size: calc(7.5 * var(--shr-u)); }
.shr-report--compact .shr-foot { margin-top: calc(5 * var(--shr-u)); padding-top: calc(3 * var(--shr-u)); }

/* Summary blocks — one column, always, whatever the body layout is. */
.shr-stats { display: flex; flex-wrap: wrap; gap: calc(10 * var(--shr-u)); margin-bottom: calc(8 * var(--shr-u)); }
.shr-stat { min-width: calc(52 * var(--shr-u)); }
.shr-stat-v { font-size: calc(16 * var(--shr-u)); font-weight: 700; line-height: 1.1; }
.shr-stat-l { font-size: calc(7 * var(--shr-u)); color: #5f584a; text-transform: uppercase; letter-spacing: .04em; }

.shr-table {
  width: 100%; border-collapse: collapse;
  font-size: calc(8.5 * var(--shr-u)); margin-bottom: calc(8 * var(--shr-u));
}
.shr-table th, .shr-table td {
  border-bottom: calc(0.5 * var(--shr-u)) solid #ded7c9;
  padding: calc(3 * var(--shr-u)) calc(4 * var(--shr-u));
  text-align: right;
}
.shr-table th:first-child, .shr-table td:first-child { text-align: left; }
.shr-table th { font-weight: 700; color: #5f584a; font-size: calc(7.5 * var(--shr-u)); }

.shr-gridwrap { margin-bottom: calc(8 * var(--shr-u)); }
.shr-gridgroup { margin-bottom: calc(6 * var(--shr-u)); }
.shr-gridlabel {
  font-size: calc(7 * var(--shr-u)); font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: #5f584a; margin-bottom: calc(3 * var(--shr-u));
}
.shr-grid { display: flex; flex-wrap: wrap; gap: calc(2.5 * var(--shr-u)); }
.shr-cell {
  display: inline-flex; align-items: baseline; gap: calc(1 * var(--shr-u));
  min-width: calc(17 * var(--shr-u));
  padding: calc(1.5 * var(--shr-u)) calc(2.5 * var(--shr-u));
  border: calc(0.5 * var(--shr-u)) solid #ded7c9;
  border-radius: calc(2 * var(--shr-u));
  font-size: calc(7 * var(--shr-u)); line-height: 1.1;
}
.shr-cell-n { font-weight: 600; }
.shr-cell-m { font-weight: 700; }
/* THE MAP ECHOES THE QUESTIONS' OWN VOCABULARY. Downstairs a miss wears a
   heavy SOLID bar and a blank a DASHED one; up here the cell's border says the
   same thing, so a parent who reads the map and then a page of questions is
   reading one language. Border, not fill, for the same reason as the bars —
   it survives a printer with background graphics off. The glyph (✓ ✗ ·) is
   still what carries the state; this only makes the misses findable. */
.shr-cell.is-ok { background: #eef2e8; }
.shr-cell.is-miss {
  background: #f7ecea; font-weight: 700;
  border-color: #14110d; border-width: calc(1 * var(--shr-u));
}
.shr-cell.is-blank {
  background: #fff; color: #3c352b;
  border-style: dashed; border-color: #14110d;
}
/* A withdrawn or annulled number is PRINTED and in no total — the strike is
   the whole statement, the same one full-test.jsx makes on screen. */
.shr-cell.is-dead, .shr-cell.is-annulled {
  background: #f2efe8; color: #6b6355; text-decoration: line-through;
}
.shr-gridkey { font-size: calc(6.5 * var(--shr-u)); color: #6b6355; margin-top: calc(2 * var(--shr-u)); }

.shr-topics { margin-bottom: calc(6 * var(--shr-u)); }
.shr-topic {
  display: flex; align-items: baseline; gap: calc(5 * var(--shr-u));
  font-size: calc(8 * var(--shr-u)); line-height: 1.5;
  border-bottom: calc(0.5 * var(--shr-u)) solid #f0ebe0;
  padding: calc(1.5 * var(--shr-u)) 0;
}
.shr-topic-n { flex: 1; min-width: 0; }
.shr-topic-s { color: #6b6355; font-size: calc(7 * var(--shr-u)); }
.shr-topic-c { color: #5f584a; white-space: nowrap; font-variant-numeric: tabular-nums; }

.shr-compare { margin-bottom: calc(8 * var(--shr-u)); }
.shr-cmp-line { font-size: calc(8 * var(--shr-u)); line-height: 1.5; }

.shr-note {
  font-size: calc(8 * var(--shr-u)); line-height: 1.5; color: #3c352b;
  background: #f5f1e8;
  padding: calc(4 * var(--shr-u)) calc(6 * var(--shr-u));
  border-radius: calc(2 * var(--shr-u));
  margin-bottom: calc(6 * var(--shr-u));
}
.shr-note.is-strong { font-weight: 700; color: #14110d; }

/* --- A question, AND ITS MARKS -------------------------------------------
 * Two complaints, one shape. (1) A missed question did not pop: the verdict
 * was a word in a grey head line, the same weight as the topic beside it.
 * (2) Nothing said where one question ended and the next began: a bottom rule
 * sat equidistant between the two, so it read as belonging to neither.
 *
 * THE GUTTER IS THE SEPARATOR. Every number hangs in a fixed left gutter at
 * the same x, larger and bolder than the body, with the stem and the choices
 * indented past it — so a column of thirty questions has one hard vertical
 * edge to scan. The rule moved from the FOOT of a block to its HEAD, with
 * clearly more space above it than below (`margin-top` > `padding-top`), which
 * is what makes it read as the lid of the question under it. A block that is
 * first in a column, or that follows a passage or a section heading, drops the
 * rule: those carry their own, stronger separator, and a passage's questions
 * should read as hanging off it.
 *
 * THE THREE STATES ARE GEOMETRY, NOT HUE:
 *   missed  — heavy SOLID bar down the left edge, full height + FILLED number
 *             box (ink ground, white digits) + a very light warm tint.
 *   blank   — the same geometry, DASHED bar + HOLLOW number box, no tint.
 *   correct — plain bold number, no bar, no box, no tint. Quiet on purpose:
 *             the misses pop by contrast, and a page of thirty questions where
 *             every one is marked is a page where none of them is.
 * BOTH BARS ARE `border-left`, NOT A BACKGROUND. A browser printing with
 * background graphics off (the default in Chrome's dialog) drops backgrounds
 * and keeps borders, so the bar, the hollow box's outline and the strike all
 * survive; the warm tint is the ONE channel allowed to vanish, and it carries
 * nothing the bar and the box do not already say. The tint and the filled box
 * ask for `print-color-adjust: exact` (set on the whole report under @media
 * print), which is what makes them survive that setting where they can.
 * The bar lives INSIDE the measured block — an absolutely positioned
 * pseudo-element on a `position: relative` box — so the packer's `scrollHeight`
 * read and `break-inside: avoid` are untouched by it. */
.shr-q {
  position: relative;
  padding-left: var(--shr-qgut);
  margin: calc(11 * var(--shr-u)) 0 0;
  border-top: calc(0.5 * var(--shr-u)) solid #ded7c9;
  padding-top: calc(3.5 * var(--shr-u));
  padding-bottom: calc(1 * var(--shr-u));
}
/* No lid on the first block of a column (compact) or of the body (detailed),
   and none where a stronger separator already sits directly above. */
.shr-col > .shr-q:first-child,
.shr-body > .shr-q:first-child,
.shr-sectionhead + .shr-q,
.shr-passage + .shr-q {
  border-top: none;
  margin-top: calc(2 * var(--shr-u));
  padding-top: 0;
}
/* The bar. `left: 0` is the padding box, i.e. under the rule, never through
   it; `bottom: 0` takes the block's full height whatever the question holds. */
.shr-q::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 0;
}
.shr-q--miss::before { border-left: calc(2.8 * var(--shr-u)) solid #14110d; }
.shr-q--blank::before { border-left: calc(2.5 * var(--shr-u)) dashed #14110d; }
.shr-q--miss {
  background: #fbf4ec;
  -webkit-print-color-adjust: exact; print-color-adjust: exact;
}
/* …and it is the ONLY fill on the page. See "PAPER CARRIES NO STRUCTURAL
   FILLS" near the foot of this file — the missed NUMBER BOX that used to sit
   beside it was a filled ground too, and a filled ground is exactly what a
   printer with background graphics off throws away. */

.shr-qhead {
  display: flex; flex-wrap: nowrap; align-items: baseline;
  gap: 0 calc(4 * var(--shr-u));
  font-size: calc(7.5 * var(--shr-u)); color: #5f584a;
  margin-bottom: calc(2 * var(--shr-u));
}
/* HANGING. The negative margin pulls the number back out into the gutter the
   block's padding reserved, so every number in the column starts at the same
   x whether it is a bare digit, a filled box or a hollow one. */
.shr-qn {
  flex: 0 0 auto;
  /* …less the bar's own width plus a hair, so a filled box sits BESIDE the
     bar rather than on top of it. Every state pays the same offset, which is
     what keeps the numbers on one x. */
  margin-left: calc(4 * var(--shr-u) - var(--shr-qgut));
  min-width: calc(var(--shr-qgut) - 8 * var(--shr-u));
  font-weight: 700; color: #14110d;
  font-size: calc(9 * var(--shr-u)); line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.shr-q--miss > .shr-qhead > .shr-qn,
.shr-q--blank > .shr-qhead > .shr-qn {
  display: inline-block; text-align: center;
  padding: calc(0.5 * var(--shr-u)) calc(1.5 * var(--shr-u));
  border-radius: calc(1.5 * var(--shr-u));
}
/* A MISSED NUMBER IS INK, NOT A GROUND. It used to be white digits on a solid
   ink box, which is a filled ground wearing a different name: printed with
   background graphics OFF (Chrome's default in the dialog, and any printer set
   to save toner) the fill goes and the digits go with it — a missed question's
   number was INVISIBLE on the sheet, which is the one number on the page that
   has to be findable. It is now a HEAVY SOLID outline with the digits in ink,
   against the blank's lighter DASHED one: the same solid-vs-dashed vocabulary
   the left bar and the summary grid already speak, and nothing on it depends
   on a background surviving. */
.shr-q--miss > .shr-qhead > .shr-qn {
  border: calc(1.5 * var(--shr-u)) solid #14110d;
  padding: calc(0.25 * var(--shr-u)) calc(1.25 * var(--shr-u));
  color: #14110d;
}
.shr-q--blank > .shr-qhead > .shr-qn {
  border: calc(0.8 * var(--shr-u)) dashed #14110d;
  padding: calc(0.45 * var(--shr-u)) calc(1.45 * var(--shr-u));
}
.shr-qn--struck { text-decoration: line-through; color: #6b6355; }
.shr-qtag {
  margin-left: auto; text-align: right; flex: 0 1 auto;
  white-space: normal;
}
.shr-qv { font-weight: 700; text-transform: uppercase; letter-spacing: .05em; }
.shr-q--miss .shr-qv, .shr-q--blank .shr-qv { color: #14110d; }
.shr-qpick, .shr-qkey { font-weight: 600; }
.shr-qmeta { color: #6b6355; flex: 0 1 auto; min-width: 0; }
.shr-q--dead { color: #6b6355; }

.shr-grid-entry {
  font-size: calc(8 * var(--shr-u)); background: #f5f1e8;
  padding: calc(3 * var(--shr-u)) calc(5 * var(--shr-u));
  border-radius: calc(2 * var(--shr-u));
  margin: calc(3 * var(--shr-u)) 0;
}
.shr-grid-key { font-size: calc(8 * var(--shr-u)); font-weight: 600; }

/* --- Passage ------------------------------------------------------------- */
.shr-passage {
  margin: calc(6 * var(--shr-u)) 0 calc(5 * var(--shr-u));
  padding-left: calc(5 * var(--shr-u));
  border-left: calc(1.5 * var(--shr-u)) solid #14110d;
}
.shr-passage-title { font-weight: 700; font-size: calc(8.5 * var(--shr-u)); }
.shr-report--detailed .shr-passage-body { zoom: var(--shr-pz-detailed); }
.shr-report--compact .shr-passage-body { zoom: var(--shr-pz-compact); }
/* The stub: a passage whose questions all went right. One line, so the run of
   answers below it still has its heading. */
.shr-passage--stub {
  font-size: calc(7.5 * var(--shr-u)); color: #5f584a;
  border-left-width: calc(0.8 * var(--shr-u));
}
.shr-passage--stub .shr-passage-title { display: inline; font-size: inherit; color: #14110d; }
.shr-passage-first { font-style: italic; }

/* =========================================================================
 * THE QUESTION TREE — reshaping ItemDispatch's own output for paper.
 * =========================================================================
 * THREE PREFIXES, ENUMERATED ON PURPOSE. The app has three choice renderers
 * with the same markup shape under different names — `mc-` (McItem: singles,
 * SHSAT Editing A, LGS generic MCQ), `pi-` (PassageItem: reading, ACT
 * English/Science) and `pei-` (PassageEditItem: SHSAT Editing B). A rule
 * written for `mc-` alone silently misses two thirds of the corpus, which is
 * exactly what happened first: reading choices printed as live tap targets
 * with a coloured fill and no glyph. Any new rule here goes on all three.
 * (`mc-why-block` is NOT one of them — all three renderers already share it.)
 *
 * …AND SINCE 2026-09-19 THERE IS A FOURTH, plus a name that covers all of
 * them. `an-` (AnalogyItem) was never enumerated here, so an SSAT analogy
 * printed as exactly the raised, shadowed tap target this block exists to
 * undo. Package B's S-04 refactor made all four renderers emit a SHARED row —
 * `mcq-choices / mcq-choice-row / mcq-choice / mcq-letter / mcq-choice-main /
 * mcq-choice-text / mcq-elim-spacer` — beside their own names, so every rule
 * below that has an `mcq-` equivalent now names it and reaches the fourth for
 * free. The per-component names stay: the three suffixes with no shared twin
 * (`-choice-verdict`, `-verdict-adorn`, `-actions`) still have to be spelt out.
 *
 * SSAT-07: the COMPACT and WORKSHEET variants had none of the `mcq-` twins at
 * all — only the base and DETAILED rules had been updated — so an analogy on a
 * printed worksheet got no choice treatment at all and came out as the
 * on-screen tap target. Every enumerated rule in all three variants now names
 * the shared primitive, and `an-eyebrow` (which has no twin) is spelt out
 * beside its three siblings.
 *
 * …and this comment used to CLOSE right here, on the line above, with a stray
 * comment terminator. (It cannot be written out here — a literal one inside a
 * comment closes it, which is the very bug.) Everything after it — six lines
 * starting with a bare `*` — then parsed
 * as a selector prelude that ran on until the next `{`, which swallowed and
 * dropped the whole `.mc-choice / .pi-choice / .pei-choice / .mcq-choice`
 * rule below. That rule is the one that strips the fill, border, radius,
 * padding and shadow off a choice row for paper, so EVERY printed choice on
 * every report was rendering as a raised tap target, not just the analogy.
 *
 * Nothing below is about BEHAVIOUR: the tree is rendered inside <Inert on>.
 * It is about a raised, shadowed, rounded tap target being the wrong object on
 * a printed page.
 * ========================================================================= */
.shr-qbody .mc-choice,
.shr-qbody .pi-choice,
.shr-qbody .pei-choice,
.shr-qbody .mcq-choice {
  box-shadow: none !important;
  background: transparent !important;
  border: none !important;
  padding: 0 !important;
  /* A SHEET HAS NO TAP TARGETS. `mc-item.css` floors a short-choice row at
     44px on tablet/desktop so the 2-up grid's tighter padding does not lose
     the touch minimum — right on a screen, and on paper it added 25pt to each
     of four rows on every short-choice maths question. THE PAGE COUNT WAS
     THEREFORE A FUNCTION OF THE VIEWER'S SURFACE: the same SHSAT practice form
     packed to 18 pages from a phone and 20 from a desktop. Measured, and the
     reason this line is `min-height` rather than a surface override: the
     report already neutralises the fill, border, padding and shadow of the
     same object for the same reason, and the floor was the one tap-target
     property missing from the list. */
  min-height: 0 !important;
  cursor: default;
}
/* THE ELIMINATOR IS A CONTROL, NOT A MARK (iOS study 2026-09-21, ISEE-14).
   Only the SPACER was listed here, so on an ISEE/SSAT sheet every choice
   printed its grey circled × in a bordered cell beside the answer bubble — a
   dead control on paper, and one a student can easily mark instead of the
   ring. The four host aliases plus the shared primitive, because a renderer
   emits both names. */
.shr-qbody .mc-elim,
.shr-qbody .pi-elim,
.shr-qbody .pei-elim,
.shr-qbody .an-elim,
.shr-qbody .mcq-elim,
.shr-qbody .mc-elim-spacer,
.shr-qbody .mc-choice-verdict,
.shr-qbody .pi-elim-spacer,
.shr-qbody .pi-choice-verdict,
.shr-qbody .pei-elim-spacer,
.shr-qbody .mcq-elim-spacer,
.shr-qbody .pei-choice-verdict,
.shr-qbody .mc-actions,
.shr-qbody .pi-actions,
.shr-qbody .item-feedback,
.shr-qbody .flag-btn,
.shr-qbody .scratchpad-btn { display: none !important; }
.shr-qbody .mc-letter,
.shr-qbody .pi-letter,
.shr-qbody .pei-letter,
.shr-qbody .mcq-letter {
  /* The letter is a fixed 22–28px round CHIP on screen (a tap affordance in
     its own right). On paper it is just the letter: no box, no fixed size, no
     line box of its own — that last one is what pushed every option in an
     inline list onto a line by itself. */
  background: transparent !important; color: inherit !important;
  display: inline !important;
  width: auto !important; min-width: 0 !important;
  height: auto !important; line-height: inherit !important;
  font-weight: 700;
  padding: 0 !important; border-radius: 0 !important; box-shadow: none !important;
}

/* THE KEY AND THE WRONG PICK, WITHOUT COLOUR.
   The GLYPH is the app's own: `revealAdornment` (runner.jsx) already puts a ✓
   in the right-edge slot of the key and a ✗ in the student's wrong pick, in
   every renderer, and it is the mark a student saw on screen. Nothing here
   invents a second one — an earlier draft added a ::before ✓ and printed two
   ticks per correct row, one of them on a line of its own. What this adds is
   the two channels a printed page needs on top of it: WEIGHT on the key and a
   STRIKE through a wrong pick, so the row survives a grey photocopy. */
.shr-qbody .mc-verdict-adorn,
.shr-qbody .pi-verdict-adorn,
.shr-qbody .pei-verdict-adorn {
  font-weight: 700; color: inherit !important;
}
.shr-qbody .mc-choice.is-correct,
.shr-qbody .pi-choice.is-correct,
.shr-qbody .pei-choice.is-correct,
.shr-qbody .mcq-choice.is-correct { font-weight: 700; }
.shr-qbody .mc-choice.is-wrong .mc-choice-text,
.shr-qbody .pi-choice.is-wrong .pi-choice-text,
.shr-qbody .pei-choice.is-wrong .pei-choice-text,
.shr-qbody .mcq-choice.is-wrong .mcq-choice-text {
  text-decoration: line-through;
  text-decoration-thickness: calc(0.6 * var(--shr-u));
}
/* The dim on the also-rans is an ink pair on screen; on paper it is just grey
   text next to black, which is all the de-emphasis a printed list needs. */
.shr-qbody .mc-choice.is-dimmed,
.shr-qbody .pi-choice.is-dimmed,
.shr-qbody .pei-choice.is-dimmed,
.shr-qbody .mcq-choice.is-dimmed { color: #4a4438 !important; }

/* --- DETAILED ------------------------------------------------------------ */
.shr-report--detailed { font-size: calc(10 * var(--shr-u)); line-height: 1.4;
  /* ~2em of the number's own size: the gutter the numbers hang in. */
  --shr-qgut: calc(18 * var(--shr-u)); }
.shr-report--detailed .shr-qbody { font-size: calc(10 * var(--shr-u)); }
.shr-report--detailed .shr-qbody .mc-choices,
.shr-report--detailed .shr-qbody .pi-choices,
.shr-report--detailed .shr-qbody .pei-choices,
.shr-report--detailed .shr-qbody .mcq-choices { display: block; }
.shr-report--detailed .shr-qbody .mc-choice-row,
.shr-report--detailed .shr-qbody .pi-choice-row,
.shr-report--detailed .shr-qbody .pei-choice-row,
.shr-report--detailed .shr-qbody .mcq-choice-row { display: block; margin: calc(1.5 * var(--shr-u)) 0; }
/* ONE TEXT LINE PER CHOICE — "A) the answer ✓" — with a hanging indent so
   a wrapped choice lines up under its own text rather than under its letter.
   NOT a grid: the letter has to be `display: inline` (it is a fixed-size round
   chip otherwise), an inline grid item has no baseline for the text column to
   align to, and the choice text printed a line above its own letter, reading
   as a superscript. One line box aligns them by construction, and it is the
   same shape COMPACT uses — two layouts, one choice grammar. */
.shr-report--detailed .shr-qbody .mc-choice,
.shr-report--detailed .shr-qbody .pi-choice,
.shr-report--detailed .shr-qbody .pei-choice,
.shr-report--detailed .shr-qbody .mcq-choice {
  display: block;
  font-size: calc(9.5 * var(--shr-u)); line-height: 1.35;
  padding-left: calc(11 * var(--shr-u)) !important;
  text-indent: calc(-11 * var(--shr-u));
}
.shr-report--detailed .shr-qbody .mc-choice-main,
.shr-report--detailed .shr-qbody .pi-choice-main,
.shr-report--detailed .shr-qbody .pei-choice-main,
.shr-report--detailed .shr-qbody .mcq-choice-main { display: inline; }
.shr-report--detailed .shr-qbody .mc-choice-text,
.shr-report--detailed .shr-qbody .pi-choice-text,
.shr-report--detailed .shr-qbody .pei-choice-text,
.shr-report--detailed .shr-qbody .mcq-choice-text { display: inline; }
.shr-report--detailed .shr-qbody .mc-choice-text .md-p,
.shr-report--detailed .shr-qbody .pi-choice-text .md-p,
.shr-report--detailed .shr-qbody .pei-choice-text .md-p { display: inline; margin: 0; }
.shr-report--detailed .shr-qbody .mcq-choice-text .md-p { display: inline; margin: 0; }
.shr-report--detailed .shr-qbody .mc-letter::after,
.shr-report--detailed .shr-qbody .pi-letter::after,
.shr-report--detailed .shr-qbody .pei-letter::after,
.shr-report--detailed .shr-qbody .mcq-letter::after { content: ")\00a0"; }
.shr-report--detailed .shr-qbody .mc-verdict-adorn,
.shr-report--detailed .shr-qbody .pi-verdict-adorn,
.shr-report--detailed .shr-qbody .pei-verdict-adorn { display: inline; margin-left: calc(3 * var(--shr-u)); }
/* THE EXPLANATION PANEL ON PAPER — a left rule, not a card.
 *
 * Three things were wrong with the card, and they are one thing: the screen's
 * panel is a 14PX box and the report resets its padding to POINTS, so every
 * measurement inside it that is authored in px is suddenly out of scale.
 *   - `.mc-why-verdict` bleeds `-14px` to the panel's edges so the verdict
 *     reads as a band. Against a 4pt/6pt report padding that is ~9px of bleed
 *     in every direction: the band sat 9px ABOVE the panel, on top of the last
 *     choice's descenders, and 9px WIDER than it on each side, so it and the
 *     "HERE'S WHY" panel under it started at two different left edges.
 *   - and the 3pt top margin left no air between the choices and the panel.
 * So on paper the panel is not a box at all: a hairline LEFT RULE with the
 * lesson indented past it, a real gap above, and the verdict a bold ink line
 * under a hairline of its own. Both edges are then the question's own text
 * column by construction, because nothing bleeds anywhere. */
.shr-report--detailed .shr-qbody .mc-why-block {
  font-size: calc(8.5 * var(--shr-u)); line-height: 1.45; color: #3c352b;
  background: none !important;
  border: none;
  border-left: calc(0.8 * var(--shr-u)) solid #bdb5a4;
  border-radius: 0;
  padding: 0 0 0 calc(6 * var(--shr-u));
  /* The gap the banner was missing. More than the choice rows' own leading, so
     the panel reads as answering the question rather than as its last option. */
  margin-top: calc(9 * var(--shr-u));
}
.shr-report--detailed .shr-qbody .mc-why {
  font-size: calc(8.5 * var(--shr-u)); line-height: 1.45; color: #3c352b;
  background: none !important; border: none; padding: 0; border-radius: 0;
  margin-top: calc(2 * var(--shr-u));
}
.shr-report--detailed .shr-qbody .figure-card { max-width: calc(300 * var(--shr-u)); }

/* --- COMPACT ------------------------------------------------------------- */
/* ~7.5pt, choices inline, no explanation — the "a whole paper on a few sheets"
   shape. The `why` is dropped rather than shrunk: at this size it would be the
   longest thing on the page and the least readable. */
.shr-report--compact { font-size: calc(7.5 * var(--shr-u)); line-height: 1.22;
  /* ~1.6em of the number's own size. Tighter than detailed because a compact
     column is a third of a sheet — but still a real gutter, which is what the
     left edge of the block is measured from. */
  --shr-qgut: calc(12 * var(--shr-u)); }
.shr-report--compact .shr-q {
  margin-top: calc(5.5 * var(--shr-u));
  padding-top: calc(2 * var(--shr-u));
  padding-bottom: calc(0.5 * var(--shr-u));
}
.shr-report--compact .shr-col > .shr-q:first-child,
.shr-report--compact .shr-sectionhead + .shr-q,
.shr-report--compact .shr-passage + .shr-q {
  margin-top: calc(1 * var(--shr-u));
  padding-top: 0;
}
.shr-report--compact .shr-q--miss::before { border-left-width: calc(2.5 * var(--shr-u)); }
.shr-report--compact .shr-q--blank::before { border-left-width: calc(2.2 * var(--shr-u)); }
.shr-report--compact .shr-qhead {
  font-size: calc(6.5 * var(--shr-u));
  gap: 0 calc(3 * var(--shr-u));
  margin-bottom: calc(0.5 * var(--shr-u));
}
.shr-report--compact .shr-qn { font-size: calc(7.5 * var(--shr-u)); }
.shr-report--compact .shr-qbody { font-size: calc(7.5 * var(--shr-u)); }
/* The renderers' own topic chip. It is the same fact the head line prints in
   DETAILED, and at three columns it is a coloured pill eating a line per
   question. */
.shr-report--compact .shr-qbody .mc-eyebrow,
.shr-report--compact .shr-qbody .pi-eyebrow,
.shr-report--compact .shr-qbody .an-eyebrow,
.shr-report--compact .shr-qbody .pei-eyebrow { display: none !important; }
.shr-report--compact .shr-qbody .mc-why-block,
.shr-report--compact .shr-qbody .mc-why,
.shr-report--compact .shr-qbody .mc-why-blocks,
.shr-report--compact .shr-qbody .mc-insight { display: none !important; }
.shr-report--compact .shr-qbody .mc-stem,
.shr-report--compact .shr-qbody .mc-quote,
.shr-report--compact .shr-qbody .pi-stem,
.shr-report--compact .shr-qbody .pei-stem,
.shr-report--compact .shr-qbody .pei-stem-p,
.shr-report--compact .shr-qbody .pei-passage-strip { margin-bottom: calc(1 * var(--shr-u)); }
.shr-report--compact .shr-qbody .md-p { margin-bottom: calc(1 * var(--shr-u)); }

/* THREE CHOICE SHAPES, ONE GRAMMAR, PICKED BY MEASUREMENT.
   The DEFAULT is one per line with a hanging indent — identical in shape to
   DETAILED, so the two layouts differ in size and not in how a choice list is
   read. `fitChoices()` (shared-share-report.jsx) then measures the rendered
   list at the real column width and promotes it:
     `.shr-ch-inline` — all of them fit one line: "A) 5 B) 6 C) 7 D) 14".
     `.shr-ch-2up`    — they fit two per line.
   It measures rather than counting characters because the bar is the COLUMN,
   which differs between Letter and A4, two columns and three, and the web's
   1pt=1.333px against WKWebView's 1pt=1px.

   THE HANGING INDENT IS THE POINT OF THE DEFAULT. Before it, a wrapped choice
   continued hard against the column edge, under its own letter, so the eye
   could not tell a wrap from the next option. `text-indent` negative +
   `padding-left` gives it on ONE line box, which is what the letter needs (it
   is `display: inline` — a grid cell has no baseline for the text to sit on,
   and the text printed a line above its own letter). */
.shr-report--compact .shr-qbody .mc-choices,
.shr-report--compact .shr-qbody .pi-choices,
.shr-report--compact .shr-qbody .pei-choices { display: block; }
.shr-report--compact .shr-qbody .mcq-choices { display: block; }
.shr-report--compact .shr-qbody .mc-choice-row,
.shr-report--compact .shr-qbody .pi-choice-row,
.shr-report--compact .shr-qbody .mcq-choice-row,
.shr-report--compact .shr-qbody .pei-choice-row {
  display: block; margin: 0;
}
.shr-report--compact .shr-qbody .mc-choice,
.shr-report--compact .shr-qbody .pi-choice,
.shr-report--compact .shr-qbody .mcq-choice,
.shr-report--compact .shr-qbody .pei-choice {
  display: block; font-size: calc(7.5 * var(--shr-u)); line-height: 1.24;
  padding-left: calc(10 * var(--shr-u)) !important;
  text-indent: calc(-10 * var(--shr-u));
}
/* THE MEASURING STATE. Held for the length of one synchronous read in
   fitChoices() and never painted: each option becomes a nowrap inline-block,
   so its box reports the width it WANTS rather than the width the column gave
   it. Nothing below may be inherited into the printed page — the class is
   always removed in the same statement run that added it. */
.shr-report--compact .shr-qbody .shr-ch-probe {
  display: block !important;
  white-space: nowrap !important;
}
.shr-report--compact .shr-qbody .shr-ch-probe > * {
  display: inline-block !important;
  width: auto !important; max-width: none !important;
  margin: 0 !important; text-indent: 0 !important; padding-left: 0 !important;
}
.shr-report--compact .shr-qbody .shr-ch-probe > * > * {
  display: inline !important;
  padding-left: 0 !important; text-indent: 0 !important;
}
/* A choice that is ONLY a formula renders as `.katex-display` — a centred
   block with a 1em margin above and below. In a promoted list that block ends
   the line, which is the whole thing the promotion was for; in the probe its
   margins are height the option does not really have. It is neutralised in
   all three, and a formula that genuinely needs the height is kept out of the
   promotion by the height test in fitChoices() instead. */
.shr-report--compact .shr-qbody .shr-ch-probe .katex-display,
.shr-report--compact .shr-qbody .shr-ch-inline .katex-display,
.shr-report--compact .shr-qbody .shr-ch-2up .katex-display {
  display: inline !important; margin: 0 !important; text-align: left !important;
}

/* All of them on one line. */
.shr-report--compact .shr-qbody .mc-choices.shr-ch-inline .mc-choice-row,
.shr-report--compact .shr-qbody .pi-choices.shr-ch-inline .pi-choice-row,
.shr-report--compact .shr-qbody .mcq-choices.shr-ch-inline .mcq-choice-row,
.shr-report--compact .shr-qbody .pei-choices.shr-ch-inline .pei-choice-row {
  display: inline; margin: 0 calc(5 * var(--shr-u)) 0 0;
}
.shr-report--compact .shr-qbody .mc-choices.shr-ch-inline .mc-choice,
.shr-report--compact .shr-qbody .pi-choices.shr-ch-inline .pi-choice,
.shr-report--compact .shr-qbody .mcq-choices.shr-ch-inline .mcq-choice,
.shr-report--compact .shr-qbody .pei-choices.shr-ch-inline .pei-choice {
  display: inline; padding-left: 0 !important; text-indent: 0;
}
/* Two per line. Each cell keeps the hanging indent, so a cell that wraps by a
   word still reads as one option. */
.shr-report--compact .shr-qbody .mc-choices.shr-ch-2up,
.shr-report--compact .shr-qbody .pi-choices.shr-ch-2up,
.shr-report--compact .shr-qbody .mcq-choices.shr-ch-2up,
.shr-report--compact .shr-qbody .pei-choices.shr-ch-2up {
  display: grid; grid-template-columns: 1fr 1fr;
  column-gap: calc(5 * var(--shr-u));
}
.shr-report--compact .shr-qbody .mc-choice-main,
.shr-report--compact .shr-qbody .pi-choice-main,
.shr-report--compact .shr-qbody .pei-choice-main { display: inline; }
.shr-report--compact .shr-qbody .mcq-choice-main { display: inline; }
.shr-report--compact .shr-qbody .mc-choice-text,
.shr-report--compact .shr-qbody .pi-choice-text,
.shr-report--compact .shr-qbody .pei-choice-text { display: inline; }
.shr-report--compact .shr-qbody .mcq-choice-text { display: inline; }
/* The choice text goes through SharedMathText, which emits Markdown block
   paragraphs; one block anywhere in the run breaks the line and the inline
   list stops being inline. */
.shr-report--compact .shr-qbody .mc-choice-text .md-p,
.shr-report--compact .shr-qbody .pi-choice-text .md-p,
.shr-report--compact .shr-qbody .pei-choice-text .md-p { display: inline; margin: 0; }
.shr-report--compact .shr-qbody .mcq-choice-text .md-p { display: inline; margin: 0; }
.shr-report--compact .shr-qbody .mc-verdict-adorn,
.shr-report--compact .shr-qbody .pi-verdict-adorn,
.shr-report--compact .shr-qbody .pei-verdict-adorn { display: inline; }
.shr-report--compact .shr-qbody .mc-letter::after,
.shr-report--compact .shr-qbody .pi-letter::after,
.shr-report--compact .shr-qbody .pei-letter::after { content: ")\00a0"; }
.shr-report--compact .shr-qbody .mcq-letter::after { content: ")\00a0"; }

/* No `:has()` fallback any more: one-per-line IS the default, and a list
   carrying figures, display math or an annotation is simply never promoted
   (fitChoices skips it outright). The old rules existed to undo an
   inline-always default that no longer exists. */

/* Figures: capped to the column, never stretched. MathFigure caps itself with
   two custom properties it sets INLINE (--fig-cap width, --fig-hcap height),
   and an ancestor rule cannot beat a non-important inline declaration without
   !important — which does. */
.shr-report--compact .shr-qbody .figure-card,
.shr-report--compact .shr-passage .figure-card {
  padding: calc(2 * var(--shr-u)) !important;
  margin-bottom: calc(2 * var(--shr-u)) !important;
}
.shr-report--compact .shr-qbody .mc-figure > div,
.shr-report--compact .shr-qbody .figure-wrap,
.shr-report--compact .shr-qbody [style*="--fig-cap"] {
  --fig-cap: calc(160 * var(--shr-u)) !important;
  --fig-hcap: calc(96 * var(--shr-u)) !important;
}
.shr-report--compact .shr-qbody svg,
.shr-report--compact .shr-qbody img { max-width: 100%; height: auto; }
.shr-report--compact .katex { font-size: 1em; }

/* =========================================================================
 * THE WORKSHEET — a blank page, at a size a child can work on
 * =========================================================================
 * The third layout variant, and the only one that is not a report: blank
 * questions, working space, an answer strip. docs/WORKSHEETS.md is the
 * contract; shared-share-report.jsx's `describeWorksheet` is the builder.
 *
 * SIZE IS THE WHOLE ARGUMENT. Compact's 7.5pt floor is right for a document a
 * parent SKIMS — a hundred questions onto a few sheets, read once. A worksheet
 * is a document a child WORKS ON for forty minutes with a pencil, so it is set
 * at 9.25pt and given a real gutter. It still packs into columns, because two
 * columns of 9.25pt on Letter is a comfortable measure and one column is a
 * third of a page of white down the right-hand side.
 *
 * NOTHING ON IT IS A FILL, from the start (see the section below): every
 * bubble is a ring, the working space is a rule, the code is type.
 * ========================================================================= */
.shr-report--worksheet {
  font-size: calc(9.25 * var(--shr-u));
  line-height: 1.38;
  /* Wider than compact's 12: the numbers are what a student and a parent both
     navigate by on paper, and they are the only fixed landmark on the page. */
  --shr-qgut: calc(16 * var(--shr-u));
}
.shr-report--worksheet .shr-qbody { font-size: calc(9.25 * var(--shr-u)); }
.shr-report--worksheet .shr-q {
  margin-top: calc(8 * var(--shr-u));
  padding-top: calc(3 * var(--shr-u));
  padding-bottom: calc(1 * var(--shr-u));
}
.shr-report--worksheet .shr-col > .shr-q:first-child,
.shr-report--worksheet .shr-sectionhead + .shr-q,
.shr-report--worksheet .shr-passage + .shr-q {
  margin-top: calc(2 * var(--shr-u));
  padding-top: 0;
}
.shr-report--worksheet .shr-qn { font-size: calc(10.5 * var(--shr-u)); }
.shr-report--worksheet .shr-qhead {
  font-size: calc(7.5 * var(--shr-u));
  margin-bottom: calc(2 * var(--shr-u));
}
/* Choices one per line with a hanging indent — the same grammar as the other
   two layouts. NEVER the inline run compact promotes to: a student has to be
   able to put a mark beside each one. */
.shr-report--worksheet .shr-qbody .mc-choices,
.shr-report--worksheet .shr-qbody .pi-choices,
.shr-report--worksheet .shr-qbody .pei-choices { display: block; }
.shr-report--worksheet .shr-qbody .mcq-choices { display: block; }
.shr-report--worksheet .shr-qbody .mc-choice-row,
.shr-report--worksheet .shr-qbody .pi-choice-row,
.shr-report--worksheet .shr-qbody .mcq-choice-row,
.shr-report--worksheet .shr-qbody .pei-choice-row {
  display: block; margin: calc(1.5 * var(--shr-u)) 0;
}
/* ONE BODY SIZE FOR STEM AND CHOICES. The three renderers set a choice's size
   themselves, in PX, at their own screen scale — so on a worksheet the prose
   questions printed their choices visibly LARGER than the stem they answer
   while the maths ones printed smaller, on the same sheet. The stem is
   9.25pt, so the choices are 9.25pt; `!important` because the value it is
   beating is `.pi-choice`'s own. */
.shr-report--worksheet .shr-qbody .mc-choice,
.shr-report--worksheet .shr-qbody .pi-choice,
.shr-report--worksheet .shr-qbody .mcq-choice,
.shr-report--worksheet .shr-qbody .pei-choice {
  display: block;
  font-size: calc(9.25 * var(--shr-u)) !important;
  line-height: 1.34;
  padding-left: calc(16 * var(--shr-u)) !important;
  text-indent: calc(-16 * var(--shr-u));
}
/* THE QUANTITATIVE-COMPARISON PANEL IS TYPE, NOT A CARD.
   INVARIANT: in every print layout the Column A / Column B panel is a
   two-column line of type with a rule between the columns — no fill, no
   radius, no padding box — and its values are set at the layout's own body
   size, whatever that is (9.25 worksheet, 10 detailed, 7.5 compact).
   WHY: on screen the panel is inset so it cannot be mistaken for one of the
   tappable choices under it; on paper nothing is tappable and NOTHING IS A
   FILL, so the inset ground is a card that is not there, and its 18px values
   printed visibly larger than the stem they belong to. Scoped to
   `.shr-qbody` rather than the worksheet alone because a sitting report
   renders qc items too; `!important` because QcItem sets these inline, the
   same reason `.mc-grid-input` needs it. */
.shr-report .shr-qbody .math-qc-panel {
  background: none !important;
  border: none !important;
  border-radius: 0 !important;
  padding: 0 !important;
  gap: calc(10 * var(--shr-u)) !important;
  margin: calc(3 * var(--shr-u)) 0 calc(4 * var(--shr-u)) !important;
}
.shr-report .shr-qbody .math-qc-col + .math-qc-col {
  border-left: calc(0.6 * var(--shr-u)) solid #14110d !important;
  padding-left: calc(8 * var(--shr-u)) !important;
}
.shr-report .shr-qbody .math-qc-label {
  font-size: calc(6.5 * var(--shr-u)) !important;
  color: #5f584a !important;
  margin-bottom: calc(1 * var(--shr-u)) !important;
}
.shr-report .shr-qbody .math-qc-value {
  font-size: inherit !important;
  color: #14110d !important;
}
/* …and the stem should not be half an inch from its own first choice. The
   renderers space a tapped list off the question; a printed one is read as
   one object. */
.shr-report--worksheet .shr-qbody .mc-choices,
.shr-report--worksheet .shr-qbody .pi-choices,
.shr-report--worksheet .shr-qbody .mcq-choices,
.shr-report--worksheet .shr-qbody .pei-choices {
  margin-top: calc(3 * var(--shr-u)) !important;
}
.shr-report--worksheet .shr-qbody .mc-choice-main,
.shr-report--worksheet .shr-qbody .pi-choice-main,
.shr-report--worksheet .shr-qbody .pei-choice-main { display: inline; }
.shr-report--worksheet .shr-qbody .mcq-choice-main { display: inline; }
.shr-report--worksheet .shr-qbody .mc-choice-text,
.shr-report--worksheet .shr-qbody .pi-choice-text,
.shr-report--worksheet .shr-qbody .pei-choice-text { display: inline; }
.shr-report--worksheet .shr-qbody .mcq-choice-text { display: inline; }
.shr-report--worksheet .shr-qbody .mc-choice-text .md-p,
.shr-report--worksheet .shr-qbody .pi-choice-text .md-p,
.shr-report--worksheet .shr-qbody .pei-choice-text .md-p { display: inline; margin: 0; }
.shr-report--worksheet .shr-qbody .mcq-choice-text .md-p { display: inline; margin: 0; }
/* AN EMPTY CIRCLE BEFORE EVERY LETTER. The one thing a printed choice needs
   that a screen one does not: somewhere to put the mark. Drawn on the letter's
   ::before so no renderer had to learn about paper, and hanging back into the
   indent the rule above reserved, so the letters stay on one x. */
.shr-report--worksheet .shr-qbody .mc-letter::before,
.shr-report--worksheet .shr-qbody .pi-letter::before,
.shr-report--worksheet .shr-qbody .mcq-letter::before,
.shr-report--worksheet .shr-qbody .pei-letter::before {
  content: '';
  display: inline-block;
  width: calc(7 * var(--shr-u));
  height: calc(7 * var(--shr-u));
  margin-right: calc(3 * var(--shr-u));
  border: calc(0.7 * var(--shr-u)) solid #14110d;
  border-radius: 50%;
  vertical-align: middle;
  text-indent: 0;
}
.shr-report--worksheet .shr-qbody .mc-letter::after,
.shr-report--worksheet .shr-qbody .pi-letter::after,
.shr-report--worksheet .shr-qbody .pei-letter::after { content: ")\00a0"; }
.shr-report--worksheet .shr-qbody .mcq-letter::after { content: ")\00a0"; }
/* A STACKED FRACTION IS TALLER THAN ITS LINE BOX, and inline content cannot
   push its neighbours apart — so a list of four bare fractions set at the
   body's leading has each denominator sitting on the next numerator. It reads,
   but not on a page somebody is going to put a pencil mark in. `:has()` gives
   the row that actually carries one its own leading (the report already relies
   on `:has()` in its print rules); a list of plain numerals is untouched, so
   nothing pays for this that does not need it. The taller line box also
   re-centres the bubble against the fraction rather than its numerator. */
.shr-report--worksheet .shr-qbody .mc-choice:has(.mfrac),
.shr-report--worksheet .shr-qbody .pi-choice:has(.mfrac),
.shr-report--worksheet .shr-qbody .pei-choice:has(.mfrac) { line-height: 2.7; }
.shr-report--worksheet .shr-qbody .mcq-choice:has(.mfrac) { line-height: 2.7; }
/* …and a row whose fraction is nested deeper (a fraction inside a root, a
   mixed number) needs more again. Two levels is as far as this goes; past
   that the renderer emits `.katex-display`, which is a block and spaces
   itself. */
.shr-report--worksheet .shr-qbody .mc-choice:has(.mfrac .mfrac),
.shr-report--worksheet .shr-qbody .pi-choice:has(.mfrac .mfrac),
.shr-report--worksheet .shr-qbody .pei-choice:has(.mfrac .mfrac) { line-height: 3.4; }
.shr-report--worksheet .shr-qbody .mcq-choice:has(.mfrac .mfrac) { line-height: 3.4; }

/* A GRID-IN HAS NO CHOICES, SO IT HAS A CONTROL — and on paper a live text
   box, a sign toggle and a greyed-out Submit are three things nobody can tap.
   The whole apparatus goes, and the row it stood in becomes what a printed
   grid-in has always been: a rule to write the number on. (The answer strip
   gives the same slot a box of its own, for copying into the app.) */
.shr-report--worksheet .shr-qbody .mc-grid-sign,
.shr-report--worksheet .shr-qbody .mc-grid-done,
.shr-report--worksheet .shr-qbody .mc-grid-submit { display: none !important; }
.shr-report--worksheet .shr-qbody .mc-grid-input {
  border: none !important;
  border-bottom: calc(0.8 * var(--shr-u)) solid #14110d !important;
  border-radius: 0 !important;
  background: none !important;
  box-shadow: none !important;
  width: calc(110 * var(--shr-u)) !important;
  min-width: 0 !important;
  height: calc(16 * var(--shr-u)) !important;
  min-height: 0 !important;
  padding: 0 !important;
  /* The placeholder is UI copy ("Your answer"), not a question. */
  color: transparent !important;
}
.shr-report--worksheet .shr-qbody .mc-grid-input::placeholder { color: transparent !important; }
.shr-report--worksheet .shr-qbody .mc-grid { margin-top: calc(4 * var(--shr-u)) !important; }

/* A worksheet is blank, so nothing here should ever render — but a renderer
   that grew a new reveal-shaped node must not be able to put a key on a page
   whose entire value is that it has none. This is the belt to the builder's
   braces (it dispatches with no `review` at all); if either one alone ever
   stops working, the page is still blank. */
.shr-report--worksheet .shr-qbody .mc-why-block,
.shr-report--worksheet .shr-qbody .mc-why,
.shr-report--worksheet .shr-qbody .mc-why-blocks,
.shr-report--worksheet .shr-qbody .mc-why-verdict,
.shr-report--worksheet .shr-qbody .mc-insight,
.shr-report--worksheet .shr-qbody .mc-grid-expected,
.shr-report--worksheet .shr-qbody .mc-verdict-adorn,
.shr-report--worksheet .shr-qbody .pi-verdict-adorn,
.shr-report--worksheet .shr-qbody .pei-verdict-adorn,
.shr-report--worksheet .shr-qtag { display: none !important; }
/* …and no highlight/strike can survive either. */
.shr-report--worksheet .shr-qbody .mc-choice.is-correct,
.shr-report--worksheet .shr-qbody .pi-choice.is-correct,
.shr-report--worksheet .shr-qbody .pei-choice.is-correct { font-weight: inherit; }
.shr-report--worksheet .shr-qbody .mcq-choice.is-correct { font-weight: inherit; }
.shr-report--worksheet .shr-qbody .mc-choice.is-wrong .mc-choice-text,
.shr-report--worksheet .shr-qbody .pi-choice.is-wrong .pi-choice-text,
.shr-report--worksheet .shr-qbody .mcq-choice.is-wrong .mcq-choice-text,
.shr-report--worksheet .shr-qbody .pei-choice.is-wrong .pei-choice-text {
  text-decoration: none;
}
.shr-report--worksheet .shr-qbody .mc-eyebrow,
.shr-report--worksheet .shr-qbody .pi-eyebrow,
.shr-report--worksheet .shr-qbody .an-eyebrow,
.shr-report--worksheet .shr-qbody .pei-eyebrow { display: none !important; }
/* Between the two report sizes: a worksheet's passage is READ, so it cannot
   be compact's 0.58, but it sits in a column beside 9.25pt questions and the
   detailed 0.79 made the prose the biggest type on the sheet. */
:root { --shr-pz-worksheet: 0.68; }
:root[data-shr-ppp="1"] { --shr-pz-worksheet: 0.51; }
.shr-report--worksheet .shr-passage-body { zoom: var(--shr-pz-worksheet); }
.shr-report--worksheet .shr-qbody .figure-card { max-width: calc(250 * var(--shr-u)); }
.shr-report--worksheet .shr-qbody svg,
.shr-report--worksheet .shr-qbody img { max-width: 100%; height: auto; }

/* ROOM TO WORK. A ruled band under a question that has to be computed at —
   two faint rules rather than a box, because a box says "your answer goes
   here" and this is scratch. Height first: 46pt is about four lines of a
   child's handwriting. */
.shr-room {
  height: calc(46 * var(--shr-u));
  margin-top: calc(3 * var(--shr-u));
  border-bottom: calc(0.5 * var(--shr-u)) dotted #bdb5a4;
}

/* ---- The header band ---------------------------------------------------- */
.shr-wshead {
  display: flex; align-items: flex-start; gap: calc(14 * var(--shr-u));
  margin-bottom: calc(8 * var(--shr-u));
  padding-bottom: calc(6 * var(--shr-u));
  border-bottom: calc(0.5 * var(--shr-u)) solid #ded7c9;
}
.shr-wshead-main { flex: 1; min-width: 0; }
.shr-wsname {
  display: flex; align-items: baseline; gap: calc(4 * var(--shr-u));
  font-size: calc(9 * var(--shr-u)); font-weight: 600;
  margin-bottom: calc(7 * var(--shr-u));
}
.shr-wsrule {
  flex: 1 1 auto; min-width: calc(60 * var(--shr-u));
  border-bottom: calc(0.7 * var(--shr-u)) solid #14110d;
  height: calc(11 * var(--shr-u));
}
.shr-wsfacts { font-size: calc(8 * var(--shr-u)); color: #5f584a; margin-bottom: calc(3 * var(--shr-u)); }
.shr-wshow { font-size: calc(8 * var(--shr-u)); line-height: 1.45; color: #3c352b; }
.shr-wsurl {
  font-size: calc(7 * var(--shr-u)); color: #6b6355; margin-top: calc(2 * var(--shr-u));
  word-break: break-all;
}
.shr-wshead-code { flex: 0 0 auto; text-align: center; }
.shr-wsqr {
  width: calc(64 * var(--shr-u)); height: calc(64 * var(--shr-u));
  color: #14110d; margin: 0 auto;
}
.shr-wsqr svg { display: block; width: 100%; height: 100%; }
.shr-wscode {
  font-size: calc(15 * var(--shr-u)); font-weight: 700; letter-spacing: .08em;
  margin-top: calc(3 * var(--shr-u)); white-space: nowrap;
}

/* ---- The answer strip --------------------------------------------------- */
.shr-bubbles-head {
  font-size: calc(11 * var(--shr-u)); font-weight: 700;
  margin-bottom: calc(2 * var(--shr-u));
}
.shr-bubbles-note {
  font-size: calc(8 * var(--shr-u)); color: #5f584a;
  margin-bottom: calc(7 * var(--shr-u));
}
.shr-bubbles-grid {
  display: flex; flex-wrap: wrap;
  gap: calc(5 * var(--shr-u)) calc(14 * var(--shr-u));
}
.shr-bub {
  display: flex; align-items: center; gap: calc(3 * var(--shr-u));
  font-size: calc(8.5 * var(--shr-u));
  min-width: calc(86 * var(--shr-u));
}
.shr-bub-n {
  min-width: calc(13 * var(--shr-u)); text-align: right;
  font-weight: 700; font-variant-numeric: tabular-nums;
}
.shr-bub-opts { display: flex; gap: calc(3 * var(--shr-u)); }
/* EVERY BUBBLE IS A RING. The letter sits inside it, so the student marks the
   one they mean rather than a blank circle beside a letter — and an outline
   survives a printer with background graphics off, which a filled disc with
   knocked-out type would not. */
.shr-bub-o {
  display: inline-flex; align-items: center; justify-content: center;
  width: calc(12 * var(--shr-u)); height: calc(12 * var(--shr-u));
  border: calc(0.6 * var(--shr-u)) solid #14110d;
  border-radius: 50%;
  font-size: calc(6.5 * var(--shr-u)); line-height: 1;
  color: #5f584a;
}
/* A grid-in has no letters: a box to write the number in. */
.shr-bub-box {
  display: inline-block;
  width: calc(54 * var(--shr-u)); height: calc(12 * var(--shr-u));
  border: calc(0.6 * var(--shr-u)) solid #14110d;
  border-radius: calc(1.5 * var(--shr-u));
}
.shr-bub--dead .shr-bub-n { text-decoration: line-through; color: #6b6355; }
.shr-bub-x {
  display: inline-block;
  width: calc(54 * var(--shr-u)); height: calc(12 * var(--shr-u));
  border-bottom: calc(0.6 * var(--shr-u)) solid #bdb5a4;
}

/* ---- The running head --------------------------------------------------- */
/* INSIDE the page box, never an `@page` margin box: those do nothing at all in
   the iOS print formatter, so the only running text that survives both engines
   is one the document draws itself. Its height is RESERVED by the packer
   before anything is dealt (STAMP_PT in shared-share-report.jsx) — 8pt of box
   plus the 3pt gap below it. The two numbers must move together. */
.shr-stamp {
  height: calc(8 * var(--shr-u));
  margin-bottom: calc(3 * var(--shr-u));
  font-size: calc(7 * var(--shr-u));
  line-height: calc(8 * var(--shr-u));
  color: #6b6355;
  display: flex; justify-content: space-between;
  border-bottom: calc(0.4 * var(--shr-u)) solid #ded7c9;
  box-sizing: border-box;
  white-space: pre;
}

/* =========================================================================
 * PAPER CARRIES NO STRUCTURAL FILLS (owner, 2026-09-19)
 * =========================================================================
 * The rule: a report must look the SAME whether or not the browser is printing
 * backgrounds, and must not spend toner on grounds. §6 of docs/SHARE_REPORTS.md
 * already said colour is never the mark; this says the stronger thing — a
 * GROUND is never structure either. Everything that was a tinted card is now a
 * rule, an outline or plain text, so "Background graphics" off in Chrome's
 * dialog (its default), a toner-saving printer and a photocopier all produce
 * the same document.
 *
 * THE ONE ALLOWED FILL is the very light warm tint behind a MISSED question
 * (`.shr-q--miss`, above): nothing depends on it — the solid left bar and the
 * outlined number box carry that state on their own — so it may vanish.
 *
 * Applies to BOTH layouts and to the worksheet, and it is authored here rather
 * than at each renderer because the renderers are the APP's and a screen is
 * exactly where a fill belongs.
 * ========================================================================= */

/* The panel itself, in EVERY layout. Detailed restates the rest of its shape
   below; this is the one line that has to be true wherever it renders. */
.shr-report .shr-qbody .mc-why-block { background: none !important; }

/* The verdict head. On screen it is a semantic BAND bled to the panel's edges
   (mc-item.css, study P1.21). On paper it is a bold ink line with a hairline
   over it — no bleed, so it starts at the same x as the lesson under it. */
.shr-report .shr-qbody .mc-why-verdict {
  background: none !important;
  color: #14110d !important;
  margin: 0 0 calc(3 * var(--shr-u)) !important;
  padding: calc(3 * var(--shr-u)) 0 0 !important;
  border: none !important;
  border-top: calc(0.5 * var(--shr-u)) solid #bdb5a4 !important;
  border-radius: 0 !important;
}
/* An item with no explanation makes the verdict the panel's only content; on
   screen the band then IS the panel (two `:last-child` rules with their own
   bleed). Here it is simply the line, with no rule under it to hang off. */
.shr-report .shr-qbody .mc-why-verdict:last-child {
  margin-bottom: 0 !important;
}
.shr-report .shr-qbody .mc-why-verdict-word { color: #14110d !important; }
.shr-report .shr-qbody .mc-why-key { color: #14110d !important; }

/* THE LESSON IS SET IN THE PAGE'S OWN FACE. `.mc-why` asks for
   `--font-serif-body` ("Iowan Old Style", Charter, Georgia) because on screen
   the explanation is read at the student's most frustrated moment and earns
   passage-grade type. On paper it resolved to a Times fallback beside Lexend
   everywhere else, which reads as two documents stapled together. KaTeX is
   untouched: `.katex` sets its own family on its own subtree, so inheriting
   here cannot reach a formula. */
.shr-report .shr-qbody .mc-why,
.shr-report .shr-qbody .mc-why-blocks,
.shr-report .shr-qbody .mc-why-tldr,
.shr-report .shr-qbody .mc-why-heading,
.shr-report .shr-qbody .mc-insight,
.shr-report .shr-qbody .mc-why-key-text {
  font-family: Lexend, -apple-system, system-ui, sans-serif;
}
/* …and the bands inside the lesson lose their grounds with everything else. */
.shr-report .shr-qbody .mc-why-tldr,
.shr-report .shr-qbody .mc-insight,
.shr-report .shr-qbody .mc-grid-expected {
  background: none !important; border-radius: 0;
}

/* A FIGURE'S PANEL. `FigureCard` paints its inset INLINE (subjects/math/math.jsx),
   so this needs !important. The figure's own ink — strokes, bars, labels — is
   untouched: that is content, and the panel is only the stage it stands on.
   White with a hairline keeps the figure marked off from the prose without
   printing a tan rectangle the size of a playing card. */
.shr-report .figure-card {
  background: #fff !important;
  background-image: none !important;
  border: calc(0.5 * var(--shr-u)) solid #ded7c9 !important;
}

/* The summary's question grid: OUTLINE ONLY. The glyph (✓ ✗ ·) says the state
   and the border weight/style repeats it, which is what §6 already claimed —
   the fills were the part that was only ever reinforcement. */
.shr-cell.is-ok, .shr-cell.is-miss, .shr-cell.is-blank,
.shr-cell.is-dead, .shr-cell.is-annulled { background: none; }

/* Notes, the grid-in entry line and the day strip's tracks were all inset
   CARDS. A hairline left rule says "set off from the prose" at no cost. */
.shr-note {
  background: none; border-radius: 0;
  border-left: calc(0.8 * var(--shr-u)) solid #bdb5a4;
  padding-left: calc(5 * var(--shr-u));
}
.shr-grid-entry {
  background: none; border-radius: 0;
  border-left: calc(0.8 * var(--shr-u)) solid #bdb5a4;
  padding: 0 0 0 calc(5 * var(--shr-u));
}
/* The bar IS the datum and stays solid ink; the track behind it was a ground. */
.shr-daytrack {
  background: none;
  border: calc(0.5 * var(--shr-u)) solid #ded7c9;
  border-radius: 0;
}

/* A question never splits across a column or a page in either layout. The
   packer enforces it structurally in compact; this is what does it in
   detailed, and it is the belt to the packer's braces. */
.shr-q, .shr-passage { break-inside: avoid; page-break-inside: avoid; }

/* =========================================================================
 * PRINT
 * =========================================================================
 * Only the report prints. Everything the app draws around it — the two-tier
 * topbar, the desktop rail, the mode header, the control strip, the dev device
 * frame — is chrome, and chrome on a document sent to a parent is noise.
 *
 * The un-clipping matters as much as the hiding: the dev shell pins
 * .device-stage with `position: fixed` and the app gives .shell its own
 * scroller, and a fixed, clipped ancestor prints exactly one viewport. */
@media print {
  :root[data-shr] { background: #fff; }
  :root[data-shr], :root[data-shr] body,
  :root[data-shr] .device-stage,
  :root[data-shr] .device-frame,
  :root[data-shr] .shell,
  :root[data-shr] .shell-body,
  :root[data-shr] main.stage,
  :root[data-shr] .subject-root,
  /* The vocab app's own wrapper. It paints a cream ground and holds a
     min-height, so its tail printed as an empty tinted band under the report's
     footer on every vocabulary week. */
  :root[data-shr] .app,
  :root[data-shr] .mode-screen,
  :root[data-shr] .shr-screen {
    position: static !important;
    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;
    width: auto !important;
    max-width: none !important;
    overflow: visible !important;
    margin: 0 !important;
    padding: 0 !important;
    transform: none !important;
    background: #fff !important;
    box-shadow: none !important;
    border: none !important;
    display: block !important;
  }
  /* The un-clipping above turns EVERY subject root into a visible block, which
     is right for the one the report is in and wrong for the others — they hold
     whole screens. Only the active view prints. */
  :root[data-shr] .subject-root:not(.active) { display: none !important; }
  /* …and inside the one root that IS the report's (the Vocabulary Builder's),
     only the branch holding it. The product keeps persistent chrome as a
     sibling of the screen, which printed as an empty grey band under the
     footer. */
  :root[data-shr] #root-learning-lab > *:not(:has(.shr-report)) { display: none !important; }

  /* The zoom neutralisation is NOT here any more — it is unconditional at the
     top of this file, because the packer measures on screen and a page count
     that changed with the viewer's Aa setting was the bug. `!important` is
     kept so nothing later in the cascade can put the zoom back on paper; the
     chrome that opts back in is hidden in print anyway. */
  :root[data-shr] main.stage { zoom: 1 !important; }

  :root[data-shr] .topbar,
  :root[data-shr] .sidenav,
  :root[data-shr] .device-picker,
  :root[data-shr] .dev-switcher,
  :root[data-shr] .mode-header,
  :root[data-shr] .mode-headwrap,
  :root[data-shr] .mode-header-eyebrow,
  :root[data-shr] .progress-bar-track,
  :root[data-shr] .shr-controls,
  /* …UNLESS THE REPORT IS INSIDE IT. On the Vocabulary Builder the whole
     product mounts in #root-learning-lab, so hiding that root outright printed
     a blank sheet for the vocabulary week — the one surface whose report has
     no other home. The other roots are hidden because they are the OTHER
     subject sitting behind this one; this one is the page. */
  :root[data-shr] #root-learning-lab:not(:has(.shr-report)),
  :root[data-shr] #host-intro,
  :root[data-shr] #library-links,
  :root[data-shr] #exam-links,
  :root[data-shr] .signin-gate,
  :root[data-shr] .onboard-scrim,
  :root[data-shr] .badge-toast,
  :root[data-shr] .no-print { display: none !important; }

  /* THE SAFE-AREA STRIPS ARE FIXED, AND A FIXED BOX PRINTS ON EVERY PAGE.
     `body::before` (index.html) paints a cream band over
     env(safe-area-inset-top) so content scrolls under the status bar; on the
     web env() is 0 and it costs nothing, but in the packaged app it is ~59pt
     — and it painted that band OVER the top of every printed sheet, so the
     report's title, its subtitle and the top half of the stat row were in the
     PDF's text but invisible in its picture. `body::after` is the desktop
     rail's twin. Neither belongs on paper. */
  :root[data-shr] body::before,
  :root[data-shr] body::after { display: none !important; content: none !important; }

  :root[data-shr] .shr-report { background: #fff; color: #000; }
  /* Exact tints: the fills are already near-white, and letting the browser
     drop them turns the question grid's three states into one. */
  :root[data-shr] .shr-report,
  :root[data-shr] .shr-report * {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  /* THE FIT IS A SCREEN AFFORDANCE AND NOTHING ELSE. On paper the sheet is
     already the right size, `@page` owns its margins, and the scaled box's
     clipped height would cut the document off at one screenful. */
  :root[data-shr] .shr-stage {
    padding: 0 !important;
  }
  :root[data-shr] .shr-fit {
    height: auto !important;
    max-width: none !important;
    overflow: visible !important;
    margin: 0 !important;
  }
  :root[data-shr] .shr-fit-in {
    width: auto !important;
    transform: none !important;
  }
  :root[data-shr] .shr-fit--on .shr-src,
  :root[data-shr] .shr-fit--on .shr-page {
    padding: 0 !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
  }

  /* WHEREVER THE PACKER RAN, the packed pages ARE the document and the
     off-page source must not print behind them. Both packed variants, named
     rather than inverted: a future variant that does NOT pack must keep its
     source, and `:not([data-shr="detailed"])` would silently take it away.
     (Measured: a 22-question worksheet printed 7 packed pages and then the
     whole source again, 12 sheets for 7 pages of content.) */
  :root[data-shr="compact"] .shr-src,
  :root[data-shr="worksheet"] .shr-src { display: none !important; }
  :root[data-shr] .shr-page {
    break-after: page; page-break-after: always;
    margin: 0 !important;
  }
  :root[data-shr] .shr-page:last-child { break-after: auto; page-break-after: auto; }
  /* Chrome honours this. The iOS print formatter ignores @page entirely and
     takes its margins from perPageContentInsets instead — keep 28 in step with
     MARGIN_PT.compact in shared-share-report.jsx.
     NO `size` HERE: the paper is a runtime choice (Letter / A4), so the rule
     that carries it is injected as <style id="shr-page-size"> while a preview
     screen is mounted (applyPageSize in shared-share-report.jsx). That style
     restates this margin, so the two cannot drift; this rule is what holds if
     the injection ever fails. */
  @page { margin: 28pt; }
}

/* The passage in COMPACT: the rule stays (it is what marks the prose off from
   the questions), the space around it goes. */
.shr-report--compact .shr-passage {
  margin: calc(3 * var(--shr-u)) 0 calc(3 * var(--shr-u));
  padding-left: calc(3.5 * var(--shr-u));
}

/* A TABLE INSIDE A STEM is drawn by the graphics engine at reading size, with
   a card round it. In COMPACT the card's own padding and the cell padding are
   most of its height — the numbers in it are already the smallest thing it
   carries, so they are left alone and the space is taken instead. */
.shr-report--compact .shr-qbody .gfx-table td,
.shr-report--compact .shr-qbody .gfx-table th,
.shr-report--compact .shr-qbody table td,
.shr-report--compact .shr-qbody table th {
  padding: calc(1.5 * var(--shr-u)) calc(3 * var(--shr-u)) !important;
}

/* --- The effort strip ----------------------------------------------------
   Thirty day-tracks, each full height with the day's bar sitting in it, so a
   month of empty days reads as an empty chart and not as a dotted rule. The
   two quantities are stated in the line below, which is why the bars are
   aria-hidden in the markup. */
.shr-days { margin-bottom: calc(8 * var(--shr-u)); }
.shr-daybars {
  display: flex; align-items: flex-end; gap: calc(1 * var(--shr-u));
  height: calc(26 * var(--shr-u)); margin-bottom: calc(3 * var(--shr-u));
}
.shr-daytrack {
  flex: 1; min-width: 0; height: 100%;
  background: #eee7d9; border-radius: calc(1 * var(--shr-u));
  display: flex; flex-direction: column; justify-content: flex-end;
}
.shr-daybar { display: block; background: #8a806c; border-radius: calc(1 * var(--shr-u)); }
.shr-daybar.is-today { background: #14110d; }
.shr-dayline { font-size: calc(8 * var(--shr-u)); color: #5f584a; line-height: 1.45; }
