/* Exhibit A — marketing site stylesheet.
 *
 * Extracted 7 Aug 2026 from the five pages' inline <style> blocks. Nothing here is new except the
 * article typography at the end; every other declaration is transcribed unchanged from the page it
 * came from, in its original order, with its original comments.
 *
 * WHY EVERY RULE IS SCOPED. The homepage and the document pages are not one design with additions —
 * they disagree. Both blocks define `.wrap` (68rem against 46rem), `body` (line-height 1.6 against
 * 1.65), `.btn`, `.kicker` and `.foot` with DIFFERENT values, and the document block additionally
 * styles bare `main` and `h1`, which on the homepage would land on the hero. Concatenated into one
 * sheet, whichever block came second would win everywhere and one of the two designs would move.
 *
 * So each page declares which it is — `<html data-page="home">` or `<html data-page="doc">` — and
 * every selector below is prefixed to match. A page receives exactly the rules it carried inline and
 * none of the other set's. The prefix is applied uniformly within each block, so specificity moves
 * by the same amount for every rule in it and the original cascade order is preserved.
 *
 * New pages are `doc`. That is what site/_skeleton.html ships with.
 *
 * The pre-paint theme script and the toggle handler stay inline in each page. The first MUST run
 * before the browser paints or the page flashes its default theme, which an external file cannot
 * guarantee; the second is left with it for now rather than split across two mechanisms. Both are
 * still duplicated five times over and are worth a later pass — this one moves CSS only.
 */

/* ==========================================================================
   LITERATA 700 — the wordmark face, and the only thing it is used for.
   Self-hosted from @fontsource/literata 5.3.0 (files/literata-latin-700-normal
   .woff2, 22KB), with the project's OFL licence sitting beside it in
   assets/fonts/. Self-hosting is the point: the page already fetches Inter from
   Google, and the mark is the one thing that should not be waiting on a third
   party or absent if one is blocked.

   The url() is relative to THIS FILE, not to the page, which is what lets
   resources/_article-template.html — one directory down — resolve it unchanged.
   ========================================================================== */
@font-face {
  font-family: 'Literata';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('assets/fonts/literata-700.woff2') format('woff2');
}

/* ==========================================================================
   THE MARK'S THREE COLOUR ROLES — Sean's ruling, 22 August 2026.

   A WHITE folder carrying a VIOLET A, on both grounds. `shell` is the folder,
   `panel` the label panel and the A's counter, `glyph` the spectacles and the A,
   `edge` the folder's outline. The first three do not move between themes and
   are declared once; only `edge` does, and that is the whole theme logic. The
   outline exists because a white folder on a near-white page has no silhouette;
   on night the ground supplies one, so it is switched off rather than recoloured.
   `panel` equals `shell`, so the label panel is not drawn as a separate field —
   that is what puts the A in the opposite colour to the field behind it. Below
   28px the mark changes tier instead, to a solid violet folder with the A
   knocked out white, because a hairline cannot survive at 16 or 20. The geometry
   and the reasoning live
   in lib/ui/askAMark.mjs; `npm run build:mark` writes the mark into every page
   here referring to these names.

   DELIBERATELY UNSCOPED, unlike every other rule in this file. The mark appears
   on BOTH page scopes — the homepage nav and its four feature cards, and every
   document page's lockup — and a `[data-page="home"]` prefix would leave the
   document pages drawing the fallback pair baked into the SVG. There is no
   collision to avoid here: these four names are new, belong to one drawing, and
   are not declared anywhere else in this sheet.

   AND THE ATTRIBUTE IS "night", NOT "dark". That is this site's spelling and it
   is NOT the app's — the app keys the same swap off `:root[data-theme="dark"]`
   in app/globals.css. Two surfaces, two vocabularies, one mark. Writing "dark"
   here is a silent no-op that leaves the site permanently in the light pair.
   ========================================================================== */
:root { --exa-shell: #FFFFFF; --exa-panel: #FFFFFF; --exa-glyph: #7C3AED; --exa-edge: #7C3AED; }
[data-theme="night"] { --exa-edge: transparent; }

/* ==========================================================================
   DOCUMENT PAGES — limits, terms, privacy, refunds, and every page built
   from _skeleton.html. Transcribed from the block that was byte-identical
   across all four.
   ========================================================================== */

[data-page="doc"], [data-page="doc"][data-theme="light"] {
  --bg: #F6F8FA; --bg-panel: #FFFFFF; --ink: #0A2540; --ink-2: #51617A;
  --line: #E2E8F0; --accent: #4338CA; --accent-strong: #4338CA;
}
[data-page="doc"][data-theme="night"] {
  --bg: #0B1220; --bg-panel: #0E1729; --ink: #E7EBF4; --ink-2: #9DA9BF;
  --line: #1E2A42; --accent: #A78BFA; --accent-strong: #7C3AED;
}
[data-page="doc"], [data-page="doc"] * { margin: 0; padding: 0; box-sizing: border-box; }
[data-page="doc"] body {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  background: var(--bg); color: var(--ink); line-height: 1.65; font-size: 16px;
  -webkit-font-smoothing: antialiased; transition: background 0.25s ease, color 0.25s ease;
}
[data-page="doc"] .wrap { max-width: 46rem; margin: 0 auto; padding: 0 1.5rem; }
[data-page="doc"] header {
  border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 10;
  background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(8px);
}
[data-page="doc"] header .hwrap { max-width: 68rem; margin: 0 auto; padding: 0 1.5rem; display: flex; align-items: center; justify-content: space-between; height: 3.5rem; }
[data-page="doc"] .mark-sm { font-weight: 600; font-size: 1.02rem; letter-spacing: -0.01em; color: var(--ink); text-decoration: none; }
/* THE LOCKUP. "Exhibit" set in Literata with the final A living inside the tab — the mark IS the
   last letter of the name, not a second A standing beside it. This replaces the 3px underline the
   A used to carry, and that rule is DELETED rather than overridden: `.mark-sm .a` also matches the
   A inside the tab, so an underline left in place would have drawn a line through the mark.
   The tab deliberately overshoots cap height — the folder notch sits above the letters. That is the
   approved drawing, not a vertical-alignment bug, and it is why .lockup aligns on flex-end. */
[data-page="doc"] .lockup { display: flex; align-items: flex-end; }
[data-page="doc"] .word { font-family: 'Literata', Georgia, serif; font-weight: 700; font-size: 28px; line-height: 1; color: var(--ink); letter-spacing: -0.01em; }
/* --accent-strong, NOT --accent, and this is the one deliberate departure from the proof's own
   text. In LIGHT the two tokens hold the same value (#4338CA), so the approved drawing is
   reproduced exactly. In DARK, --accent is #A78BFA — the accent TEXT tint — which would render the
   tab a pale lilac; --accent-strong is the token that already carries the dark FILL, #7C3AED, which
   is the value the brief requires the tab to resolve to. No new token, nothing hard-coded. */
/* The page-scoped tab: SIZE AND POSITION ONLY since the mark went vector on 22 Aug 2026.
   The tile fill, the ::before folder tab and the Literata letter are all drawn by the SVG
   now, from the same geometry the app renders — so the two surfaces cannot draw two marks,
   which they did for one day in August. The 7px lift is the approved drawing and stays. */
[data-page="doc"] .tab { margin-left: .20em; margin-bottom: 7px; width: 21px; height: 21px; display: flex; align-items: center; justify-content: center; }
[data-page="doc"] .nav-right { display: flex; align-items: center; gap: var(--btn-gap); }
[data-page="doc"] .theme-btn {
  background: none; border: none; color: var(--ink-2); font: inherit; font-size: 0.78rem;
  padding: 0.3rem 0.4rem; cursor: pointer; text-decoration: underline dotted; text-underline-offset: 3px;
}
[data-page="doc"] .theme-btn:hover { color: var(--ink); }
/* ==========================================================================
   THE BUTTON STANDARD — one shape, one gap, both scopes. Sean's ruling,
   27 August 2026. The site had three radii doing the same job: 0.55rem on
   .btn, 0.5rem on .nav-toggle and a full pill on .feat-tab. The pill wins —
   it is the app's own language and the shape that survives a phone screen
   without looking cramped — and it is a TOKEN so a fourth radius cannot be
   introduced by a section deciding for itself. --btn-gap is the matching
   rule for rows of buttons: nothing may touch.
   ========================================================================== */
[data-page] {
  --btn-radius: 999px;
  --btn-gap: 0.75rem;
}
/* Any row of two or more buttons. Flex so the gap applies in both axes when
   the row wraps on a phone — the exam-platform pair stacks with the same
   clear space it has side by side. */
[data-page] .btn-row { display: flex; flex-wrap: wrap; gap: var(--btn-gap); }

[data-page="doc"] .btn {
  display: inline-block; background: var(--accent-strong); color: #fff; font-weight: 600;
  font-size: 0.85rem; text-decoration: none; padding: 0.45rem 0.95rem;
  border-radius: var(--btn-radius);
}
[data-page="doc"] main { padding: 3.5rem 0 4.5rem; }
[data-page="doc"] .kicker {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--accent); margin-bottom: 0.8rem;
}
[data-page="doc"] h1 { font-size: 1.9rem; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 2rem; }
[data-page="doc"] .block { margin-bottom: 1.6rem; color: var(--ink-2); }
[data-page="doc"] .block strong { color: var(--ink); font-weight: 700; }
[data-page="doc"] .block a { color: var(--accent); font-weight: 600; text-decoration: none; border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent); }
[data-page="doc"] .blank {
  font-style: italic; color: var(--ink-2);
  border-bottom: 1px dotted var(--ink-2); padding-bottom: 1px;
}
[data-page="doc"] footer { border-top: 1px solid var(--line); padding: 2.5rem 0 3rem; }
[data-page="doc"] .foot { max-width: 68rem; margin: 0 auto; padding: 0 1.5rem; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1rem; }
[data-page="doc"] .foot-links { display: flex; flex-wrap: wrap; gap: 1.5rem; }
[data-page="doc"] .foot-links a { color: var(--ink-2); text-decoration: none; font-size: 0.9rem; }
[data-page="doc"] .foot-links a:hover { color: var(--ink); }
[data-page="doc"] .foot-brand { color: var(--ink-2); font-size: 0.85rem; }
/* The cross-subject signpost (relay of 21 October 2026), written by scripts/subjects-footers.mjs. Its own line in the footer row; absent while it has nothing to say. */
[data-page="doc"] .foot-subjects { flex-basis: 100%; margin: 0; color: var(--ink-2); font-size: 0.9rem; }
[data-page="doc"] .foot-subjects a { color: var(--accent); text-decoration: none; font-weight: 600; }
[data-page="doc"] .foot-subjects a:hover { text-decoration: underline; }

/* ==========================================================================
   ARTICLE TYPOGRAPHY — NEW, and nothing uses it yet.
   For the resource pages. Every rule is scoped to a `.article` wrapper that
   exists on no page currently in the repo, so this section is provably inert
   on limits, terms, privacy and refunds: those four style their prose with
   `.block` and carry no h2, h3, list, quote, table or figure at all.
   Opt in by wrapping the prose: <div class="wrap"><div class="article">…
   ========================================================================== */

[data-page="doc"] .article { color: var(--ink-2); }
[data-page="doc"] .article > * + * { margin-top: 1.15rem; }

/* Headings take their space from the element ABOVE them, which is why the margin-top here
   overrides the flow rule rather than sitting beside it. A heading needs more air before it
   than a paragraph does; it needs none of its own after. */
/* THE HEADING SCALE, STATED IN ONE PLACE.
   Four tiers, each a clear step from the one below, and each with space above proportional to the
   break it marks. The h3 was the problem: at 1.08rem against a 1rem body it was barely a step at
   all, so a run of sub-sections read as bold paragraphs. Card headings (callout, toggle summary)
   share ONE size deliberately — they are the same kind of object and were 1.02 and 1.02 against a
   1.08 h3, which put a card heading below a body sub-heading for no reason.

     h2.major  1.55rem   page-level break (the export's mid-document h1s)
     h2        1.35rem   section
     h3        1.15rem   sub-section
     card      1.08rem   callout heading, toggle summary                                        */
[data-page="doc"] .article h2 {
  color: var(--ink); font-size: 1.35rem; font-weight: 700; letter-spacing: -0.015em;
  line-height: 1.3; margin-top: 2.6rem;
}
[data-page="doc"] .article h3 {
  color: var(--ink); font-size: 1.15rem; font-weight: 700; letter-spacing: -0.01em;
  line-height: 1.4; margin-top: 2rem;
}
[data-page="doc"] .article h2:first-child, [data-page="doc"] .article h3:first-child { margin-top: 0; }
[data-page="doc"] .article strong { color: var(--ink); font-weight: 700; }
/* `:not(.btn)` IS LOAD-BEARING — see the button rule at the end of this sheet.
   Without it this selector (0,2,1) outranks `.btn` (0,2,0) by one point, repaints the button's
   label in the link colour, and in LIGHT that colour is identical to the button's own background:
   an invisible label on a solid pill. It also hung a border across the bottom of the pill. */
[data-page="doc"] .article a:not(.btn) {
  color: var(--accent); font-weight: 600; text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
}
[data-page="doc"] .article a:not(.btn):hover { border-bottom-color: var(--accent); }

/* The `*` reset zeroes list padding, so the indent is restored here deliberately rather than
   inherited from the browser default — which differs between engines and would drift. */
[data-page="doc"] .article ul, [data-page="doc"] .article ol { padding-left: 1.4rem; }
[data-page="doc"] .article li + li { margin-top: 0.5rem; }
[data-page="doc"] .article li::marker { color: var(--ink-2); }

[data-page="doc"] .article blockquote {
  border-left: 3px solid var(--accent);
  padding: 0.15rem 0 0.15rem 1.15rem;
  color: var(--ink); font-size: 1.03rem;
}

/* Tables scroll inside their own wrapper rather than widening the page: a table that overruns
   its column takes the whole document sideways on a phone, and there is no width at which
   exam-shaped tabular content is guaranteed to fit. Wrap: <div class="table-wrap"><table>… */
[data-page="doc"] .table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
[data-page="doc"] .article table { border-collapse: collapse; width: 100%; font-size: 0.95rem; }
[data-page="doc"] .article th, [data-page="doc"] .article td {
  border: 1px solid var(--line); padding: 0.6rem 0.75rem; text-align: left; vertical-align: top;
}
[data-page="doc"] .article th { color: var(--ink); font-weight: 700; background: color-mix(in srgb, var(--ink) 4%, var(--bg-panel)); }

[data-page="doc"] .article figure { margin-top: 1.9rem; }
[data-page="doc"] .article figure img { display: block; width: 100%; height: auto; border: 1px solid var(--line); border-radius: 0.7rem; }
[data-page="doc"] .article figcaption { margin-top: 0.65rem; color: var(--ink-2); font-size: 0.88rem; }

/* 16:9 embed. aspect-ratio on the box, absolute fill on the iframe: the iframe is sized by the
   box, so the row cannot collapse to zero height before the frame loads and cannot be resized
   from inside it. Use: <div class="video-embed"><iframe …></iframe></div> */
[data-page="doc"] .video-embed {
  position: relative; aspect-ratio: 16 / 9; width: 100%;
  background: var(--bg-panel); border: 1px solid var(--line);
  border-radius: 0.7rem; overflow: hidden;
}
[data-page="doc"] .video-embed iframe {
  position: absolute; inset: 0; width: 100%; height: 100%; border: 0;
}
/* .article spaces its OWN children; .wrap does not space its children at all, and the `*` reset
   has already zeroed the margins a browser would otherwise supply. So an embed sitting between
   the h1 and the prose has to state the gap beneath it, or the first paragraph butts against the
   player. Caught by looking at the rendered template, not at the rule. */
[data-page="doc"] .video-embed + .article { margin-top: 2rem; }

/* ==========================================================================
   ABOUT AND RESOURCES — NEW, and deliberately provisional.
   These two pages are being built in parallel with the identity work, so this
   section invents no colour, no type scale and no page width: it lays both
   pages out with the tokens already declared above, at the same 46rem measure
   limits/terms/privacy/refunds have always used. It is expected to be replaced
   when the chosen identity lands. Nothing above depends on it.
   ========================================================================== */

/* About: the portrait sits beside the opening statement, and is bottom-aligned
   to it — the cutout has a flush opaque base, and standing it on the baseline is
   what stops it reading as a floating crop. Same reasoning as the homepage
   portrait it replaces. */
[data-page="doc"] .about-top { display: grid; grid-template-columns: minmax(0, 1fr) 13rem; gap: 2.5rem; align-items: start; }
[data-page="doc"] .about-top h1 { margin-bottom: 0; }
[data-page="doc"] .about-portrait img { display: block; width: 100%; height: auto; }
[data-page="doc"] .about-top + .article { margin-top: 2.5rem; }
@media (max-width: 47rem) {
  [data-page="doc"] .about-top { grid-template-columns: 1fr; gap: 1.8rem; }
  [data-page="doc"] .about-portrait { justify-self: center; width: 11rem; max-width: 100%; }
}

/* End-of-page bands. Panels, not banners: each carries one control and no copy
   of its own, so nothing here has to be worded before the design pass. */
[data-page="doc"] .bands { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.25rem; margin-top: 3.5rem; }
[data-page="doc"] .cta {
  background: var(--bg-panel); border: 1px solid var(--line); border-radius: 0.9rem;
  padding: 1.6rem 1.5rem; display: flex; align-items: center; justify-content: center; text-align: center;
}
/* Three routes instead of two, for the end of an article. A modifier rather than a change to
   .bands, so about.html's two-band row is provably untouched by it. */
[data-page="doc"] .bands-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 47rem) {
  [data-page="doc"] .bands { grid-template-columns: 1fr; }
  [data-page="doc"] .bands-3 { grid-template-columns: 1fr; }
}

/* Resource cards. Two up at this measure, never three — at 46rem a third column
   leaves each card too narrow to hold a title of this length on fewer than four
   lines, and the titles are the whole content of the card. */
[data-page="doc"] .cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.25rem; }
[data-page="doc"] .card {
  background: var(--bg-panel); border: 1px solid var(--line); border-radius: 0.9rem;
  padding: 1.4rem 1.4rem 1.2rem; display: flex; flex-direction: column; gap: 0.9rem;
}
[data-page="doc"] .card h2 { color: var(--ink); font-size: 1.02rem; font-weight: 700; line-height: 1.35; letter-spacing: -0.01em; }
/* margin-top:auto pins the status to the bottom edge, so a two-line title and a
   four-line title still align their status rows across the row. */
[data-page="doc"] .card .status {
  margin-top: auto; color: var(--ink-2); font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
}
/* The case analysis is not one of the series — it is the worked reference the
   series points at, so it takes the accent edge and the full row. */
[data-page="doc"] .card.reference { grid-column: 1 / -1; border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
[data-page="doc"] .card.reference .status { color: var(--accent); }
/* Unwritten, and it should look unwritten. */
[data-page="doc"] .card.todo { border-style: dashed; background: none; }
[data-page="doc"] .card.todo h2 { color: var(--ink-2); font-weight: 600; }
@media (max-width: 40rem) {
  [data-page="doc"] .cards { grid-template-columns: 1fr; }
}

/* ==========================================================================
   HOMEPAGE — index.html only.
   ========================================================================== */

[data-page="home"], [data-page="home"][data-theme="light"] {
  --bg: #F6F8FA;
  --bg-raised: #FFFFFF;
  --bg-panel: #FFFFFF;
  --ink: #0A2540;
  --ink-2: #51617A;
  --lede: #3D4E68;
  --line: #E2E8F0;
  --accent: #4338CA;
  --accent-strong: #4338CA;
  --strap: #44427A;
  --hero-glow: radial-gradient(ellipse 70% 55% at 50% -12%, rgba(67, 56, 202, 0.09), transparent 70%);
}
[data-page="home"][data-theme="night"] {
  --bg: #0B1220;
  --bg-raised: #101A2E;
  --bg-panel: #0E1729;
  --ink: #E7EBF4;
  --ink-2: #9DA9BF;
  --lede: #C7D0DE;
  --line: #1E2A42;
  --accent: #A78BFA;
  --accent-strong: #7C3AED;
  --strap: #A78BFA;
  --hero-glow: radial-gradient(ellipse 70% 55% at 50% -12%, rgba(124, 58, 237, 0.16), transparent 70%);
}
[data-page="home"], [data-page="home"] * { margin: 0; padding: 0; box-sizing: border-box; }
[data-page="home"] { scroll-behavior: smooth; }
[data-page="home"] body {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  background: var(--bg);
  color: var(--ink);
  line-height: 1.6;
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
  transition: background 0.25s ease, color 0.25s ease;
}
[data-page="home"] .num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }
[data-page="home"] .wrap { max-width: 68rem; margin: 0 auto; padding: 0 1.5rem; }

/* ---------- header ---------- */
[data-page="home"] header {
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 10;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(8px);
}
[data-page="home"] .nav { display: flex; align-items: center; justify-content: space-between; height: 3.75rem; }
[data-page="home"] .mark-sm { font-weight: 600; font-size: 1.02rem; letter-spacing: -0.01em; color: var(--ink); text-decoration: none; }
/* THE LOCKUP, second copy. Identical to the document-scope block above, prefix apart — see there
   for why the underline rule is deleted and why the tab reads --accent-strong. The two halves of
   this stylesheet each carry their own mark rules, so the mark changes in two places or in none. */
[data-page="home"] .lockup { display: flex; align-items: flex-end; }
[data-page="home"] .word { font-family: 'Literata', Georgia, serif; font-weight: 700; font-size: 28px; line-height: 1; color: var(--ink); letter-spacing: -0.01em; }
[data-page="home"] .tab { margin-left: .20em; margin-bottom: 7px; width: 21px; height: 21px; display: flex; align-items: center; justify-content: center; }
[data-page="home"] .nav-right { display: flex; align-items: center; gap: var(--btn-gap); }
[data-page="home"] .theme-btn {
  background: none; border: none; color: var(--ink-2);
  font: inherit; font-size: 0.78rem; padding: 0.3rem 0.4rem;
  cursor: pointer; transition: color 0.15s; text-decoration: underline dotted;
  text-underline-offset: 3px;
}
[data-page="home"] .theme-btn:hover { color: var(--ink); }
[data-page="home"] .btn {
  display: inline-block; background: var(--accent-strong); color: #fff;
  font-weight: 600; font-size: 0.95rem; text-decoration: none;
  padding: 0.55rem 1.15rem; border-radius: var(--btn-radius);
  transition: filter 0.15s;
}
[data-page="home"] .btn:hover { filter: brightness(1.12); }
[data-page="home"] .btn-sm { font-size: 0.85rem; padding: 0.45rem 0.95rem; }

/* ---------- hero ---------- */
/* Split grid. The wash is the existing accent-tinted --hero-glow token and nothing else —
   no shapes, no second surface — so the hero reads as the page, lit, rather than as a box. */
/* THE PARK BAND — the dusk photograph behind the hero and the facts strip, NIGHT IN BOTH THEMES.
   Re-ruled 27 August 2026 after the first cut was seen live: a light veil over a dark photograph
   made fog, not atmosphere, and dark ink over a half-visible photo sat at the edge of illegible.
   So the band pins the night tokens locally — every child (headline, sub, pills, borders) renders
   the night way whatever theme the page is in — and the scrim's LAST stop is the page's own
   background token, which is the whole hand-off: from the trust pills down, the page is simply
   whichever theme the visitor chose. The mid stop is deliberately light so the wheel and coaster
   read clearly through the hero's empty right half. */
/* THE PHOTOGRAPH CAME OUT ON 19 OCTOBER 2026 (Figma 389:21). The band is now plain navy with a soft
   indigo glow behind the reviews column, and the scrims that existed only to make text legible over
   the sunset are gone with it. Everything else this block records still holds: night in both
   themes, tokens pinned locally, and the hand-off to the page's own --bg after the pills.
   The review cards in the band read --bg-panel and the --rv-* tokens, so those are pinned to their
   night values here too; without that, the light theme would draw white cards on the navy band. */
[data-page="home"] .hero-park {
  position: relative;
  isolation: isolate;
  background:
    radial-gradient(ellipse 42% 58% at 76% 40%, rgba(79, 70, 229, 0.22), rgba(79, 70, 229, 0) 72%),
    radial-gradient(ellipse 30% 40% at 0% 45%, rgba(124, 58, 237, 0.10), rgba(124, 58, 237, 0) 70%),
    #0B1220;
  --ink: #E7EBF4;
  --ink-2: #9DA9BF;
  --lede: #C7D0DE;
  --line: #1E2A42;
  --accent: #A78BFA;
  --accent-strong: #7C3AED;
  --strap: #A78BFA;
  --bg-panel: #0E1729;
  --rv-border: #3730A3;
  --rv-star: #00B67A;
}
/* THE HAND-OFF HAPPENS AFTER THE PILLS — Sean's seam ruling, 27 August 2026. The first cut faded
   to the page background THROUGH the facts row, so in light mode the pills sat on a nearly-white
   backdrop under night-light text: grey ghosts, legible in neither theme. Now the scrim is solid
   night from below the fold through the whole pills row (the calc stop), and the band's own
   padding-bottom is the fade's room — the transition to the visitor's page happens entirely after
   the last pill, eased through a mixed middle stop so it reads as sky darkening into page rather
   than a band of grey. No element straddles the fade. */
[data-page="home"] .hero-park { padding-bottom: 6rem; }
/* ONLY THE HAND-OFF IS LEFT. The two scrims (4 September) existed to carry text over the sunset
   photograph — the horizontal one measured the accent line at 1.23:1 without it. With the
   photograph gone there is nothing to scrim, so what remains is the 27 August seam exactly as
   ruled: solid night through the pills, then eased into the page's own --bg after the last one. */
[data-page="home"] .hero-park::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background: linear-gradient(
    to bottom,
    rgba(11, 18, 32, 0) calc(100% - 5rem),
    color-mix(in srgb, #0B1220 40%, var(--bg)) calc(100% - 2.25rem),
    var(--bg) 100%
  );
}
[data-page="home"] .hero-park > * { position: relative; z-index: 1; }
[data-page="home"] .hero-park .facts-bar {
  border-color: color-mix(in srgb, #1E2A42 55%, transparent);
}
/* NOTE: the band overrides ink/line/accent tokens but deliberately NOT --bg, so the ::before's
   final `var(--bg)` stop resolves from the page root — that inheritance IS the theme hand-off. */

/* THE PILLS CARRY THEIR OWN BACKING — Sean's ruling, 27 August 2026, after two passes that tuned
   the gradient and still failed. The lesson is in the method: the previous fix measured GEOMETRY
   (the pills sat 16px above the fade at every width, true at every width) when the property that
   mattered was LEGIBILITY (the fade was already lifting the background at that row, so in light
   mode pale text composited onto near-white). A trust bar over photography does not borrow its
   contrast from a scrim it cannot see: each pill is its own chip, so the row is immune to whatever
   the photograph or the fade does behind it, in both themes, at every width. */
[data-page="home"] .hero-park .facts-bar { border-top: 0; border-bottom: 0; }
[data-page="home"] .hero-park .facts { gap: 0.6rem; }
[data-page="home"] .hero-park .fact,
[data-page="home"] .hero-park .fact + .fact {
  background: rgba(8, 13, 24, 0.82);
  border: 1px solid rgba(231, 235, 244, 0.26);
  border-radius: 999px;
  padding: 0.8rem 0.85rem;
  color: #FFFFFF;
}
/* (The free line went full white to hold over a bright patch of the photograph. With the photograph
   gone it is back on its own --ink-2, as Figma 389:21 draws it: 6.9:1 on the navy band.) */

/* (The phone's own sunset portrait and its flat scrim came out with the photograph, 19 Oct 2026.) */

[data-page="home"] .hero {
  position: relative;
  padding: 4.25rem 0 4.5rem;
}
/* Copy left, reviews right (Figma 389:21). The columns are near-equal now that the right one
   carries content rather than a window onto a photograph.
   TOP-ALIGNED, NOT CENTRED. With Jiwan's lead card the reviews column is the taller one, and
   centring pulled the copy down with it: Sign in measured 744px at 1280x720, under the fold. From
   the top, the copy sits where it always has and the button stays on the first screen. */
[data-page="home"] .hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: 3.5rem;
  align-items: start;
}
/* The short violet rule under the headline, from the frame. Decoration only: aria-hidden. */
[data-page="home"] .hero-rule { width: 88px; height: 3px; border-radius: 2px; background: var(--accent-strong); margin-top: 1.4rem; }
/* min-width:0 is load-bearing on a grid child: without it the column takes its
   max-content width from the nowrap headline and pushes the row past the page. */
[data-page="home"] .hero-copy { min-width: 0; max-width: 35rem; }
[data-page="home"] .hero-h1 {
  font-weight: 800;
  /* Two clamps, not one. The headline sets against two different measures — the full page
     width on a phone, a ~560px column on a desktop — and a single curve that suits one
     leaves the other either wrapping a sentence across two lines or overrunning its column.
     This is the desktop curve; the phone curve is in the max-width block below. */
  font-size: clamp(1.75rem, 3.6vw, 2.875rem);
  line-height: 1.12;
  letter-spacing: -0.02em;
  color: var(--ink);
}
/* Each sentence is its own block, so the three lines can never share one — on any
   width. nowrap is the desktop refinement on top of that, not the mechanism. */
[data-page="home"] .hero-h1 .l { display: block; }
[data-page="home"] .hero-h1 .marked { color: var(--accent); }
[data-page="home"] .hero-sub {
  margin-top: 1.25rem;
  color: var(--lede);
  font-size: 1.05rem;
  max-width: 32rem;
}
[data-page="home"] .hero-actions {
  margin-top: 1.9rem;
  display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 1.15rem;
}
[data-page="home"] .hero-actions .btn { font-size: 1.02rem; padding: 0.8rem 1.7rem; }
/* "Sit a free mock", 20 October 2026 (record § A37): the hero's second button, an outline so Sign in
   stays the primary. The band is navy in both themes, so the label stays the white every a.btn forces;
   only the fill goes, replaced by a violet ring the width of the lessons card's outline button. */
[data-page="home"] .hero-actions .btn.btn-outline {
  background: transparent; box-shadow: inset 0 0 0 1.5px var(--accent);
}
[data-page="home"] .hero-actions .btn.btn-outline:hover { filter: none; background: color-mix(in srgb, var(--accent) 18%, transparent); }
[data-page="home"] .hero-free { color: var(--ink-2); font-size: 0.95rem; }

[data-page="home"] .hero-visual { min-width: 0; }
/* The inset sits inside this padding, so the caption below the group is never overlapped. */
[data-page="home"] .hero-stack { position: relative; padding-bottom: 4rem; }
/* Browser-style chrome. The dots are neutral ink by design — green/red/amber are reserved
   for meaning in this product and are never page decoration. */
[data-page="home"] .frame {
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 0.7rem;
  overflow: hidden;
  box-shadow: 0 18px 40px -24px rgba(10, 37, 64, 0.35);
}
[data-page="home"][data-theme="night"] .frame { box-shadow: 0 18px 40px -20px rgba(0, 0, 0, 0.6); }
[data-page="home"] .frame-bar {
  display: flex; align-items: center; gap: 0.35rem;
  padding: 0.5rem 0.7rem;
  border-bottom: 1px solid var(--line);
  background: color-mix(in srgb, var(--ink) 4%, var(--bg-panel));
}
[data-page="home"] .frame-dot {
  width: 0.5rem; height: 0.5rem; border-radius: 50%;
  background: color-mix(in srgb, var(--ink-2) 45%, transparent);
}
[data-page="home"] .frame img { display: block; width: 100%; height: auto; }
[data-page="home"] .hero-inset {
  position: absolute; right: -0.5rem; bottom: 0;
  width: 52%;
}
[data-page="home"] .hero-caption {
  margin-top: 1.1rem;
  color: var(--ink-2);
  font-size: 0.88rem;
}
@media (max-width: 44rem) {
  [data-page="home"] .hero { padding: 2.6rem 0 3.2rem; }
  [data-page="home"] .hero-grid { grid-template-columns: 1fr; gap: 2.2rem; }
  [data-page="home"] .hero-copy { max-width: none; }
  /* Phone curve: tracks the viewport hard enough that the longest sentence still holds one
     line at 360px, where the desktop curve's floor would have broken it across two. */
  [data-page="home"] .hero-h1 { font-size: clamp(1.35rem, 6.6vw, 2.4rem); }
  [data-page="home"] .hero-h1 .l { white-space: normal; }
  [data-page="home"] .hero-actions { margin-top: 1rem; gap: 0.9rem; }
  /* SIGN IN ABOVE THE FOLD AT 375x667, required by the 19 October round. Measured: the button's
     bottom edge sat at 680px on the live page (already 13px under a 667px screen) and at 705px once
     the violet rule went in. Tightening the hero's top padding and the gaps around the rule and the
     two sub-lines brings it back above the fold; the words are untouched. */
  [data-page="home"] .hero { padding-top: 1.25rem; }
  [data-page="home"] .hero-rule { margin-top: 0.9rem; }
  [data-page="home"] .hero-sub { margin-top: 0.75rem; }
  [data-page="home"] .hero-actions .btn { display: block; width: 100%; text-align: center; }
  /* THE TWO BUTTONS STACK ON A PHONE (20 October 2026). Side by side was tried first: at 375x667
     both fit above the fold, but the fixed "Ask A" launcher sat over 36% of "Sit a free mock" and
     cut its label to "Sit a f". Stacked, Sign in keeps the first screen and the second button
     sits just under it, clear of the launcher. */
  [data-page="home"] .hero-free { width: 100%; }
  [data-page="home"] .hero-stack { padding-bottom: 3rem; }
  [data-page="home"] .hero-inset { right: 0; }
}
/* nowrap starts at 48rem, not at the 44rem column break. Between the two the grid is
   already split but the column is still narrower than the longest sentence at the clamp's
   floor size — holding one line there would push the page sideways. The sentences are
   block-level regardless, so they never share a line at any width; nowrap is the desktop
   refinement on top of that guarantee, applied only where it actually fits. */
@media (min-width: 48rem) {
  [data-page="home"] .hero-h1 .l { white-space: nowrap; }
}

/* ---------- facts bar ---------- */
[data-page="home"] .facts-bar { border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
[data-page="home"] .facts {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
[data-page="home"] .fact {
  padding: 1.15rem 1rem;
  display: flex; align-items: center; justify-content: center; gap: 0.55rem;
  text-align: center;
  font-size: 0.95rem; font-weight: 600;
  color: var(--ink);
}
[data-page="home"] .fact + .fact { border-left: 1px solid var(--line); }
@media (max-width: 47rem) {
  [data-page="home"] .facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  [data-page="home"] .fact { padding: 0.95rem 0.75rem; font-size: 0.88rem; gap: 0.4rem; }
  [data-page="home"] .fact:nth-child(3) { border-left: none; }
  [data-page="home"] .fact:nth-child(n+3) { border-top: 1px solid var(--line); }
}

/* ---------- icons ---------- */
/* Lucide line icons, pasted inline — no CDN, no sprite, no font, so the page still makes
   zero external requests. Each keeps its Lucide stroke attributes verbatim and draws in
   currentColor; only the fixed 24px width/height are dropped so `em` sizing can track the
   type beside it. Muted ink, never the accent: these are decorative markers next to a
   label, and colouring them the accent would give them the weight of a link. */
[data-page="home"] .icon {
  flex: none;
  width: 1.05em; height: 1.05em;
  color: var(--ink-2);
  vertical-align: -0.15em;
}
[data-page="home"] .beat .icon { width: 1.25rem; height: 1.25rem; }
[data-page="home"] .beat-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1rem; }

/* ---------- section scaffolding ---------- */
[data-page="home"] section { padding: 4.75rem 0; border-top: 1px solid var(--line); }
[data-page="home"] section.flush { border-top: none; }
@media (max-width: 47rem) { [data-page="home"] section { padding: 3.2rem 0; } }
[data-page="home"] .kicker {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--accent); margin-bottom: 0.9rem;
}
[data-page="home"] h2 {
  font-size: clamp(1.55rem, 2.6vw, 2rem);
  font-weight: 800; letter-spacing: -0.02em; line-height: 1.2;
  margin-bottom: 2.2rem;
}

/* ---------- three beats ---------- */
[data-page="home"] .beats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.25rem; }
[data-page="home"] .beat {
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 0.9rem;
  padding: 1.7rem 1.5rem;
}
[data-page="home"] .step-n {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.75rem; height: 1.75rem; border-radius: 50%;
  font-size: 0.82rem; font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
}
/* the number now sits in .beat-head, which carries the spacing below the row */
[data-page="home"] .beat h3 { font-size: 1.05rem; font-weight: 700; margin-bottom: 0.5rem; }
[data-page="home"] .beat p { color: var(--ink-2); font-size: 0.95rem; }
[data-page="home"] .beat p.beat-note { margin-top: 0.75rem; font-size: 0.9rem; }
@media (max-width: 47rem) {
  [data-page="home"] .beats { grid-template-columns: 1fr; gap: 0; }
  [data-page="home"] .beat { border-radius: 0; border-bottom: none; padding: 1.3rem 1.4rem; }
  [data-page="home"] .beat:first-child { border-radius: 0.9rem 0.9rem 0 0; }
  [data-page="home"] .beat:last-child { border-radius: 0 0 0.9rem 0.9rem; border-bottom: 1px solid var(--line); }
  [data-page="home"] .beat + .beat { border-top: 1px solid var(--line); }
}

/* ---------- see it working ---------- */
/* Two screenshots per row, one per theme, both in the DOM and switched by the
   same data-theme attribute the palette above keys off. The light file is the
   default so a page with no stored choice — or with the head script blocked —
   shows the theme the markup ships in. */
[data-page="home"] .shots { display: grid; gap: 4rem; }
[data-page="home"] .shot {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr);
  gap: 2.8rem;
  align-items: center;
  opacity: 0;
  transform: translateY(0.75rem);
  transition: opacity 500ms ease, transform 500ms ease;
}
[data-page="home"] .shot.in { opacity: 1; transform: none; }
/* Alternate rows put the image on the left. The column widths mirror with them, so
   the screenshot keeps the wider half of the row whichever side it sits on. */
[data-page="home"] .shot:nth-child(even) { grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); }
[data-page="home"] .shot:nth-child(even) .shot-copy { grid-column: 2; grid-row: 1; }
[data-page="home"] .shot:nth-child(even) .shot-frame { grid-column: 1; grid-row: 1; }
/* The command-word panel is the one exception. It is a small upright panel, not a screen capture,
   and stretching it to a 563px column would blow it up past its own resolution — so it keeps the
   narrower column, sits at natural size and centres in it. Everything else about the row — side,
   theme swap, fade — is unchanged. */
[data-page="home"] .shot.shot-verbs { grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr); }
[data-page="home"] .shot-verbs .shot-frame { display: flex; justify-content: center; }
[data-page="home"] .shot-verbs .shot-frame img { width: auto; max-width: 100%; }
[data-page="home"] .shot-copy .step-n { margin-bottom: 0.85rem; }
[data-page="home"] .shot h3 { font-size: 1.25rem; font-weight: 700; letter-spacing: -0.01em; margin-bottom: 0.6rem; }
[data-page="home"] .shot p { color: var(--ink-2); font-size: 1rem; }
[data-page="home"] .shot-frame img {
  display: block; width: 100%; height: auto;
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 0.8rem;
}
[data-page="home"] .shot-night, [data-page="home"][data-theme="light"] .shot-night { display: none; }
[data-page="home"][data-theme="night"] .shot-light { display: none; }
[data-page="home"][data-theme="night"] .shot-night { display: block; }
@media (max-width: 47rem) {
  [data-page="home"] .shots { gap: 2.8rem; }
  [data-page="home"] .shot, [data-page="home"] .shot:nth-child(even) { grid-template-columns: 1fr; gap: 1.2rem; }
  [data-page="home"] .shot .shot-copy, [data-page="home"] .shot:nth-child(even) .shot-copy,
  [data-page="home"] .shot .shot-frame, [data-page="home"] .shot:nth-child(even) .shot-frame { grid-column: 1; grid-row: auto; }
}
@media (prefers-reduced-motion: reduce) {
  [data-page="home"] .shot { opacity: 1; transform: none; transition: none; }
}

/* ---------- credibility band ---------- */
/* The band OPPOSES the page theme: dark surface on the light page, light surface on the dark
   one. Either way it carries its OWN fixed surface tokens and never inherits the page
   background — that is the structural guarantee, and it is what the opposition is built on
   top of, not a thing the opposition replaces. A band that derives its colours from the
   ambient theme is exactly how the mock shipped white-on-white in one of them; here each
   theme names a complete, self-consistent set, so ink and surface can never drift apart.
   Both sets are the repo's own palette values — the light-theme band below is the night
   palette, the night-theme band is the light palette, swapped whole rather than picked from. */
[data-page="home"] .band {
  --band-bg: #0B1220;
  --band-ink: #E7EBF4;
  --band-ink-2: #9DA9BF;
  --band-accent: #A78BFA;
  background: var(--band-bg);
  color: var(--band-ink);
  border-top: none;
}
/* Night page, light band. Only the five tokens are restated — every rule below reads through
   them, so the opposition costs no duplicated markup and no second set of selectors. */
[data-page="home"][data-theme="night"] .band {
  --band-bg: #F6F8FA;
  --band-ink: #0A2540;
  --band-ink-2: #51617A;
  --band-accent: #4338CA;
}
[data-page="home"] .band-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 3rem; }
[data-page="home"] .band .kicker { color: var(--band-accent); }
[data-page="home"] .band p { color: var(--band-ink-2); font-size: 1rem; }
[data-page="home"] .band p + p { margin-top: 0.9rem; }
[data-page="home"] .band a {
  color: var(--band-accent); font-weight: 600; text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--band-accent) 45%, transparent);
}
[data-page="home"] .band a:hover { border-bottom-color: var(--band-accent); }
@media (max-width: 47rem) {
  [data-page="home"] .band-grid { grid-template-columns: 1fr; gap: 2.2rem; }
}

/* ---------- who ---------- */
[data-page="home"] .who p { max-width: 42rem; color: var(--ink-2); font-size: 1.05rem; }
[data-page="home"] .who p strong { color: var(--ink); font-weight: 600; }

/* ---------- pricing ---------- */
[data-page="home"] .price-sub { margin: -1.6rem 0 2.2rem; color: var(--ink-2); font-size: 1.05rem; }
/* THE NEGATIVE TOP MARGIN ABOVE IS FOR THE SUBLINE THAT FOLLOWS THE HEADING, and it pulls it up
   under the <h2>. A SECOND .price-sub was added BELOW the pack cards on 8 August 2026 with the
   two-option pricing, inherited the same -1.6rem, and was dragged 25.6px INTO the cards: the
   sentence rendered straight across the £15 and £59 tiles, with the £59 card's border cutting
   through the words. Live on the homepage's pricing block, which is the most commercially important
   block on the site.
   Found by tools/visual-sweep.mjs (4 hits, both themes, both widths) and confirmed on a crop —
   which is the whole reason the sweep runs on every visual change. It reads as deliberate design at
   a glance, which is exactly why two rounds of human review had already passed it.
   Scoped with the adjacent sibling combinator rather than by adding a class, so the heading subline
   is untouched and no markup had to change. */
[data-page="home"] .packs + .price-sub { margin-top: 1.6rem; }
/* AND AGAIN, ONE SELECTOR OVER — 1 September 2026. The contrast line ("Ninety-nine answers are
   marked by the machine. Your mock is marked by me.") was inserted between `.price-cards` and the
   expiry, and the expiry's inherited -1.6rem promptly dragged it 25.6px INTO that sentence: the
   same overlap, the same distance, 4 hits across both themes and both widths.
   Caught by tools/visual-sweep.mjs and NOT by eye — the rendered page looked deliberate at a glance,
   which is what the note above says about the first occurrence and is why the sweep runs on every
   visual change. Same remedy and the same shape: an adjacent-sibling override, no new class on the
   paragraph and no markup change, so the heading's own subline is untouched. */
[data-page="home"] .free-line + .price-sub { margin-top: 1.6rem; }
[data-page="home"] .packs { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.25rem; max-width: 52rem; align-items: center; }
[data-page="home"] .pack {
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 0.9rem;
  padding: 1.8rem 1.5rem;
  text-align: center;
}
/* The middle pack is raised by the accent alone — no badge. (It was display-only until 8 Aug 2026;
   all three cards are now links to the same sign-in the button beside them uses.) */
[data-page="home"] .pack.mid {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 20px 44px -26px color-mix(in srgb, var(--accent) 60%, transparent);
  padding: 2.3rem 1.5rem;
}
[data-page="home"] .pack .count {
  font-size: 2.6rem; font-weight: 800; letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums; line-height: 1;
}
[data-page="home"] .pack.mid .count { color: var(--accent); }
[data-page="home"] .pack .label { color: var(--ink-2); font-size: 0.9rem; margin-top: 0.45rem; }
[data-page="home"] .pack .price {
  margin-top: 1.1rem; font-size: 1.35rem; font-weight: 700;
  color: var(--accent); font-variant-numeric: tabular-nums;
}
[data-page="home"] .free-line { margin-top: 1.9rem; color: var(--ink-2); font-size: 1rem; max-width: 44rem; }
[data-page="home"] .free-line strong { color: var(--ink); font-weight: 600; }
@media (max-width: 47rem) {
  [data-page="home"] .price-sub { margin: -1.4rem 0 1.8rem; }
  /* The same correction at phone width, where the cards stack and the collision was identical. */
  [data-page="home"] .packs + .price-sub { margin-top: 1.4rem; }
  /* And for the contrast line, which collided identically at 390. */
  [data-page="home"] .free-line + .price-sub { margin-top: 1.4rem; }
  [data-page="home"] .packs { grid-template-columns: 1fr; gap: 0.8rem; }
  [data-page="home"] .pack, [data-page="home"] .pack.mid { padding: 1.3rem 1.5rem; }
}

/* ---------- built by ---------- */
[data-page="home"] .builtby p { max-width: 46rem; color: var(--ink-2); font-size: 0.98rem; }

/* ---------- more than practice ---------- */
[data-page="home"] .more-grid { display: grid; grid-template-columns: minmax(0, 1fr) 18rem; gap: 3rem; align-items: start; }
[data-page="home"] .more p { max-width: 44rem; color: var(--ink-2); font-size: 1.05rem; }
[data-page="home"] .more p + p { margin-top: 1rem; }
[data-page="home"] .more p strong { color: var(--ink); font-weight: 600; }
[data-page="home"] .more a { color: var(--accent); font-weight: 600; text-decoration: none; border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent); }
[data-page="home"] .more a:hover { border-bottom-color: var(--accent); }

/* ---------- portrait ---------- */
/* Transparent cutout — one asset, both themes. Deliberately no border,
   radius, background or shadow: the flush opaque bottom edge is what makes
   it read as standing at the base of the section. */
[data-page="home"] .portrait {
  align-self: end;
  opacity: 0;
  transform: translateY(0.75rem);
  transition: opacity 500ms ease, transform 500ms ease;
}
[data-page="home"] .portrait.in { opacity: 1; transform: none; }
[data-page="home"] .portrait img { display: block; width: 100%; height: auto; }
@media (max-width: 47rem) {
  [data-page="home"] .more-grid { grid-template-columns: 1fr; gap: 2.2rem; }
  [data-page="home"] .portrait { justify-self: center; width: 14rem; max-width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  [data-page="home"] .portrait { opacity: 1; transform: none; transition: none; }
}

/* ---------- footer ---------- */
[data-page="home"] footer { border-top: 1px solid var(--line); padding: 2.5rem 0 3rem; }
[data-page="home"] .foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1rem; }
[data-page="home"] .foot-links { display: flex; flex-wrap: wrap; gap: 1.5rem; }
[data-page="home"] .foot-links a { color: var(--ink-2); text-decoration: none; font-size: 0.9rem; }
[data-page="home"] .foot-links a:hover { color: var(--ink); }
[data-page="home"] .foot-brand { color: var(--ink-2); font-size: 0.85rem; }
/* The cross-subject signpost (relay of 21 October 2026), written by scripts/subjects-footers.mjs. Its own line in the footer row; absent while it has nothing to say. */
[data-page="home"] .foot-subjects { flex-basis: 100%; margin: 0; color: var(--ink-2); font-size: 0.9rem; }
[data-page="home"] .foot-subjects a { color: var(--accent); text-decoration: none; font-weight: 600; }
[data-page="home"] .foot-subjects a:hover { text-decoration: underline; }
/* NON-AFFILIATION. Counsel's §VII.1 asks for this prominently on the main page, the terms and the
   payment page, and prominence here is deliberate: full ink rather than the muted footer grey,
   above the link row rather than trailing it, and its own rule. It is the cheapest risk reduction
   available on the trade mark point, so it is not treated as small print. */
[data-page="home"] .foot-disclaimer {
  color: var(--ink); font-size: 0.92rem; font-weight: 500;
  max-width: 46rem;
  margin-bottom: 1.5rem; padding-bottom: 1.4rem;
  border-bottom: 1px solid var(--line);
}

/* ==========================================================================
   HEADER NAVIGATION — deliberately NOT written twice.
   Every other block in this file is duplicated across [data-page="doc"] and
   [data-page="home"] because those two designs DISAGREE about the same
   selectors. The nav is new and identical on both, so it is scoped to
   [data-page] — which matches the html element of every page, home and
   document alike — and there is one copy to keep in step, not two.
   ========================================================================== */
[data-page] .nav-links { display: flex; align-items: center; gap: 1.4rem; }
[data-page] .nav-links a {
  color: var(--ink-2); text-decoration: none; font-size: 0.92rem; font-weight: 500;
  white-space: nowrap; transition: color 0.15s;
}
[data-page] .nav-links a:hover { color: var(--ink); }
/* Current page. The colour reads off aria-current, so the styling cannot drift
   from what a screen reader is told — one state, one source. */
[data-page] .nav-links a[aria-current="page"] { color: var(--accent); font-weight: 600; }
[data-page] .nav-toggle {
  display: none; background: none; border: 1px solid var(--line);
  border-radius: var(--btn-radius);
  color: var(--ink-2); font: inherit; font-size: 0.8rem; font-weight: 600;
  padding: 0.35rem 0.7rem; cursor: pointer;
}
[data-page] .nav-toggle:hover { color: var(--ink); }

@media (max-width: 66rem) {
  /* Below this the links leave the bar and become a panel under it. The panel takes
     an OPAQUE background of its own: the header is translucent and backdrop-blurred,
     so links inheriting that would sit on whatever happened to scroll beneath them.
     60rem, was 56rem: the nav gained its sixth item (Exam platform) and between 897px and
     ~956px the full row overflowed the bar by up to 54px, scrolling the whole page sideways —
     measured, not assumed. At 960px and up the header is exactly what it always was.
     66rem, was 60rem (21 October 2026, record § A38): "Mock exams" made seven items, and with the
     header button reading "Sign in" the row's first link met the logo at 1,052px (gap 0; 1px at
     1,055, 3px at 1,058) and the row overflowed its bar by 4px at 1,024px. 66rem = 1,056px is the
     first whole rem above the collision. At 1,057px and up the header is exactly what it was. */
  [data-page] .nav-toggle { display: inline-block; }
  [data-page] .nav-links {
    display: none; position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; align-items: flex-start; gap: 0;
    background: var(--bg-panel); border-bottom: 1px solid var(--line);
    padding: 0.4rem 1.5rem 0.9rem;
  }
  [data-page] .nav-links.open { display: flex; }
  [data-page] .nav-links a { padding: 0.7rem 0; font-size: 1rem; width: 100%; }
}

/* THE HEADER BAR WRAPS RATHER THAN CRUSHES.
   Adding the Menu button put four controls plus the lockup in a bar that was already tight, and at
   390px the sign-in CTA broke across three lines and spilled out of its own 3.5rem header — caught
   by looking at it on a phone width, not by the overflow check, which stayed clean throughout
   because the damage was vertical.
   The fix is to let the bar become two rows instead of one, and to keep every control and every
   string: the alternative was dropping the theme toggle or the CTA on mobile, and which of those a
   site can afford to lose is not a decision that belongs in a stylesheet. nowrap stops the CTA
   breaking mid-phrase now that it has room to sit whole. */
@media (max-width: 40rem) {
  [data-page] header .hwrap, [data-page] header .nav {
    flex-wrap: wrap; height: auto; min-height: 3.5rem;
    padding-top: 0.5rem; padding-bottom: 0.5rem; row-gap: 0.5rem;
  }
  [data-page] .nav-right { flex-wrap: wrap; }
  [data-page] header .btn { white-space: nowrap; }
}

/* THE HEADER BUTTON STAYS ON ONE LINE AT EVERY WIDTH (record § A38, 21 October 2026). The "Mock exams"
   link filled the desktop row and "Sign in to practise" broke across two lines at every desktop width.
   The label is now "Sign in"; nowrap is the guard, so a fuller menu shows up as a crowded row that can
   be measured, never as a button quietly breaking in two. */
[data-page] header .btn { white-space: nowrap; }

/* ==========================================================================
   RESOURCES — the editorial page. Document scope only.
   ========================================================================== */
[data-page="doc"] .who-for { color: var(--ink-2); font-size: 1.02rem; max-width: 44rem; }
[data-page="doc"] .section-head { color: var(--ink); font-size: 1.15rem; font-weight: 700; letter-spacing: -0.01em; margin-bottom: 1.2rem; }
[data-page="doc"] .section-note { color: var(--ink-2); font-size: 0.98rem; margin-top: 1.2rem; }
[data-page="doc"] .rule { border: 0; border-top: 1px solid var(--line); margin: 3.5rem 0; }
[data-page="doc"] .block-gap { margin-top: 3rem; }

/* The 30-second taste. RED AND GREEN ARE MEANING HERE, not decoration — they mark
   an answer that scores against one that does not, which is the single thing this
   page exists to demonstrate. Declared on the component, never as page tokens, so
   they cannot leak into anything else on the site. */
[data-page="doc"] .taste { --edge-weak: #DC2626; --edge-strong: #15803D; display: grid; gap: 1.25rem; }
[data-page="doc"][data-theme="night"] .taste { --edge-weak: #F87171; --edge-strong: #4ADE80; }
[data-page="doc"] .panel {
  background: var(--bg-panel); border: 1px solid var(--line); border-left: 4px solid var(--line);
  border-radius: 0.7rem; padding: 1.2rem 1.4rem;
}
[data-page="doc"] .panel.weak { border-left-color: var(--edge-weak); }
[data-page="doc"] .panel.strong { border-left-color: var(--edge-strong); }
[data-page="doc"] .panel .verdict {
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
  margin-bottom: 0.6rem; line-height: 1.4;
}
[data-page="doc"] .panel.weak .verdict { color: var(--edge-weak); }
[data-page="doc"] .panel.strong .verdict { color: var(--edge-strong); }
[data-page="doc"] .panel blockquote { color: var(--ink); font-size: 1rem; line-height: 1.6; }
@media (min-width: 52rem) { [data-page="doc"] .taste { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Flagship. The one reference piece, given the accent edge the article rows do not have. */
[data-page="doc"] .flagship {
  background: var(--bg-panel); border: 1px solid var(--accent);
  box-shadow: 0 0 0 1px var(--accent); border-radius: 0.9rem; padding: 1.6rem 1.5rem;
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0.8rem;
}
[data-page="doc"] .flagship h2 { color: var(--ink); font-size: 1.15rem; font-weight: 700; line-height: 1.35; max-width: 34rem; }
[data-page="doc"] .flagship .status { color: var(--accent); font-size: 0.72rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; }

/* Numbered editorial rows — rules between items, not boxes around them. */
[data-page="doc"] .rows { border-top: 1px solid var(--line); }
[data-page="doc"] .row {
  display: grid; grid-template-columns: 3rem minmax(0, 1fr) auto; align-items: baseline;
  gap: 0.4rem 1rem; padding: 1.15rem 0; border-bottom: 1px solid var(--line);
}
[data-page="doc"] .row .n { color: var(--accent); font-size: 0.85rem; font-weight: 700; font-variant-numeric: tabular-nums; }
[data-page="doc"] .row h2 { color: var(--ink); font-size: 1.05rem; font-weight: 700; line-height: 1.4; }
[data-page="doc"] .row .status { color: var(--ink-2); font-size: 0.72rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; white-space: nowrap; }
[data-page="doc"] .row.held h2 { color: var(--ink-2); font-weight: 600; }
@media (max-width: 40rem) {
  [data-page="doc"] .row { grid-template-columns: 2.2rem minmax(0, 1fr); }
  [data-page="doc"] .row .status { grid-column: 2; }
}

/* The ladder. Exhibit A first, then the courses, then the quiet line. */
[data-page="doc"] .ladder { display: grid; gap: 1.25rem; }
[data-page="doc"] .rung {
  background: var(--bg-panel); border: 1px solid var(--line); border-radius: 0.9rem; padding: 1.5rem;
}
[data-page="doc"] .rung.first { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
[data-page="doc"] .rung h2 { color: var(--ink); font-size: 1.1rem; font-weight: 700; margin-bottom: 0.5rem; }
[data-page="doc"] .rung p { color: var(--ink-2); font-size: 0.95rem; }
[data-page="doc"] .rung p + p { margin-top: 0.7rem; }
[data-page="doc"] .rung .btn { margin-top: 1.1rem; }
[data-page="doc"] .rung a:not(.btn) { color: var(--accent); font-weight: 600; text-decoration: none; border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent); }
[data-page="doc"] .packs { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.8rem; margin: 1.1rem 0 0; }
/* THE DESCRIPTION LINE UNDER THE PACK DOORS. Same job as the home page's second .price-sub, which
   sits below its pack cards: the doors carry a price and a count, and this says what the price
   actually buys. Scoped for doc separately because the home rule opens with a NEGATIVE top margin
   (it pulls that copy up under an <h2>), and inheriting that here would drag the sentence into the
   doors above it. `.packs + .price-sub` rather than a new class, so the two pages describe the
   same object with the same name. */
[data-page="doc"] .packs + .price-sub { margin-top: 1.1rem; color: var(--ink-2); font-size: 0.95rem; }
[data-page="doc"] .pack { border: 1px solid var(--line); border-radius: 0.7rem; padding: 1rem 0.75rem; text-align: center; }
[data-page="doc"] .pack .count { color: var(--ink); font-size: 1.8rem; font-weight: 800; line-height: 1; font-variant-numeric: tabular-nums; }
[data-page="doc"] .pack .label { color: var(--ink-2); font-size: 0.78rem; margin-top: 0.35rem; }
[data-page="doc"] .pack .price { color: var(--accent); font-size: 1.05rem; font-weight: 700; margin-top: 0.7rem; font-variant-numeric: tabular-nums; }
/* THE DOORS ON A PHONE, 12 October 2026. Three fixed columns leave each tile about 90px wide at
   390px, and the tile's big word is set at 1.8rem: "December" measured wider than the tile that
   holds it and ran out across its border. Under 40rem the doors sit two to a row and the big word
   comes down; under 22.5rem it comes down once more. Both breakpoints were MEASURED, not guessed:
   a first cut at 34rem still spilled 18px at 545px wide (three columns again, too early) and 13px
   at 320px. Nothing is reworded. */
@media (max-width: 40rem) {
  [data-page="doc"] .packs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  [data-page="doc"] .pack .count { font-size: 1.3rem; }
}
@media (max-width: 22.5rem) {
  [data-page="doc"] .pack .count { font-size: 1.05rem; }
}
[data-page="doc"] .courses { display: grid; gap: 0.9rem; margin-top: 1rem; }
[data-page="doc"] .course { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0.5rem 1rem; }
[data-page="doc"] .course .price { color: var(--ink); font-weight: 700; font-variant-numeric: tabular-nums; }
[data-page="doc"] .quiet { color: var(--ink-2); font-size: 0.95rem; text-align: center; margin-top: 1.6rem; }
[data-page="doc"] .quiet a { color: var(--accent); font-weight: 600; text-decoration: none; border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent); }

/* Article page: the inline callout and the prev/next pair. */
[data-page="doc"] .callout {
  background: color-mix(in srgb, var(--accent) 7%, var(--bg-panel));
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  border-radius: 0.8rem; padding: 1.3rem 1.4rem; margin: 2rem 0;
}
[data-page="doc"] .callout h2 { color: var(--ink); font-size: 1.08rem; font-weight: 700; line-height: 1.4; margin-bottom: 0.5rem; margin-top: 0; }
[data-page="doc"] .callout p { color: var(--ink-2); font-size: 0.95rem; margin-bottom: 1rem; }
[data-page="doc"] .prevnext { display: grid; gap: 1rem; margin-top: 3rem; }
[data-page="doc"] .prevnext a {
  display: block; background: var(--bg-panel); border: 1px solid var(--line);
  border-radius: 0.8rem; padding: 1rem 1.2rem; text-decoration: none;
}
/* BOTH SPANS MUST BE BLOCKS. They were inline, so they ran together on one line — "NEXTWhat To Do
   Before Your SBL Pre-Seen Arrives" — and the `margin-top` below was silently doing nothing,
   because vertical margins do not apply to inline boxes. The rule looked correct and had no
   effect, which is why it survived. */
[data-page="doc"] .prevnext .dir {
  display: block; color: var(--ink-2); font-size: 0.7rem; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
}
[data-page="doc"] .prevnext .ttl {
  display: block; color: var(--ink); font-weight: 700; font-size: 0.98rem;
  line-height: 1.4; margin-top: 0.45rem;
}
[data-page="doc"] .prevnext a:hover { border-color: var(--accent); }
/* A LONE CARD TAKES THE FULL ROW. The first article has no Previous and the last has no Next, so
   one card would otherwise sit in the left column with a hole beside it, reading as a card whose
   partner failed to render rather than as the end of the series. */
[data-page="doc"] .prevnext a:only-child { grid-column: 1 / -1; }
@media (min-width: 40rem) { [data-page="doc"] .prevnext { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ==========================================================================
   ABOUT PORTRAIT — theme-paired, following the homepage screenshot pattern.
   Two images in the DOM, one shown per theme, switched by the same data-theme
   attribute the palette keys off. The light file is the default so a page with
   no stored choice — or with the head script blocked — shows the theme the
   markup ships in. Identical mechanism to .shot-light/.shot-night on the
   homepage; a separate pair of class names because these are scoped to
   document pages and the two must not be confused when either is edited.
   ========================================================================== */
[data-page="doc"] .portrait-night, [data-page="doc"][data-theme="light"] .portrait-night { display: none; }
[data-page="doc"][data-theme="night"] .portrait-light { display: none; }
[data-page="doc"][data-theme="night"] .portrait-night { display: block; }

/* Course entries — name and price on one line, the description under it. */
[data-page="doc"] .course-list { display: grid; gap: 1.4rem; margin-top: 1.2rem; }
[data-page="doc"] .course-item .head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.6rem; }
[data-page="doc"] .course-item .name { color: var(--ink); font-weight: 700; font-size: 1rem; }
[data-page="doc"] .course-item .price { color: var(--accent); font-weight: 700; font-variant-numeric: tabular-nums; }
[data-page="doc"] .course-item .tag { color: var(--ink-2); font-size: 0.85rem; }
[data-page="doc"] .course-item .desc { color: var(--ink-2); font-size: 0.95rem; margin-top: 0.4rem; }

/* Flagship article hero — the accent treatment that marks the reference piece. */
[data-page="doc"] .flagship-hero {
  background: color-mix(in srgb, var(--accent) 8%, var(--bg-panel));
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  border-radius: 0.9rem; padding: 1.8rem 1.7rem; margin-bottom: 2.5rem;
}
/* THE SITTING BANNER. Loud enough to be read before the article starts, quiet enough not to look
   like an error state - it is a signpost, not a warning. Accent rule on the leading edge rather
   than a filled block, so it reads as part of the page in both themes. */
[data-page="doc"] .sitting-banner {
  margin: 0 0 1.6rem; padding: 0.85rem 1.1rem;
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  border-left: 3px solid var(--accent); border-radius: 0 10px 10px 0;
}
[data-page="doc"] .sitting-banner p { color: var(--ink); font-size: 0.95rem; margin: 0; }
[data-page="doc"] .sitting-banner a { color: var(--accent); font-weight: 600; }
[data-page="doc"] .flagship-hero .kicker { margin-bottom: 0.7rem; }
[data-page="doc"] .flagship-hero h1 { margin-bottom: 1rem; }
[data-page="doc"] .flagship-hero .framing { color: var(--ink-2); font-size: 1.02rem; max-width: 42rem; }

/* A holding block that reads as unwritten rather than as thin content. */
[data-page="doc"] .holding {
  border: 1px dashed var(--line); border-radius: 0.8rem;
  padding: 1.4rem 1.5rem; color: var(--ink-2); text-align: center; font-size: 0.98rem;
}
[data-page="doc"] .holding strong { color: var(--ink); font-weight: 700; }

/* The flagship banner on resources is a link now, so it needs link behaviour. */
[data-page="doc"] a.flagship { text-decoration: none; transition: box-shadow 0.15s; }
[data-page="doc"] a.flagship:hover { box-shadow: 0 0 0 2px var(--accent); }

/* ==========================================================================
   PRICING CARDS ARE LINKS — 8 Aug 2026.
   All three cards now go where the "Sign in to practise" button beside them
   goes, so every click target in that section lands on one entry route. A real
   anchor wrapping the card, not a click handler: it keys, it middle-clicks, it
   shows its destination in the status bar, and it works with JS off.

   `color: inherit` is the load-bearing line. On the HOMEPAGE, .pack .count sets
   no colour of its own — it inherited from body, which was fine while the card
   was a div. As an <a> it would have taken the browser's default link blue, and
   the biggest number on the pricing section would have changed colour. Both
   scopes get it, so neither can drift.

   Hover uses the middle card's accent border as its reference, which is what
   makes the hover state legible without inventing anything: hovering any card
   gives it the border the recommended card already wears. On .mid that is a
   no-op, which is correct — it is already there.
   ========================================================================== */
[data-page] a.pack {
  display: block; color: inherit; text-decoration: none;
  transition: border-color 0.15s;
}
[data-page] a.pack:hover { border-color: var(--accent); }
/* :focus-visible, not :focus — a mouse click should not leave a ring behind, but a keyboard
   user must never lose their place. Offset so the ring sits outside the card's own border. */
[data-page] a.pack:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* THE LOCKUP'S RULE — a drawn mark, not a link state. Reinstated 8 August 2026.
   The previous round removed an underline from the wordmark because it was ACCIDENTAL: `.mark-sm`
   had been a <div> on the homepage until the nav round made it a link, the home rule never carried
   `text-decoration: none` because a div never needed one, and the document rule always had it —
   so exactly one page underlined its wordmark and nine did not. Removing it was right at the time:
   an inconsistency nobody chose is a defect however it looks.
   Sean then chose it. So it comes back as a DRAWING rather than as a link default, and the
   difference is the whole point of this block:
     · it sits under `.word` only — the tab-A is a shape, and a rule under a shape reads as a
       mistake. The A is not part of the word here, it IS the word's last letter living in the tab.
     · it is stated in the accent token, so it is the mark's colour and not the text's, and it
       moves with the theme without a second rule. Both scopes resolve --accent to the same pair
       (#4338CA light, #A78BFA night), so home and doc cannot drift apart.
     · it is STATIC. Not on hover, not on :visited, not suppressed anywhere — a mark that appears
       and disappears with pointer state is a link decoration again, which is what this replaces.
     · text-decoration rather than border-bottom: `.lockup` aligns on flex-end, and a border would
       join the box model and lift the word off the tab it is aligned to.
   `text-decoration: none` stays on the anchor itself, and still for every state: it stops the
   decoration propagating from the <a> across the WHOLE lockup, tab included. The word draws its
   own. The focus ring is deliberately NOT suppressed — it is the one outline that must survive. */
[data-page] .mark-sm,
[data-page] .mark-sm:link,
[data-page] .mark-sm:visited,
[data-page] .mark-sm:hover,
[data-page] .mark-sm:active,
[data-page] .mark-sm:focus { text-decoration: none; }
[data-page] .lockup .word {
  text-decoration-line: underline;
  text-decoration-color: var(--accent);
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
  /* Continuous by construction: "Exhibit" has no descender today, but a font swap that introduced
     one would otherwise break the rule into segments and turn the mark into a typo. */
  text-decoration-skip-ink: none;
}
[data-page] .mark-sm:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ==========================================================================
   CONTENT LINKS NEVER FALL THROUGH TO THE BROWSER DEFAULT — 8 Aug 2026.

   The reported defect: "Watch on YouTube" on resources.html rendered in the
   browser's link blue, and purple once visited — unreadable on the dark theme.
   The cause was not a wrong colour but an ABSENT one. `.section-note` carried
   no `a` rule, so its link took the UA stylesheet, and `:visited` took the UA
   purple. Every other content link on the site had a rule; this one context
   did not, which is why it was the only place the leak showed.

   Fixing that one selector would leave the same hole open for the next block
   that ships without an `a` rule — so the floor is set here for every link in
   `main`, `:link` and `:visited` stated together. The two states resolve to
   the same token deliberately: a visited-versus-unvisited distinction is not
   part of this design, and the UA's idea of that distinction is the bug.

   Header and footer links are NOT in scope — both already declare their own
   colours, and both sit outside `main`.

   The exclusions are the links that are not content links: buttons and pricing
   cards paint themselves, and the flagship banner colours its inner spans.
   Every exclusion is a SIMPLE `:not(.class)` — Selectors 3, supported
   everywhere. A complex one such as `:not(.prevnext a)` would be Selectors 4,
   and where that is unsupported the whole comma-separated rule is dropped and
   the purple comes straight back on exactly the old browsers least able to
   cope with it. Prev/next needs no exclusion anyway: both spans inside it set
   their own colour, so the anchor's own colour never paints.
   ========================================================================== */
/* A FLOOR, NOT A CEILING — corrected 8 August 2026.

   As first written this rule carried specificity (0,4,2), which was far more than the job needs
   and enough to beat rules that had deliberately chosen a different colour. It silently overrode
   `[data-page="home"] .band a` (0,2,1) — the homepage's inverted band, which correctly paints its
   links in `--band-accent`, the token that flips WITH the band rather than with the theme. The
   result was a link in the theme's accent sitting on the band's opposite background: dark indigo
   on near-black in the light theme, pale lilac on near-white in the dark one. Wrong in both, and
   wrong in opposite directions, which is why neither theme looked obviously broken on its own.

   `:where()` contributes ZERO specificity, so this now sits at (0,0,1) — the weight of `main`
   alone. That is all it ever needed: an author declaration beats the user-agent stylesheet by
   ORIGIN, not by specificity, so a floor at (0,0,1) still stops every link falling through to the
   browser's blue and purple. What it can no longer do is outrank a scope that made a decision. */
main :where(a:not(.btn):not(.pack):not(.flagship)),
main :where(a:not(.btn):not(.pack):not(.flagship):link),
main :where(a:not(.btn):not(.pack):not(.flagship):visited) {
  color: var(--accent);
}
main :where(a:not(.btn):not(.pack):not(.flagship):hover) {
  color: var(--accent-strong);
}
[data-theme="night"] main :where(a:not(.btn):not(.pack):not(.flagship):hover) {
  /* --accent-strong is the dark FILL (#7C3AED) and goes DARKER than the resting
     text tint, which on a #0B1220 page reads as the link dimming on hover. Dark
     hover brightens instead, using the same token pair the rest of the sheet
     uses rather than inventing a third value. */
  color: color-mix(in srgb, var(--accent) 75%, #FFFFFF);
}
/* The two content-link contexts that had rules already keep their underline and
   weight; only the visited state is added, so nothing about them moves. */
[data-page="doc"] .section-note a,
[data-page="doc"] .watch a {
  font-weight: 600; text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
}
[data-page="doc"] .section-note a:hover,
[data-page="doc"] .watch a:hover { border-bottom-color: var(--accent); }

/* ==========================================================================
   ARTICLE CALLOUTS — the Notion <aside> blocks, rendered as themselves.

   The weak/strong contrast IS the teaching on these pages: a reader has to see
   at a glance which answer loses the marks and which earns them, the way the
   Notion original shows it. So the tint carries the meaning and the emoji the
   source wrote is kept as the marker, rather than being dropped for a generic
   panel that would make both sides look alike.

   Four variants, mapped from the emoji in the source: 🔴 red, ⚠️/🟠 amber,
   ✅ green, everything else (💡 📌 🔧 🧭 📘 …) the site accent.

   The red and green edge values are LIFTED FROM .taste above rather than newly
   chosen — that block already solved "a red and a green that hold up on both
   themes" for the same weak/strong job on resources.html, and two answers to
   one question would drift apart. Amber is the one new pair.
   ========================================================================== */
[data-page="doc"] .note {
  --note: var(--accent);
  display: grid; grid-template-columns: 1.6rem minmax(0, 1fr); gap: 0 0.7rem;
  background: color-mix(in srgb, var(--note) 7%, var(--bg-panel));
  border: 1px solid color-mix(in srgb, var(--note) 28%, transparent);
  border-left: 3px solid var(--note);
  border-radius: 0.7rem; padding: 1.1rem 1.2rem;
}
[data-page="doc"] .note-red { --note: #DC2626; }
[data-page="doc"] .note-green { --note: #15803D; }
[data-page="doc"] .note-amber { --note: #B45309; }
[data-page="doc"][data-theme="night"] .note-red { --note: #F87171; }
[data-page="doc"][data-theme="night"] .note-green { --note: #4ADE80; }
[data-page="doc"][data-theme="night"] .note-amber { --note: #FBBF24; }

[data-page="doc"] .note-icon { font-size: 1rem; line-height: 1.6; }
[data-page="doc"] .note-body > * + * { margin-top: 0.8rem; }
[data-page="doc"] .note-body > :first-child { margin-top: 0; }
/* Bold inside a callout is the verdict line — "Weak — earns nothing", "Strong —
   earns in full". It takes the callout's own colour so the verdict is readable
   before the sentence under it is. */
[data-page="doc"] .note-body strong { color: var(--note); }
[data-page="doc"] .note-body p { color: var(--ink); }
[data-page="doc"] .note-body blockquote { border-left-color: var(--note); }

/* ==========================================================================
   TOGGLES, RENDERED OPEN.
   The Notion source collapses the long case description behind a toggle. On a
   page whose whole job is to be readable in one pass there is nothing to gain
   by hiding it and a real cost if a reader never opens it, so it ships open —
   marked as an aside by its panel, not by a control that has to be clicked.
   ========================================================================== */
[data-page="doc"] .toggle-open {
  background: var(--bg-panel); border: 1px solid var(--line);
  border-radius: 0.7rem; padding: 1.2rem 1.3rem;
}
[data-page="doc"] .toggle-title {
  color: var(--ink); font-weight: 700; font-size: 1rem; margin-bottom: 0.8rem;
}
[data-page="doc"] .toggle-body > * + * { margin-top: 0.8rem; }

/* Section separator inside an article. Lighter than .rule, which is page-level
   furniture at 3.5rem of margin and would tear the prose apart at this rhythm. */
[data-page="doc"] .article .sep {
  border: 0; border-top: 1px solid var(--line); margin: 2.4rem 0;
}
/* The export carries mid-document h1s ("The five sentences"). One h1 per page,
   so they become h2s that still read as a bigger break than the h2s around them. */
[data-page="doc"] .article h2.major {
  font-size: 1.55rem; margin-top: 3rem;
  padding-bottom: 0.6rem; border-bottom: 2px solid var(--accent);
}
[data-page="doc"] .meta {
  color: var(--ink-2); font-size: 0.8rem; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase; margin: -1.4rem 0 1.6rem;
}
[data-page="doc"] .watch { color: var(--ink-2); font-size: 0.98rem; }

/* ==========================================================================
   RESOURCES INDEX — the blocks the Notion index carries that this page did not.
   ========================================================================== */

/* The opening callout runs the full measure and leads the page, so it takes more
   air beneath it than a callout sitting mid-article does. */
[data-page="doc"] .lede-note { margin-bottom: 1.6rem; }
[data-page="doc"] .section-intro { color: var(--ink-2); font-size: 1rem; margin-bottom: 1.2rem; }
[data-page="doc"] .section-intro + .section-intro { margin-top: -0.4rem; }
[data-page="doc"] .who-for strong { color: var(--ink); font-weight: 700; }

/* THE 30-SECOND TASTE IS THE SAME CONTRAST THE ARTICLE CALLOUTS DRAW, so it is
   tinted on the same formula rather than left as a bare edge — a reader
   comparing the two panels should be able to tell weak from strong before
   reading either. .taste already declared the red and green pair per theme;
   this only spends them on a background as well as the border. */
[data-page="doc"] .panel.weak { background: color-mix(in srgb, var(--edge-weak) 7%, var(--bg-panel)); }
[data-page="doc"] .panel.strong { background: color-mix(in srgb, var(--edge-strong) 7%, var(--bg-panel)); }

/* Every row is a link now — nothing on this page is a dead label. `.row` was a
   div, so it never carried link behaviour; `color: inherit` keeps .n, h2 and
   .status painting themselves exactly as they did, rather than taking the
   anchor colour the way an unstyled <a> would. */
[data-page="doc"] a.row {
  color: inherit; text-decoration: none; transition: background 0.15s;
}
[data-page="doc"] a.row:hover { background: color-mix(in srgb, var(--accent) 6%, transparent); }
[data-page="doc"] a.row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ==========================================================================
   THE LONG-FORM ANALYSIS PAGE — contents nav and open toggles.
   Used by the pre-seen analysis, which is one page of twelve lettered sections
   and is far longer than any article in the series.
   ========================================================================== */

/* A contents nav, not a sidebar: it sits in the flow directly under the
   opening callout, so the no-prediction line is read before a reader is
   offered a way to skip around it. */
[data-page="doc"] .toc {
  display: grid; gap: 0.15rem;
  background: var(--bg-panel); border: 1px solid var(--line);
  border-radius: 0.8rem; padding: 1.2rem 1.3rem;
}
[data-page="doc"] .toc-head {
  color: var(--ink-2); font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase; margin-bottom: 0.6rem;
}
/* NO COLOUR DECLARED HERE, DELIBERATELY. The site-wide content-link rule already
   paints every link in `main` with the accent token and outranks this selector,
   so a `color` here would be a dead declaration that reads as the source of
   truth. These are links in a list of links; taking the link colour is right. */
[data-page="doc"] .toc a {
  font-weight: 600; font-size: 0.95rem;
  text-decoration: none; padding: 0.25rem 0;
}
@media (min-width: 42rem) {
  [data-page="doc"] .toc { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.15rem 1.4rem; }
  [data-page="doc"] .toc-head { grid-column: 1 / -1; }
}

/* TOGGLES SHIP OPEN, and the marker still turns — a reader who wants to collapse
   one of five tensions to compare the rest can, but nothing is hidden on arrival.
   `open` is in the markup rather than forced in CSS deliberately: forcing it would
   take the control away, and the reason to keep <details> at all is that the
   structure is real and a reader may want to work it. */
[data-page="doc"] .article .toggle {
  background: var(--bg-panel); border: 1px solid var(--line);
  border-radius: 0.8rem; padding: 1.1rem 1.3rem;
}
[data-page="doc"] .article .toggle + .toggle { margin-top: 1rem; }
[data-page="doc"] .article .toggle > summary {
  color: var(--ink); font-weight: 700; font-size: 1.08rem; line-height: 1.4;
  cursor: pointer; list-style-position: outside;
}
[data-page="doc"] .article .toggle > summary::marker { color: var(--accent); }
[data-page="doc"] .article .toggle[open] > summary {
  margin-bottom: 0.9rem; padding-bottom: 0.7rem; border-bottom: 1px solid var(--line);
}
[data-page="doc"] .article .toggle > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
[data-page="doc"] .article .toggle .toggle-body > * + * { margin-top: 0.9rem; }

/* THE END-OF-PAGE QUIZ SHIPS CLOSED — the opposite of the toggles above, and for the opposite
   reason. A quiz whose answer is already showing is a paragraph. The question and its options are
   the <summary>; the answer and its explanation are inside. No script: the browser's own
   disclosure is the whole mechanism.

   THE CUE IS THE LABEL, NOT THE TRIANGLE. The first cut relied on the browser's marker alone, and
   a small triangle beside a bold question does not read as "tap me" on a phone. So the marker is
   switched off and `.quiz-show` carries the cue, styled as the control it is. Once the answer is
   open the label is hidden: "Show answer" above an answer that is showing is a false instruction.
   The whole summary stays the tap target, so the question closes it again.
   Markup and the label's words come from tools/quiz-reveal.mjs. */
[data-page="doc"] .article .quiz-q > summary { cursor: pointer; list-style: none; }
[data-page="doc"] .article .quiz-q > summary::-webkit-details-marker { display: none; }
[data-page="doc"] .article .quiz-q > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
[data-page="doc"] .article .quiz-q .quiz-show {
  display: inline-block; margin-top: 0.55rem; padding: 0.3rem 0.8rem;
  border: 1px solid var(--accent); border-radius: 999px;
  color: var(--accent); font-size: 0.9rem; font-weight: 600; line-height: 1.4;
}
[data-page="doc"] .article .quiz-q[open] .quiz-show { display: none; }
[data-page="doc"] .article .quiz-q > p {
  margin-top: 0.6rem; padding-left: 0.9rem; border-left: 2px solid var(--accent);
}

/* A pointer out of the series, not a card in it. Deliberately slim: the latest pre-seen analysis
   is the bigger draw and has its own tab, so what belongs here is a signpost, not a second
   flagship competing with the series it is not part of. */
[data-page="doc"] .pointer { font-size: 1rem; font-weight: 600; margin-bottom: 1.6rem; }

/* ==========================================================================
   A BUTTON'S LABEL IS NOT A LINK COLOUR — the backstop, 8 August 2026.

   THE BUG THIS ENDS. `.article a` set a link colour at (0,2,1); `.btn` sets
   the label at (0,2,0). One point of specificity, so any `.btn` sitting inside
   `.article` took the link colour instead of white. On the DARK theme that was
   #A78BFA on #7C3AED — odd but readable, so it survived review. On LIGHT both
   tokens resolve to #4338CA, so the label was painted in exactly the button's
   own background colour: a blank purple pill on every article page.

   The scope rule is now `:not(.btn)`, which fixes it at source. This block is
   the backstop, and it is here because the same trap is one careless
   `.something a { color: … }` away from returning — the next author has no
   reason to know that a filled control lives inside their scope.

   Stated for every state, and `!important` on the colour deliberately: there
   is no design in which a button's label should take an ancestor's link
   colour, so this is one of the few places where refusing to lose a
   specificity race is the correct behaviour rather than a shortcut.
   ========================================================================== */
[data-page] a.btn,
[data-page] a.btn:link,
[data-page] a.btn:visited,
[data-page] a.btn:hover,
[data-page] a.btn:active {
  color: #fff !important;
  border-bottom: 0;
}

/* ==========================================================================
   THE LEGAL PAGES — limits, terms, privacy, refunds. 8 August 2026.

   FORMATTING ONLY, AND ENFORCED BY CONSTRUCTION: every improvement here is a
   CSS rule, so the four documents' HTML is byte-identical to what counsel
   approved. Not one word moved, and not one word CAN have moved.

   That is not merely tidiness. `site/terms.html` carries the independence
   clause whose exact <p> bytes are hashed in tests/ui/non-affiliation-blocks
   .test.ts. Promoting these clauses to real <h2> markup — the obvious way to
   do this job — would have rewritten that element and broken the lock. Doing
   it in the stylesheet gets the same reading experience with the words
   provably untouched, which is exactly the split Sean ruled: the frame can
   improve while the wording cannot move.

   Each clause already opens with a bold lead-in — "What Exhibit A is.",
   "Independence." — that was doing a heading's work inline. Setting it to
   display:block promotes it visually without promoting it structurally.
   `> strong:first-child` and not `strong`, so a bold phrase used mid-sentence
   later would not be torn onto its own line.
   ========================================================================== */
[data-page="doc"] .block { margin-bottom: 0; }
[data-page="doc"] .block + .block {
  margin-top: 1.7rem; padding-top: 1.7rem; border-top: 1px solid var(--line);
}
[data-page="doc"] .block > strong:first-child {
  display: block; color: var(--ink); font-size: 1.08rem; font-weight: 700;
  line-height: 1.4; letter-spacing: -0.01em; margin-bottom: 0.4rem;
}
/* The lede sits under the h1 and above the first rule, so it needs the air the
   clause separator gives every later block. */
[data-page="doc"] h1 + .block { margin-top: 0.4rem; }
/* Lists, if a future clause needs one. There are none on these four pages
   today — stated so the next person adding one does not have to invent it. */
[data-page="doc"] .block ul, [data-page="doc"] .block ol { padding-left: 1.4rem; margin-top: 0.7rem; }
[data-page="doc"] .block li + li { margin-top: 0.45rem; }
[data-page="doc"] .block li::marker { color: var(--accent); }

/* ==========================================================================
   THE BRAND SURFACES — the light-theme UI pass, 14 August 2026.

   THE PROBLEM THIS SOLVES. The doc palette only ever expressed the brand in
   the DARK theme: navy, violet and Literata all live in the night tokens, so
   the light theme resolved to indigo-on-white and read as a generic docs page.
   The fix is structure and type, plus violet as an ACCENT — not a change of
   ground.

   THEME-NATIVE, AND THE FIRST CUT WAS NOT. Sean's ruling, 14 August 2026: the
   first version of this section put a hard-navy card on the white page as a
   "brand bridge". That is out. A feature card is now the SAME OBJECT in both
   themes — a raised panel on the page's own ground — and it gets there by
   using nothing but the theme tokens: --bg-panel for the card, --bg for the
   wells inside it, --ink / --ink-2 for the type, --line for the edges. White
   card on the white page, tonal panel on the navy page, one set of rules. The
   dark theme was already doing this quietly; light now mirrors its TONALITY,
   not its colour.

   VIOLET IS AN ACCENT AND NEVER A GROUND. It survives on the numerals, the
   prices, the chip, the play triangle and the "Free." in the h1 — via --brand,
   which is violet on light and lilac on night so the ink always has contrast
   against the ground it is painted on.

   KICKERS TAKE --accent, WHICH MEANS THEY ARE NOT VIOLET IN LIGHT. #A78BFA on
   white fails contrast at kicker size, so light kickers are indigo #4338CA.
   That needed no rule at all in the end: the base .kicker already reads
   --accent, which is indigo in light and lilac in night. The two overrides
   this section used to carry are DELETED rather than corrected — the right
   value was already there, and a second declaration of it is a second place to
   get it wrong.

   The dark theme keeps the violet radial glow on feature cards. That is the
   one deliberate asymmetry: on navy it reads as depth, and on white the same
   wash reads as a tint nobody asked for.
   ========================================================================== */

[data-page="doc"], [data-page="doc"][data-theme="light"] { --brand: #7C3AED; }
[data-page="doc"][data-theme="night"] { --brand: #A78BFA; }
[data-page="doc"] { --violet: #7C3AED; --lilac: #A78BFA; }

/* The cardier layout needs more measure than the 46rem the legal pages read at.
   Opted into per page with `data-layout="wide"` on the ROOT element, so
   limits/terms/privacy/refunds and every article keep the measure they were
   set at.

   ON THE ROOT, AND NOT AS `<main class="wide">`, WHICH IS WHAT THIS WAS FIRST.
   tests/ui/site-metadata.test.ts extracts a page's visible text with the
   literal regex /<main>([\s\S]*?)<\/main>/ to check that no meta description
   invents a sentence. An attribute on <main> does not match that pattern, so
   the extraction silently returned the empty string and the check passed
   vacuously on structure while failing on content — a test that cannot see a
   page is worse than one that fails. The root element already carries
   data-page and data-theme for exactly this kind of page-level switch, so the
   flag belongs there and <main> stays bare. */
[data-page="doc"][data-layout="wide"] main { padding-top: 0; }
[data-page="doc"][data-layout="wide"] .wrap { max-width: 55rem; }

/* ---------- the hero band ---------- */
/* Full-bleed, so it sits flush under the sticky header and reads as the page's
   masthead rather than as a first paragraph. It owns its vertical padding
   because main.wide gives up its padding-top to let the band reach the header. */
[data-page="doc"] .hero-band {
  background:
    radial-gradient(ellipse 60% 90% at 78% -20%, color-mix(in srgb, var(--violet) 10%, transparent), transparent 65%),
    var(--bg-panel);
  border-bottom: 1px solid var(--line);
  padding: 4rem 0 3.5rem;
}
/* No colour here: the base .kicker already reads --accent, which is indigo in
   light and lilac in night. That is the ruled value in both. */
[data-page="doc"] .hero-band .kicker { letter-spacing: 0.16em; margin-bottom: 0.9rem; }
[data-page="doc"] .hero-band h1 {
  font-family: 'Literata', Georgia, serif; font-weight: 800;
  font-size: clamp(2rem, 5vw, 3rem); line-height: 1.1; letter-spacing: -0.02em;
  color: var(--ink); margin-bottom: 0;
}
[data-page="doc"] .hero-band h1 .free { color: var(--brand); }
[data-page="doc"] .hero-lede { font-size: 1.15rem; color: var(--ink-2); margin-top: 1.15rem; max-width: 39rem; }
[data-page="doc"] .hero-lede + .hero-lede { margin-top: 0.8rem; }
[data-page="doc"] .hero-lede strong { color: var(--ink); font-weight: 700; }

/* ---------- the heading device: Literata + a short lilac rule ---------- */
/* The rule is a ::after on the heading rather than a sibling <div>, so every
   existing `<div class="section-head">` inherits the device without its markup
   changing — and so a heading can never ship without its rule. */
[data-page="doc"] .section-head {
  font-family: 'Literata', Georgia, serif; font-weight: 700;
  font-size: 1.5rem; letter-spacing: -0.01em; color: var(--ink);
  margin-bottom: 1.2rem;
}
[data-page="doc"] .section-head::after {
  content: ''; display: block; width: 44px; height: 3px;
  background: var(--lilac); border-radius: 2px; margin-top: 0.5rem;
}

/* ---------- the feature card ---------- */
/* Theme tokens only, so this is one object in two themes rather than two. */
[data-page="doc"] .feature {
  background: var(--bg-panel);
  border: 1px solid var(--line); border-radius: 1rem;
  padding: 2.1rem 2.25rem; margin-top: 1.6rem; color: var(--ink-2);
}
/* Night keeps the glow. On navy it reads as depth; the identical wash on white
   reads as a tint, which is the ground-not-accent line this ruling drew. */
[data-page="doc"][data-theme="night"] .feature {
  background:
    radial-gradient(ellipse 70% 90% at 25% -30%, color-mix(in srgb, var(--violet) 25%, transparent), transparent 70%),
    var(--bg-panel);
}
[data-page="doc"] .feature .kicker { letter-spacing: 0.16em; margin-bottom: 0.8rem; }
/* `> h2`, NOT `h2`. A feature card contains rows, and a row's title is an h2 as
   well — the descendant form repainted every row title at 1.6rem Literata and
   the list stopped reading as a list. The child combinator is the whole fix. */
[data-page="doc"] .feature > h2 {
  font-family: 'Literata', Georgia, serif; font-weight: 800; font-size: 1.6rem;
  letter-spacing: -0.01em; color: var(--ink); margin-bottom: 0.5rem;
}
[data-page="doc"] .feature .intro { color: var(--ink-2); max-width: 40rem; margin-bottom: 1.25rem; }
[data-page="doc"] .feature .next { color: var(--ink-2); font-size: 0.95rem; margin-top: 1rem; }
[data-page="doc"] .feature .next em { color: var(--ink); font-style: normal; }
/* `:not(.row)` IS LOAD-BEARING — the same trap the .btn backstop at the end of
   this sheet records, in a new place. A row IS an anchor, so the inline-link
   treatment landed on it and drew a lilac underline along the bottom edge of
   every row card inside a feature. Caught by reading the computed border-color
   back off the element (three values, the last one lilac), not by looking. */
[data-page="doc"] .feature a:not(.btn):not(.row) { color: var(--brand); border-bottom-color: color-mix(in srgb, var(--brand) 40%, transparent); }

/* ---------- the row grammar, shared by the list card and the feature card ---------- */
/* .rows was a bare top border; it is now a card, so the list reads as one
   object. `overflow: hidden` is what lets the first and last rows take the
   card's corner radius without either of them declaring a radius of its own. */
[data-page="doc"] .rows {
  background: var(--bg-panel); border: 1px solid var(--line);
  border-radius: 1rem; overflow: hidden; margin-top: 1.4rem;
}
[data-page="doc"] .row {
  display: flex; align-items: center; gap: 1.1rem;
  padding: 1.25rem 1.6rem; border-top: 1px solid var(--line);
}
[data-page="doc"] .row:first-child { border-top: 0; }
[data-page="doc"] .row .n {
  font-family: 'Literata', Georgia, serif; font-weight: 800; font-size: 1.1rem;
  color: var(--brand); min-width: 2rem; flex: none;
}
[data-page="doc"] .row h2 { font-size: 1.05rem; font-weight: 700; color: var(--ink); margin: 0; }
[data-page="doc"] .row .status {
  margin-left: auto; display: flex; align-items: center; gap: 0.6rem;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-2); white-space: nowrap;
}
[data-page="doc"] a.row:hover { background: color-mix(in srgb, var(--violet) 6%, var(--bg-panel)); }

/* The same row inside a feature card, which is a WELL rather than a divided
   list: the card is --bg-panel, so a row painted --bg sinks below it. That one
   relationship holds in both themes without a single themed override, because
   both tokens flip together. */
[data-page="doc"] .feature .rows { background: transparent; border: 0; border-radius: 0; margin-top: 0; overflow: visible; }
[data-page="doc"] .feature .row {
  background: var(--bg); border: 1px solid var(--line);
  border-radius: 0.75rem; padding: 1.15rem 1.4rem;
}
[data-page="doc"] .feature .row + .row { margin-top: 0.7rem; }
[data-page="doc"] .feature a.row:hover { background: color-mix(in srgb, var(--violet) 8%, var(--bg)); }

/* THE RESET CARD, 13 October 2026: the way in to /reset, on the resources page under its heading and
   on the homepage in "Who it is for". One component on both, so it is scoped to [data-page].
   EXISTING TOKENS ONLY. The border and the label take --brand, the token behind "Free." in the
   resources heading; the homepage palette has no --brand, so there they fall back to that page's
   own --accent. The ground is --bg, which sits as a well on a panel and level on the page. */
[data-page] .reset-card {
  display: block; max-width: 39rem; margin-top: 1.5rem; padding: 1.1rem 1.35rem;
  background: var(--bg); border: 2px solid var(--brand, var(--accent)); border-radius: 0.75rem;
  text-decoration: none;
}
[data-page] .reset-card .rc-label { display: block; font-weight: 700; font-size: 1.12rem; line-height: 1.35; color: var(--brand, var(--accent)); }
[data-page] .reset-card .rc-sub { display: block; margin-top: 0.3rem; font-size: 0.95rem; font-weight: 400; line-height: 1.5; color: var(--ink-2); }
[data-page] .reset-card:hover { background: color-mix(in srgb, var(--brand, var(--accent)) 8%, var(--bg)); }
[data-page="home"] .who .reset-card { margin-bottom: 1.4rem; }
/* Beside "All free resources" on the homepage: a link next to a button sits on the button's centre line. */
[data-page="home"] .free-line.btn-row { align-items: center; }

/* ---------- the VIDEO chip ---------- */
/* ONE METADATA GRAMMAR. Every row's meta is "N MIN READ" in the same small
   caps; a video is a CHIP beside it, never a different sentence shape. That is
   the rule this component exists to make unbreakable — the row it replaced read
   "12 min read · with video", which no other row could have matched. */
[data-page="doc"] .chip {
  display: inline-flex; align-items: center; gap: 0.4rem;
  background: color-mix(in srgb, var(--violet) 8%, transparent);
  border: 1px solid var(--violet); color: var(--violet);
  border-radius: 999px; padding: 0.15rem 0.6rem;
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.1em;
}
/* ONE chip, everywhere. It used to carry a second, heavier treatment for the
   navy card; with feature cards theme-native there is no second ground for it
   to sit on, so the variant is DELETED rather than kept in step. The chip is
   now literally the same component on all seven rows. */
[data-page="doc"][data-theme="night"] .chip {
  color: var(--lilac); background: color-mix(in srgb, var(--violet) 22%, transparent);
}
/* The play triangle, drawn in borders so it needs no asset and takes currentColor. */
[data-page="doc"] .chip .tri {
  display: inline-block; width: 0; height: 0; border-style: solid;
  border-width: 4px 0 4px 6px; border-color: transparent transparent transparent currentColor;
}

/* ---------- the YouTube row, the list card's own last row ---------- */
[data-page="doc"] .rows .yt-row {
  display: flex; align-items: center; gap: 0.7rem;
  padding: 1.15rem 1.6rem; border-top: 1px solid var(--line);
  background: color-mix(in srgb, var(--ink) 2%, var(--bg-panel));
  font-size: 0.95rem; color: var(--ink-2);
}
[data-page="doc"] .rows .yt-row .play {
  width: 20px; height: 14px; flex: none; border-radius: 4px; background: var(--violet);
  display: inline-flex; align-items: center; justify-content: center;
}
[data-page="doc"] .rows .yt-row .play::after {
  content: ''; border-style: solid; border-width: 4px 0 4px 6px;
  border-color: transparent transparent transparent #fff;
}
[data-page="doc"] .rows .yt-row a:not(.btn) {
  color: var(--brand); font-weight: 600; text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--brand) 40%, transparent);
}

/* ---------- the courses cards ---------- */
/* .course-item was a bare block on both resources.html AND about.html. Making
   it a card here gives BOTH pages the treatment from one rule, with no edit to
   either page's markup — which is the only reason about.html moves at all. */
[data-page="doc"] .course-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; }
[data-page="doc"] .course-item {
  background: var(--bg-panel); border: 1px solid var(--line);
  border-radius: 0.875rem; padding: 1.35rem 1.5rem;
}
/* Three products into a two-column grid leaves one orphan. The third spans
   rather than sitting half-width beside a gap. */
[data-page="doc"] .course-item:last-child:nth-child(odd) { grid-column: 1 / -1; }
[data-page="doc"] .course-item .name { font-family: 'Literata', Georgia, serif; font-size: 1.08rem; }
[data-page="doc"] .course-item .price { color: var(--brand); }

/* The second rung holds cards now, so its own border would be a box drawn round
   three boxes. It keeps its heading and gives up its frame. */
[data-page="doc"] .rung:not(.first) { background: none; border: 0; padding: 0; }

/* The flagship rung takes the feature treatment, so the product ladder opens on
   the same object the series card established. Its two .pack doors are the
   marketing rail's only entrances on this page and stay exactly where they are.

   THE RING IS KEPT, AND IT IS THE ONLY THING MARKING THE FLAGSHIP NOW. With
   feature cards theme-native, .rung.first is a white card among white cards,
   so the accent ring .rung.first already carried is doing real work rather than
   competing with a navy ground. It is left to win on its own specificity. */
[data-page="doc"] .rung.first h2 { font-family: 'Literata', Georgia, serif; font-size: 1.35rem; }
[data-page="doc"] .rung.first .pack .price { color: var(--brand); }
[data-page="doc"] .rung.first a.pack:hover { border-color: var(--brand); }

/* ---------- mobile ---------- */
@media (max-width: 40rem) {
  [data-page="doc"] .feature { padding: 1.6rem 1.4rem; }
  [data-page="doc"] .row { padding: 1.1rem 1.2rem; gap: 0.8rem; flex-wrap: wrap; }
  [data-page="doc"] .row .status { margin-left: 0; width: 100%; padding-left: 2rem; }
  [data-page="doc"] .rows .yt-row { padding: 1.1rem 1.2rem; }
  [data-page="doc"] .course-list { grid-template-columns: 1fr; }
  [data-page="doc"] .course-item:last-child:nth-child(odd) { grid-column: auto; }
}

/* ============================================================================================
   HOME v4 — the sections the rebuild added.
   Everything here is scoped to [data-page="home"] and uses only tokens defined at :root, so it
   themes with the page rather than carrying its own light and dark values. --violet/--lilac are
   deliberately NOT used: they are defined for [data-page="doc"] only, and reaching for them here
   would render an unset colour on this page.
   ============================================================================================ */

[data-page="home"] .lede { color: var(--ink-2); font-size: 1.05rem; max-width: 46rem; margin: 0.4rem 0 1.6rem; }
[data-page="home"] .quiet { color: var(--ink-2); font-size: 0.95rem; margin-top: 1.2rem; }
[data-page="home"] .quiet a, [data-page="home"] .lede a { color: var(--accent); font-weight: 600; }

/* ── THE RECURRING A MARK ──
   The same square-A face as the header lockup, shrunk to a motif and repeated: before every
   section kicker, on the Ask A card, on the mock card, once in the footer. It is DRAWN, not an
   image, so it inherits the theme and costs no request - and it is the identical construction to
   .tab above, which is why the folder notch is the same ::before trick rather than a new shape.
   Deliberately quiet: 15px, and it sits beside type rather than competing with it. */
/* THE MARK IS A VECTOR SINCE 22 AUGUST 2026 (Sean's ruling). Every rule that BUILT a mark —
   the violet tile, the ::before folder tab, the Literata letter — is retired here and in the
   two blocks below: the SVG draws the folder, the tab and the letter itself, from the same
   geometry the app uses. What is left is SIZE and SPACING, which is all a container should
   ever have decided. */
[data-page="home"] .a-mark {
  width: 15px; height: 15px; flex: none; vertical-align: -2px; margin-right: 0.45rem;
  display: inline-block;
}
/* The kicker's drawing fills its wrapper. One rule for every page scope, because the mark is
   the same size wherever a kicker is. */
.a-mark-svg { width: 100%; height: 100%; display: block; }
[data-page="home"] .kicker { display: flex; align-items: center; }
[data-page="home"] .foot-mark { margin-bottom: 0.8rem; }

/* ── FEATURE BOXES, the row under the value strip ──
   Anchors, not buttons: with no script they jump to the showcase, which is a worse experience but
   never a dead one. The script upgrades them to activating the matching tab in place. */
/* FIVE EQUAL COLUMNS since the exam platform joined the strip (27 August 2026). Four cards wrapped
   4+1 the moment the fifth arrived, which reads as a mistake rather than a set. The two narrower
   rules below are untouched: 2-up under 900 and 1-up under 560, exactly as before. */
[data-page="home"] .feature-boxes { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 1rem; }
[data-page="home"] .fbox {
  display: block; text-decoration: none;
  background: var(--bg-panel); border: 1px solid var(--line); border-radius: 14px;
  padding: 1.2rem 1.3rem;
}
[data-page="home"] .fbox:hover { border-color: var(--accent); }
[data-page="home"] .fbox:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
[data-page="home"] .fbox h3 { font-size: 1rem; font-weight: 700; color: var(--ink); margin: 0.5rem 0 0.4rem; }
[data-page="home"] .fbox p { color: var(--ink-2); font-size: 0.9rem; }

/* ── CAROUSEL ──
   The controls are hidden until `js-on` is set, because arrows that do nothing are worse than no
   arrows. Slide one is always in the markup unhidden, so the no-script state is one real captioned
   screenshot rather than an empty box. */
[data-page="home"] .carousel { position: relative; }
[data-page="home"] .car-slide[hidden] { display: none; }
[data-page="home"] .car-slide { margin: 0; }
[data-page="home"] .car-slide img { width: 100%; height: auto; display: block; }
[data-page="home"] .car-slide figcaption { color: var(--ink-2); font-size: 0.9rem; margin-top: 0.7rem; }
[data-page="home"] .car-arrow, [data-page="home"] .car-dots { display: none; }
[data-page="home"] .carousel.js-on .car-arrow { display: flex; }
[data-page="home"] .carousel.js-on .car-dots { display: flex; }
[data-page="home"] .car-arrow {
  position: absolute; top: 38%; transform: translateY(-50%); z-index: 2;
  align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: 999px; cursor: pointer;
  background: var(--bg-panel); border: 1px solid var(--line); color: var(--ink);
  font-size: 1.2rem; line-height: 1; padding: 0 0 2px;
}
[data-page="home"] .car-arrow:hover { border-color: var(--accent); color: var(--accent); }
[data-page="home"] .car-arrow:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
[data-page="home"] .car-prev { left: -14px; }
[data-page="home"] .car-next { right: -14px; }
[data-page="home"] .car-dots { justify-content: center; gap: 0.4rem; margin-top: 0.9rem; }
[data-page="home"] .car-dot {
  width: 8px; height: 8px; border-radius: 999px; cursor: pointer; padding: 0;
  background: var(--line); border: 1px solid var(--line);
}
[data-page="home"] .car-dot.is-on { background: var(--accent); border-color: var(--accent); }
[data-page="home"] .car-dot:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The mock card sits at the foot of pricing when Sean confirms the flow exists. */
[data-page="home"] .mock-card {
  margin-top: 1.4rem; background: var(--bg-panel);
  border: 1px solid var(--accent); border-radius: 14px; padding: 1.4rem 1.5rem;
}
[data-page="home"] .mock-card h3 { display: inline; font-size: 1.05rem; font-weight: 700; color: var(--ink); }
[data-page="home"] .mock-card p { color: var(--ink-2); margin: 0.6rem 0 0.9rem; }

/* NEXT SITTING FREE — the strip between the pricing cards and the free articles, added 4 September
   2026. Deliberately the .mock-card recipe above and not a new one: the page already has exactly
   one way of drawing "a single bordered statement that is not a card in a row", and a second way
   would be a second thing to keep in step. The accent border is the point — it is the only element
   between two large sections and it has to read as deliberate rather than as a stray paragraph. */
[data-page="home"] .next-free {
  background: var(--bg-panel);
  border: 1px solid var(--accent);
  border-radius: 14px;
  padding: 1.4rem 1.5rem;
}
[data-page="home"] .next-free h2 { font-size: 1.35rem; margin: 0 0 0.6rem; }
[data-page="home"] .next-free p { color: var(--ink-2); margin: 0; max-width: 62ch; }

/* Trust tiles — used twice, under Built to the case and under the chat section. */
[data-page="home"] .trust { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.1rem; margin-top: 1.4rem; }
[data-page="home"] .trust-tile { background: var(--bg-panel); border: 1px solid var(--line); border-radius: 14px; padding: 1.3rem 1.4rem; }
[data-page="home"] .trust-tile h3 { font-size: 1rem; font-weight: 700; color: var(--ink); margin-bottom: 0.5rem; }
[data-page="home"] .trust-tile p { color: var(--ink-2); font-size: 0.95rem; }
[data-page="home"] .trust-tile p + p { margin-top: 0.6rem; }

/* Features showcase. THE HIDING RULE IS GATED ON .js-on — see the note in index.html: without the
   script every panel after the first is hidden by the plain `hidden` attribute and the first one
   stands alone, so the no-JS state is a complete static panel rather than an empty frame. */
[data-page="home"] .feats { margin-top: 1.4rem; }
[data-page="home"] .feat-tabs { display: flex; flex-wrap: wrap; gap: var(--btn-gap); margin-bottom: 1.2rem; }
[data-page="home"] .feat-tab {
  appearance: none; cursor: pointer; font: inherit;
  background: var(--bg-panel); color: var(--ink-2);
  border: 1px solid var(--line); border-radius: var(--btn-radius);
  padding: 0.5rem 1rem; font-size: 0.9rem; font-weight: 600;
}
[data-page="home"] .feat-tab:hover { color: var(--ink); border-color: var(--accent); }
[data-page="home"] .feat-tab.is-active { background: var(--accent); border-color: var(--accent); color: #fff; }
/* CONTRAST, AND WHY THE NIGHT THEME FLIPS THE TEXT RATHER THAN THE GROUND.
   --accent is a deep indigo on the light theme, where white text sits on it at about 8.5:1. On the
   night theme --accent is the LIGHT lilac the rest of the page reads against, and white on lilac
   measures 2.72:1 - below the 4.5 floor, and caught by tools/visual-sweep.mjs rather than by eye.
   Darkening the chip instead would break its job as the one filled element in the row, so the text
   goes dark and the accent keeps its brightness. */
[data-page="home"][data-theme="night"] .feat-tab.is-active { color: var(--bg); }
[data-page="home"] .feat-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
[data-page="home"] .feat-panel[hidden] { display: none; }
[data-page="home"] .feat-body { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); gap: 2rem; align-items: center; }
[data-page="home"] .feat-copy h3 { font-size: 1.25rem; font-weight: 700; color: var(--ink); margin-bottom: 0.6rem; }
[data-page="home"] .feat-copy p { color: var(--ink-2); }
[data-page="home"] .feat-steps { margin: 1rem 0 0; padding-left: 1.1rem; color: var(--ink-2); font-size: 0.95rem; }
[data-page="home"] .feat-steps li { margin-bottom: 0.4rem; }
[data-page="home"] .feat-shot img { width: 100%; height: auto; display: block; }
[data-page="home"] .feat-note {
  background: var(--bg-panel); border: 1px dashed var(--line);
  border-radius: 14px; padding: 1.6rem; color: var(--ink-2);
}
[data-page="home"] .feat-note strong { color: var(--ink); }
[data-page="home"] .feat-note p + p { margin-top: 0.5rem; }
[data-page="home"] .feat-nav { display: flex; gap: 0.6rem; margin-top: 1.2rem; }
[data-page="home"] .feat-arrow {
  appearance: none; cursor: pointer; font: inherit; font-size: 0.9rem; font-weight: 600;
  background: none; border: 1px solid var(--line); border-radius: 999px;
  padding: 0.45rem 1rem; color: var(--ink-2);
}
[data-page="home"] .feat-arrow:hover { color: var(--ink); border-color: var(--accent); }
[data-page="home"] .feat-arrow:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The chat demo card. Deliberately not a live widget: it is a picture of an exchange, drawn in
   markup so it themes and reflows instead of being a screenshot that does neither. */
[data-page="home"] .chat-demo {
  background: var(--bg-panel); border: 1px solid var(--line);
  border-radius: 16px; padding: 1.4rem; max-width: 42rem; margin-top: 0.6rem;
}
[data-page="home"] .chat-head { display: flex; align-items: center; gap: 0.6rem; padding-bottom: 0.9rem; border-bottom: 1px solid var(--line); }
[data-page="home"] .chat-face {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 9px;
  background: var(--accent); color: #fff; font-weight: 800; font-size: 0.95rem;
}
/* Same flip as the active tab, and for the same measured reason. */
[data-page="home"][data-theme="night"] .chat-face { color: var(--bg); }
[data-page="home"] .chat-name { font-weight: 700; color: var(--ink); }
[data-page="home"] .chat-body { padding-top: 1rem; }
[data-page="home"] .chat-q {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border-radius: 12px 12px 2px 12px; padding: 0.7rem 0.9rem;
  color: var(--ink); margin-left: auto; max-width: 80%; width: fit-content;
}
[data-page="home"] .chat-a { margin-top: 0.9rem; color: var(--ink-2); }
[data-page="home"] .chat-cite { margin-top: 0.7rem; }
[data-page="home"] .cite-chip {
  display: inline-block; border: 1px solid var(--accent); color: var(--accent);
  border-radius: 999px; padding: 0.15rem 0.7rem; font-size: 0.75rem; font-weight: 700;
}
[data-page="home"] .chat-foot { margin-top: 1rem; padding-top: 0.8rem; border-top: 1px solid var(--line); color: var(--ink-2); font-size: 0.85rem; }

/* Two-card rows: pre-seen funnel and method library. */
[data-page="home"] .two-cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.1rem; margin-top: 1.2rem; }
[data-page="home"] .info-card { background: var(--bg-panel); border: 1px solid var(--line); border-radius: 14px; padding: 1.4rem 1.5rem; }
[data-page="home"] .info-card h3 { font-size: 1.05rem; font-weight: 700; color: var(--ink); margin-bottom: 0.6rem; }
[data-page="home"] .info-card p { color: var(--ink-2); }
[data-page="home"] .info-card p + p { margin-top: 0.6rem; }
[data-page="home"] .info-card .btn { margin-top: 0.4rem; }
[data-page="home"] .key-list { list-style: none; padding: 0; margin: 0.4rem 0 0; }
[data-page="home"] .key-list li { color: var(--ink-2); padding: 0.5rem 0; border-bottom: 1px solid var(--line); }
[data-page="home"] .key-list li:last-child { border-bottom: 0; }
[data-page="home"] .key-list strong { color: var(--ink); }
[data-page="home"] .chapter-list { list-style: none; padding: 0; margin: 0.4rem 0 0; counter-reset: none; }
[data-page="home"] .chapter-list li { display: flex; gap: 0.7rem; align-items: baseline; color: var(--ink-2); padding: 0.45rem 0; border-bottom: 1px solid var(--line); font-size: 0.95rem; }
[data-page="home"] .chapter-list li:last-child { border-bottom: 0; }
[data-page="home"] .ch-n { flex: none; width: 1.4rem; text-align: right; font-weight: 700; color: var(--accent); }

/* Email capture. Styled now although the markup is commented out, so uncommenting it is a
   one-line change rather than a design round. */
[data-page="home"] .signup { margin-top: 1.6rem; background: var(--bg-panel); border: 1px solid var(--line); border-radius: 14px; padding: 1.4rem 1.5rem; }
[data-page="home"] .signup-form { display: flex; gap: 0.6rem; flex-wrap: wrap; margin-top: 0.6rem; }
[data-page="home"] .signup-form input {
  flex: 1 1 16rem; font: inherit; padding: 0.6rem 0.8rem;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--bg); color: var(--ink);
}
[data-page="home"] .signup-note { color: var(--ink-2); font-size: 0.9rem; margin-top: 0.6rem; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* Pricing: three cards. NO badge, no "best value", no struck-through figure — the buy surface's
   standing rule is that the tiers carry identical weight. .pc-feature is a ring, not a rank. */
[data-page="home"] .price-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.1rem; margin-top: 1.2rem; }
[data-page="home"] .price-card { background: var(--bg-panel); border: 1px solid var(--line); border-radius: 16px; padding: 1.5rem; display: flex; flex-direction: column; }
[data-page="home"] .price-card.pc-feature { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
[data-page="home"] .pc-name { font-weight: 700; color: var(--ink); }
/* /reset links straight to the December card. The header is sticky, so without this the card's own
   name would land underneath it. */
[data-page="home"] #december-course { scroll-margin-top: 6rem; }
[data-page="home"] .pc-price { font-size: 2.1rem; font-weight: 800; color: var(--accent); margin: 0.3rem 0 0.2rem; }
[data-page="home"] .pc-exam { color: var(--ink-2); font-size: 0.9rem; margin-bottom: 0.6rem; }
[data-page="home"] .pc-list { list-style: none; padding: 0; margin: 0.6rem 0 1.2rem; flex: 1; }
[data-page="home"] .pc-list li { color: var(--ink-2); font-size: 0.95rem; padding: 0.4rem 0; border-bottom: 1px solid var(--line); }
[data-page="home"] .pc-list li:last-child { border-bottom: 0; }
/* THE SAME NEGATIVE-MARGIN TRAP THE DOC PAGE HIT. `.price-sub` opens with margin-top: -1.6rem
   because its original job was to sit tucked under an <h2>. Following the pricing cards instead, it
   was being pulled 1.6rem up INTO them - a real 25.6px overlap, found by the visual sweep and not
   visible in a casual read of either file. */
[data-page="home"] .price-cards + .price-sub { margin-top: 1.1rem; }

/* The one-to-one lessons card, 28 September 2026. It renders only from its <template> in index.html,
   through lessons.js, once both booking links are set. Built to the Figma frames (378:20 desktop,
   378:37 mobile), full content width below the course cards and their lines.
   NO NEW COLOURS. The frames are drawn in the night palette; every value here is a token or a mix of
   tokens, so the card follows the light theme too. #4338CA is the palette's own indigo (the light
   accent). Deliberate departures from the frames, each for a reason the frames could not see:
   · Width is the page's content column (.wrap, 1040px at 1440) like the course cards above it, not
     the frames' 1312; so the columns split 2:3 (left capped at the frames' 470px) instead of a fixed
     470, which at 1040 would leave the price rows three lines deep.
   · The buttons keep the site's one --btn-radius (Sean's button standard, 27 August 2026), not 10px.
   · The outline button is NOT a .btn. `[data-page] a.btn` forces a white label with !important (the
     backstop for FILLED buttons), which vanished on the light theme's white row. Its label is
     var(--ink) instead: near-white on night, as drawn, and dark on light.
   · The columns stack at 900px, the course cards' own breakpoint, so a tablet never gets two
     cramped columns; the rows stack at 720px as specified. The page gutter stays the site's 1.5rem. */
[data-page] .lessons-card {
  --lc-edge: color-mix(in srgb, #4338CA 40%, transparent);
  display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); gap: 48px;
  background: var(--bg-raised, var(--bg-panel)); border: 1px solid var(--lc-edge); border-radius: 16px;
  padding: 36px 40px; margin-top: 1.9rem;
}
[data-page] .lessons-card h3 { font-size: 28px; font-weight: 800; line-height: 1.2; color: var(--ink); margin: 0 0 0.75rem; }
[data-page] .lessons-card .lc-pitch p { font-size: 15px; line-height: 1.4; color: color-mix(in srgb, var(--ink) 55%, var(--ink-2)); margin: 0; }
[data-page] .lessons-card .lc-offers { display: flex; flex-direction: column; gap: 12px; }
[data-page] .lessons-card .lc-row {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  background: var(--bg); border: 1px solid var(--lc-edge); border-radius: 12px; padding: 18px 22px;
}
[data-page] .lessons-card .lc-price { font-size: 17px; line-height: 1.35; color: var(--ink); margin: 0; }
[data-page] .lessons-card .lc-price strong { font-weight: 600; }
[data-page] .lessons-card .lc-amount { font-weight: 800; color: var(--accent); }
[data-page] .lessons-card .lc-pitch { max-width: 470px; }
[data-page] .lessons-card .btn,
[data-page] .lessons-card .lc-btn-outline {
  flex: none; display: inline-block; font-size: 15px; font-weight: 600; line-height: 1.3;
  padding: 12px 22px; border-radius: var(--btn-radius); text-decoration: none; white-space: nowrap;
}
[data-page] .lessons-card .lc-btn-outline {
  background: transparent; color: var(--ink); box-shadow: inset 0 0 0 1.5px var(--accent);
  transition: background 0.15s;
}
[data-page] .lessons-card .lc-btn-outline:hover { color: var(--ink); background: color-mix(in srgb, var(--accent) 12%, transparent); }
[data-page] .lessons-card .lc-small { font-size: 13px; line-height: 1.45; color: var(--ink-2); margin: 4px 0 0; }
@media (max-width: 900px) {
  [data-page] .lessons-card { grid-template-columns: minmax(0, 1fr); gap: 24px; }
}
@media (max-width: 720px) {
  [data-page] .lessons-card { gap: 20px; padding: 26px 22px; }
  [data-page] .lessons-card .lc-row { flex-direction: column; align-items: flex-start; gap: 14px; }
  [data-page] .lessons-card .btn,
  [data-page] .lessons-card .lc-btn-outline { white-space: normal; }
}

/* THE LESSONS PAGE, /lessons, 29 September 2026. The card rules above are scoped to [data-page]
   (were [data-page="home"]) so the same card renders on the document page too; the document palette
   has no --bg-raised, so the card falls back to that palette's own raised surface, --bg-panel. No new
   colours. The document column is 46rem, too narrow for the card's two columns, so there it is always
   the stacked layout the homepage uses below 900px. */
[data-page="doc"] .lessons-card { grid-template-columns: minmax(0, 1fr); gap: 24px; margin-top: 0; }
[data-page="doc"] .lessons-page h2:first-child { margin-top: 2.6rem; }
/* The steps: violet number badges, the accent token on the page's own background. */
[data-page="doc"] .lessons-page .lp-steps { list-style: none; counter-reset: lp-step; margin-top: 1.1rem; padding-left: 0; }
[data-page="doc"] .lp-steps li {
  counter-increment: lp-step; position: relative; padding-left: 2.9rem; min-height: 2rem;
  color: var(--ink-2); line-height: 1.55; padding-top: 0.2rem;
}
[data-page="doc"] .lp-steps li + li { margin-top: 1rem; }
[data-page="doc"] .lp-steps li::before {
  content: counter(lp-step); position: absolute; left: 0; top: 0;
  width: 2rem; height: 2rem; border-radius: 999px;
  display: flex; align-items: center; justify-content: center;
  background: var(--accent-strong); color: #fff; font-weight: 700; font-size: 0.95rem;
}
[data-page="doc"] .lessons-page p { color: var(--ink-2); margin-top: 1.1rem; }
/* The non-affiliation line on a document page: the homepage's treatment, in the document footer. */
[data-page="doc"] .foot-disclaimer {
  color: var(--ink); font-size: 0.92rem; font-weight: 500;
  max-width: 68rem; margin: 0 auto 1.5rem; padding: 0 1.5rem 1.4rem;
  border-bottom: 1px solid var(--line);
}

/* Broken Down cards. */
[data-page="home"] .bd-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.1rem; margin-top: 1.2rem; }
[data-page="home"] .bd-card { display: block; text-decoration: none; background: var(--bg-panel); border: 1px solid var(--line); border-radius: 14px; padding: 1.3rem 1.4rem; }
[data-page="home"] .bd-card:hover { border-color: var(--accent); }
[data-page="home"] .bd-n { font-weight: 800; color: var(--accent); font-size: 0.85rem; letter-spacing: 0.08em; }
[data-page="home"] .bd-card h3 { font-size: 1.02rem; font-weight: 700; color: var(--ink); margin: 0.4rem 0 0.7rem; }
[data-page="home"] .bd-meta { color: var(--ink-2); font-size: 0.85rem; display: flex; align-items: center; gap: 0.5rem; }
[data-page="home"] .chip {
  display: inline-flex; align-items: center; gap: 0.4rem;
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border: 1px solid var(--accent); color: var(--accent);
  border-radius: 999px; padding: 0.1rem 0.55rem;
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.08em;
}
[data-page="home"] .chip .tri {
  display: inline-block; width: 0; height: 0; border-style: solid;
  border-width: 4px 0 4px 6px; border-color: transparent transparent transparent currentColor;
}

@media (max-width: 900px) {
  [data-page="home"] .trust,
  [data-page="home"] .two-cards,
  [data-page="home"] .price-cards,
  [data-page="home"] .bd-cards { grid-template-columns: 1fr; }
  [data-page="home"] .feat-body { grid-template-columns: 1fr; gap: 1.2rem; }
  /* The tab row becomes a horizontal scroller rather than wrapping to three lines on a phone. */
  [data-page="home"] .feat-tabs {
    flex-wrap: nowrap; overflow-x: auto; padding-bottom: 0.4rem;
    scrollbar-width: none; -webkit-overflow-scrolling: touch;
  }
  [data-page="home"] .feat-tabs::-webkit-scrollbar { display: none; }
  [data-page="home"] .feat-tab { flex: none; }
  [data-page="home"] .chat-q { max-width: 100%; }
  [data-page="home"] .feature-boxes { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* The arrows come inside the frame on a phone, where there is no gutter to hang them in. */
  [data-page="home"] .car-prev { left: 4px; }
  [data-page="home"] .car-next { right: 4px; }
}

@media (max-width: 560px) {
  [data-page="home"] .feature-boxes { grid-template-columns: 1fr; }
}

/* ============================================================================================
   THE FLOATING ASK A LAUNCHER — every marketing page, injected by ask-a.js.
   UNSCOPED, unlike almost everything else in this file, because it is the one component that has
   to render identically on [data-page="home"] and [data-page="doc"]. It carries its own copy of
   the drawn A badge for the same reason: .a-mark is defined under the home scope only, and the
   launcher appears on pages that are not home.
   ============================================================================================ */
.aska-root { position: fixed; right: 20px; bottom: 20px; z-index: 60; }
/* HELD WHILE THE HERO IS ON SCREEN, phones only (ask-a.js sets the class; ruled in the relay of
   21 Oct 2026). Opacity and pointer-events only, never display or visibility, so the button keeps
   its place in the tab order; focusing it shows it at once. Fixed position, so nothing shifts. */
.aska-root { transition: opacity 0.2s ease; }
.aska-root.aska-held:not(:focus-within) { opacity: 0; pointer-events: none; }
@media (prefers-reduced-motion: reduce) {
  .aska-root { transition: none; }
}

/* BIGGER, AND IT SAYS WHAT IT IS (Sean, 22 August 2026). At 52px carrying only the badge it read
   as a logo somebody had stuck in the corner. It is now 60px - above the 56px floor - with a
   larger face, and a pill beside it naming the thing. The pill is laid out as part of the button
   rather than floated next to it, so the whole shape is one hit target. */
.aska-launcher {
  display: flex; align-items: center; justify-content: center; gap: 0.5rem;
  min-width: 60px; height: 60px; border-radius: 999px; cursor: pointer;
  padding: 0 0.55rem;
  margin-left: auto;
  background: var(--bg-panel); border: 1px solid var(--line);
  box-shadow: 0 8px 26px rgba(0, 0, 0, 0.22);
}
/* The name: present in the layout only while greeting, on hover, or on keyboard focus. It is
   width-animated rather than display-toggled so the button grows smoothly instead of jumping. */
.aska-pill {
  max-width: 0; overflow: hidden; white-space: nowrap;
  font-size: 0.86rem; font-weight: 700; color: var(--ink);
  transition: max-width 0.22s ease, opacity 0.18s ease;
  opacity: 0;
}
.aska-root.aska-greeted .aska-pill,
.aska-launcher:hover .aska-pill,
.aska-launcher:focus-visible .aska-pill { max-width: 6rem; opacity: 1; }
/* Reduced motion keeps the behaviour and drops the animation. */
@media (prefers-reduced-motion: reduce) {
  .aska-pill { transition: none; }
}
.aska-launcher:hover { border-color: var(--accent); }
.aska-launcher:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* The badge, repeated here so it does not depend on the home-scoped rules. */
.aska-root .a-mark { width: 28px; height: 28px; flex: none; display: block; }
/* The panel header wants the smaller mark - it sits beside 1rem type, not alone in a circle.
   IT NOW HAS ONE AT ALL: Sean named this header as carrying no face. The widget builds its
   markup in JS, so `scripts/build-mark.mjs` injects the plain variant into both draw sites. */
.aska-head .a-mark { width: 22px; height: 22px; margin-right: 0.55rem; }

.aska-panel {
  width: min(370px, calc(100vw - 40px));
  max-height: min(560px, calc(100vh - 120px));
  overflow-y: auto;
  margin-bottom: 12px;
  background: var(--bg-panel); border: 1px solid var(--line); border-radius: 16px;
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.28);
}
.aska-panel[hidden] { display: none; }

.aska-head {
  display: flex; align-items: center;
  padding: 0.9rem 1rem; border-bottom: 1px solid var(--line);
}
.aska-name { font-weight: 700; color: var(--ink); }
.aska-close {
  margin-left: auto; cursor: pointer; background: none; border: 0;
  color: var(--ink-2); font-size: 1.3rem; line-height: 1; padding: 0 0.2rem;
}
.aska-close:hover { color: var(--ink); }
.aska-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.aska-body { padding: 1rem; }
.aska-greeting { color: var(--ink); font-weight: 600; margin-bottom: 0.8rem; }
.aska-list { display: grid; gap: 0.5rem; }
.aska-q {
  width: 100%; text-align: left; cursor: pointer; font: inherit; font-size: 0.92rem;
  background: none; color: var(--ink);
  border: 1px solid var(--line); border-radius: 10px; padding: 0.6rem 0.8rem;
}
.aska-q:hover { border-color: var(--accent); }
.aska-q:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.aska-q[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); font-weight: 600; }
.aska-a {
  color: var(--ink-2); font-size: 0.9rem; line-height: 1.5;
  padding: 0.6rem 0.8rem 0.2rem;
}
.aska-a[hidden] { display: none; }
.aska-foot {
  margin-top: 1rem; padding-top: 0.8rem; border-top: 1px solid var(--line);
  color: var(--ink-2); font-size: 0.8rem;
}

@media (max-width: 560px) {
  .aska-root { right: 14px; bottom: 14px; }
}

/* ============================================================================================
   NAV LABEL ON TWO LINES, AND THE ICON-ONLY THEME TOGGLE (Sean, 22 August 2026).
   The nav had run out of horizontal room: "SBL Broken Down (free resources)" beside a written
   "Dark theme" was two long strings competing in one bar. The word free still has to be visible
   without clicking, so it drops to a second, quieter line rather than disappearing.
   ============================================================================================ */
.nav-links a .nav-sub {
  display: block; font-size: 0.68rem; font-weight: 500; line-height: 1.15;
  margin-top: 1px; color: var(--ink-2); letter-spacing: 0.01em;
}
.nav-links a:hover .nav-sub { color: inherit; }
/* The bar centres on the single-line links, so the two-line one aligns from its first line. */
[data-page="home"] .nav-links, [data-page="doc"] .nav-links { align-items: flex-start; }

/* THE TOGGLE IS A 34px SQUARE NOW. It kept a text button's padding when it held a word; an icon
   in that box drifts off the optical centre of the row, so the box is sized to the icon. */
.theme-btn { display: inline-flex; align-items: center; justify-content: center; }
[data-page="home"] .theme-btn, [data-page="doc"] .theme-btn {
  width: 34px; height: 34px; padding: 0; border-radius: 999px;
  text-decoration: none; color: var(--ink-2);
}
[data-page="home"] .theme-btn:hover, [data-page="doc"] .theme-btn:hover { color: var(--ink); }
[data-page="home"] .theme-btn:focus-visible, [data-page="doc"] .theme-btn:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
.theme-icon { width: 18px; height: 18px; display: block; }

/* THE LINK UNDER A FEATURE PARAGRAPH HAD NO AIR (Sean, 22 August 2026). It was already its own
   <p>, so the markup was right and the spacing was missing: .feat-copy set a colour and nothing
   else, so consecutive paragraphs butted together and the link read as the tail of the sentence
   above it. Gap first, then the link sits as its own line rather than as continued prose. */
[data-page="home"] .feat-copy p + p { margin-top: 1rem; }
[data-page="home"] .feat-copy p > a {
  display: inline-block; color: var(--accent); font-weight: 600;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  text-decoration: none;
}
[data-page="home"] .feat-copy p > a:hover { border-bottom-color: var(--accent); }


/* ── THE FACES, 22 August 2026 (Sean's ruling; committed for his Cloudflare upload) ──────────────
   The lockup's tab and the four feature cards carry the mascot artwork instead of the drawn A.
   The artwork IS a violet folder with its own tab, so the CSS folder underneath each one is
   removed rather than covered — background transparent and the ::before tab off, or the page
   would draw a second tab three pixels above the picture's own.
   These sit at the END of the sheet on purpose: the .tab rules are declared once per page scope
   ([data-page="home"], [data-page="doc"], …) at equal specificity, so the cascade's last-wins is
   what lets one block override all of them without out-specifying anything. */
/* No tile, no clip: the folder path draws its own corners on transparency, and a rounded
   overflow box here would shave them. */
.lockup .tab { width: 22px; height: 22px; }
.lockup .tab::before { display: none; }
.lockup .tab .tab-face { width: 100%; height: 100%; display: block; }
.fbox-face { width: 44px; height: 44px; display: block; }

/* ---------- student reviews: the hero column and the £97 inset, 19 October 2026 ----------
   Figma 389:21 desktop, 389:55 phone, 386:171 £97 card. The reviews moved out of their own section
   into the hero; the classes are the ones that section used. One card per reviewer.
   STARS ARE EACH REVIEW'S OWN RATING, as plain glyphs in a green that is ours: Trustpilot's boxed
   star mark is its brand asset and is not on the free plan. They carry role="img" and a label, so
   the rating never rests on colour. In the hero they render on the night band (tokens pinned on
   .hero-park); the values below serve the £97 inset, which follows the page theme. The light green
   is #00774F because #00875A measured 4.28:1 on the inset's grey and the sweep caught it. */
[data-page="home"], [data-page="home"][data-theme="light"] { --rv-border: #6366F1; --rv-star: #00774F; --rv-inset: #F6F8FA; }
[data-page="home"][data-theme="night"] { --rv-border: #3730A3; --rv-star: #00B67A; --rv-inset: #0B1220; }

[data-page="home"] .hero-reviews { min-width: 0; }
[data-page="home"] .hr-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.35rem 1rem; margin-bottom: 0.9rem; }
/* An <h2> for the outline, drawn as the frame's eyebrow; the specificity beats the home h2 rule. */
[data-page="home"] .hero-reviews .rv-eyebrow {
  font-size: 0.8125rem; font-weight: 500; letter-spacing: 0.12em; line-height: 1.4;
  text-transform: uppercase; color: var(--accent); margin: 0;
}
[data-page="home"] .rv-link { color: var(--accent); font-weight: 600; font-size: 0.875rem; text-decoration: none; }
[data-page="home"] .rv-link:hover, [data-page="home"] .rv-link:focus-visible { text-decoration: underline; }

/* Side by side while each card keeps ~15rem; below that they stack, so the two-column hero between
   the phone break and ~1100px never squeezes a quote into a sliver. */
[data-page="home"] .rv-pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 1rem; align-items: stretch; }
[data-page="home"] .rv-card {
  margin: 0; background: var(--bg-panel); border: 1px solid var(--rv-border); border-radius: 16px;
  padding: 20px 22px; display: flex; flex-direction: column;
}
/* The lead card (Jiwan's): full width above the pair, 2px violet border, 17px quote (frame 389:21). */
[data-page="home"] .rv-card.rv-feature { border: 2px solid var(--accent-strong); padding: 24px 26px; margin-bottom: 1rem; }
[data-page="home"] .rv-feature .rv-quote p { font-size: 1.0625rem; line-height: 1.55; }
[data-page="home"] .rv-stars { display: block; color: var(--rv-star); font-size: 1rem; letter-spacing: 0.12em; line-height: 1; margin-bottom: 0.8rem; }
[data-page="home"] .rv-quote { margin: 0; flex: 1; }
[data-page="home"] .rv-quote p { font-size: 0.875rem; line-height: 1.5; color: var(--ink); }
/* Pinned to the card's foot by the quote's flex: 1, so the two names line up. */
[data-page="home"] .rv-by { margin-top: 1rem; display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.2rem 0.65rem; }
[data-page="home"] .rv-name { font-weight: 600; color: var(--ink); font-size: 0.875rem; }
[data-page="home"] .rv-src { color: var(--accent); font-size: 0.8rem; text-decoration: none; }
[data-page="home"] .rv-src:hover, [data-page="home"] .rv-src:focus-visible { text-decoration: underline; }
[data-page="home"] .rv-foot { margin-top: 0.9rem; font-size: 0.8125rem; color: var(--ink-2); }

/* Q4's inset on the £97 card: above the button, inside the card's own padding. */
[data-page="home"] .rv-inset {
  margin: 0 0 1rem; background: var(--rv-inset); border-left: 3px solid var(--accent-strong);
  border-radius: 10px; padding: 0.9rem 1rem;
}
[data-page="home"] .rv-stars-sm { font-size: 0.95rem; margin-bottom: 0.6rem; }
[data-page="home"] .rv-inset .rv-quote p { font-size: 0.875rem; line-height: 1.5; }
[data-page="home"] .rv-by-inline { margin-top: 0.6rem; font-size: 0.8rem; color: var(--ink-2); }
/* The inline quote at the top of Pricing and through the page (20 Oct 2026). */
[data-page="home"] .rv-inset.rv-inline { max-width: 46rem; margin: 0 0 1.4rem; }
/* In the light theme the page's --accent is indigo #4338CA, 7.2:1 on the inset's grey. */
[data-page="home"] .rv-by-inline .rv-src { font-size: inherit; }

/* ---------- reviews throughout the site, 20 October 2026 (record § A34) ----------
   The inline quote (violet left edge, small plain stars, name, "Read the full review ↗") now sits in
   six homepage sections and on /about and /lessons. 15px quote text, per the brief. One per section. */
[data-page="home"] .rv-inset.rv-inline .rv-quote p { font-size: 0.9375rem; line-height: 1.55; }
/* After a grid of cards rather than a paragraph: a little more air above it. */
[data-page="home"] .rv-inset.rv-inline.rv-after { margin-top: 1.6rem; }

/* The same component on the doc pages (/about, /lessons). Their own blockquote rule
   ([data-page="doc"] .article blockquote: a left rule, padding, 1.03rem) is undone for the quote
   inside, so the component draws one violet edge and not two. */
[data-page="doc"], [data-page="doc"][data-theme="light"] { --rv-star: #00774F; --rv-inset: #F6F8FA; }
[data-page="doc"][data-theme="night"] { --rv-star: #00B67A; --rv-inset: #101A2E; }
[data-page="doc"] .rv-inset.rv-inline {
  margin: 1.4rem 0 0; max-width: 46rem; background: var(--rv-inset);
  border-left: 3px solid var(--accent-strong); border-radius: 10px; padding: 0.9rem 1rem;
}
[data-page="doc"] .rv-inline .rv-stars { display: block; color: var(--rv-star); font-size: 0.95rem; letter-spacing: 0.12em; line-height: 1; margin-bottom: 0.6rem; }
[data-page="doc"] .article .rv-inline .rv-quote { border-left: 0; padding: 0; margin: 0; font-size: inherit; }
[data-page="doc"] .rv-inline .rv-quote p { font-size: 0.9375rem; line-height: 1.55; color: var(--ink); margin: 0; }
[data-page="doc"] .rv-inline .rv-by-inline { margin-top: 0.6rem; font-size: 0.8rem; color: var(--ink-2); }
[data-page="doc"] .rv-inline .rv-src { color: var(--accent); text-decoration: none; }
[data-page="doc"] .rv-inline .rv-src:hover, [data-page="doc"] .rv-inline .rv-src:focus-visible { text-decoration: underline; }
/* Above the video on /pre-seen-analysis (§ A39): the sitting banner already sets the gap above it,
   so the quote takes the banner's own gap below instead. */
[data-page="doc"] .rv-inset.rv-inline.rv-top { margin: 0 0 1.6rem; }

/* ---------- the reviews page, /reviews, 20 October 2026 (Figma 397:21; record § A36) ----------
   Every review in full, in cards. A doc page, so the doc tokens apply in both themes; it widens the
   doc column to carry three cards. Each review's stars are its own rating as plain glyphs, in the
   doc pages' --rv-star green (set with the inline quote on 20 Oct). No score, no count, no logo. */
[data-page="doc"], [data-page="doc"][data-theme="light"] { --rv-border: #6366F1; }
[data-page="doc"][data-theme="night"] { --rv-border: #3730A3; }
[data-page="doc"] .wrap.rvp { max-width: 72rem; }
[data-page="doc"] .rvp-eyebrow {
  font-size: 0.8125rem; font-weight: 500; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--accent); margin-bottom: 0.6rem;
}
[data-page="doc"] .rvp h1 { font-size: clamp(1.6rem, 3vw, 2.1rem); font-weight: 800; margin-bottom: 0.8rem; }
[data-page="doc"] .rvp-intro { color: var(--ink-2); font-size: 1.05rem; max-width: 40rem; margin-bottom: 2rem; }

/* Three explicit columns, as the frame stacks them. align-items: start keeps each column its own height. */
/* Uneven widths on purpose (§ A42, eight reviews): the cards are grouped and the widths tuned until the
   three end level. Measured at 1366px with Inter loaded: 1668 / 1663 / 1640px (the § A40 widths,
   1.06 : 0.96 : 0.98, gave 1416 / 1731 / 1778). */
[data-page="doc"] .rvp-grid { display: grid; grid-template-columns: minmax(0, 0.92fr) minmax(0, 1fr) minmax(0, 1.08fr); gap: 1.25rem; align-items: start; }
[data-page="doc"] .rvp-col { display: flex; flex-direction: column; gap: 1.25rem; min-width: 0; }
[data-page="doc"] .rvp-card {
  background: var(--bg-panel); border: 1px solid var(--rv-border); border-radius: 16px; padding: 22px 24px;
}
[data-page="doc"] .rvp-card.rvp-lead { border: 2px solid var(--accent-strong); }
[data-page="doc"] .rvp-stars { display: block; color: var(--rv-star); font-size: 1rem; letter-spacing: 0.12em; line-height: 1; margin-bottom: 0.9rem; }
[data-page="doc"] .rvp .rvp-title { font-size: 1rem; font-weight: 600; line-height: 1.35; letter-spacing: 0; color: var(--ink); margin: 0 0 0.6rem; }
[data-page="doc"] .rvp-text { margin: 0; padding: 0; border: 0; }
[data-page="doc"] .rvp-text p { font-size: 0.9rem; line-height: 1.6; color: var(--ink-2); }
[data-page="doc"] .rvp-text p + p { margin-top: 0.9rem; }
/* Younes's review breaks with a single newline, not a paragraph gap: the next line follows on directly. */
[data-page="doc"] .rvp-text p + p.rvp-line { margin-top: 0; }
[data-page="doc"] .rvp-by { margin-top: 1rem; display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.2rem 0.65rem; font-size: 0.85rem; }
[data-page="doc"] .rvp-name { font-weight: 600; color: var(--ink); }
[data-page="doc"] .rvp-src, [data-page="doc"] .rvp-leave { color: var(--accent); text-decoration: none; }
[data-page="doc"] .rvp-src:hover, [data-page="doc"] .rvp-src:focus-visible,
[data-page="doc"] .rvp-leave:hover, [data-page="doc"] .rvp-leave:focus-visible { text-decoration: underline; }
[data-page="doc"] .rvp-end { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 1.5rem; margin-top: 2.5rem; }
[data-page="doc"] .rvp-free { color: var(--ink-2); font-size: 0.9rem; }
[data-page="doc"] .rvp-leave { font-weight: 600; font-size: 0.95rem; }

/* Tablet: two columns, the third column (Elie, Younes) full width beneath them. */
@media (max-width: 1100px) {
  [data-page="doc"] .rvp-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  [data-page="doc"] .rvp-col:nth-child(3) { grid-column: 1 / -1; }
}
/* Phone: one column. The column wrappers dissolve and each card's order sets the sequence:
   Megan, Aashutosh, Jiwan, Sayed, Younes, Elie, Muzzammil, Trustpilot reviewer. */
@media (max-width: 700px) {
  [data-page="doc"] .rvp-grid { display: flex; flex-direction: column; gap: 1rem; }
  [data-page="doc"] .rvp-col { display: contents; }
  [data-page="doc"] .rvp-o1 { order: 1; }
  [data-page="doc"] .rvp-o2 { order: 2; }
  [data-page="doc"] .rvp-o3 { order: 3; }
  [data-page="doc"] .rvp-o4 { order: 4; }
  [data-page="doc"] .rvp-o5 { order: 5; }
  [data-page="doc"] .rvp-o6 { order: 6; }
  [data-page="doc"] .rvp-o7 { order: 7; }
  [data-page="doc"] .rvp-o8 { order: 8; }
  [data-page="doc"] .rvp-card { padding: 20px; }
}