/* =============================================================================
   site-reading.css  ·  THE PROSE ARCHETYPE for public document pages
   =============================================================================
   One reading layout for the five standalone document pages that moved onto
   public_base.html on 2026-08-07: /privacy-policy, /terms-of-service, /faq,
   /contact and /pitch-deck.

   WHAT IT REPLACES
   ------------------------------------------------------------------------
   Each of those pages carried its own ~400-line inline <style> opening with a
   PRIVATE token block (--bg-primary #0a0e27, --accent-cyan, --text-primary,
   --gradient-primary, --glass-*, --blur-intense). Five near-identical forks,
   already drifting: --blur-intense was blur(12px) on contact and blur(20px) on
   the other four. None of them read the real contract, so the page black was
   #0a0e27 rather than --bg-page (#070a1c), and #764ba2 — a MARK colour that
   site-tokens.css explicitly bans as a UI accent — painted every h1, every
   section-number badge, every card top-rule and every button.

   Everything here reads site-tokens.css. There is no private palette.

   LOAD ORDER
   ------------------------------------------------------------------------
       fonts.css -> site-tokens.css -> site-base.css -> THIS FILE -> site-chrome.css

   i.e. it is linked from `{% block additional_head %}`, which public_base.html
   renders after site_chrome_css. Everything here is class-scoped under `rd-`,
   so it cannot reach the shared header or footer.

   THIS FILE ADDS NO `!important` AND MUST NOT.

   TYPE
   ------------------------------------------------------------------------
   h1  Clash Display  (--font-display, --type-d2, --track-display)
   h2  General Sans semibold (--font-ui 600, --type-u1)
   body  the READING tier: --type-r1 / --lh-read / --measure (66ch; the two
         legal pages override it to 68ch page-scoped, see section 1.1)

   CONTRAST
   ------------------------------------------------------------------------
   #00f2fe on rgba(255,255,255,.85) over #070a1c is 1.004:1 — cyan and body
   ink are separated by HUE ALONE, which fails WCAG 1.4.1. Every inline link
   therefore carries a permanent underline; the underline, not the colour, is
   what identifies it. Cyan against --ink-3 is 1.95:1, under the 3:1 floor for
   a UI state boundary, so nothing here signals state with cyan alone either —
   every state change also moves a border, a fill or a weight.

   NO backdrop-filter. A blurred translucent card over navy is the clearest
   marker of a templated dark SaaS page, and it flattens the ink ladder by
   lifting the surface luminance under the text.

   COLOUR · P2  (2026-08-07)
   ------------------------------------------------------------------------
   The 2026-08-07 migration stripped ALL non-cyan colour off these five pages,
   because the only other hue the contract had was #764ba2, which measures
   3.08:1 and is banned as a UI accent. site-tokens.css now carries THE
   SEMANTIC COLOUR SYSTEM, so the pages can say things again. Every colour
   below comes from that block. There is not one hue hex in this file.

   The five roles are surfaced here as one modifier family, `.rd-lv--*`,
   which sets a `--rd-lvl-*` group that .rd-note, .rd-callout, .rd-card,
   .rd-rung and the page-private FAQ/pitch components all read. Nothing reads
   --sem-* directly, so re-pointing a role is a five-line change.

     .rd-lv--entry     ladder 1 · arriving, the way in     · cyan
     .rd-lv--build     ladder 2 · working inside           · violet
     .rd-lv--master    ladder 3 · confirmed / you have it  · mint
     .rd-lv--caution   interrupt · read before you act     · amber
     .rd-lv--halt      interrupt · this did not work       · rose

   WHY EVERY LEVEL CARRIES A SECOND CHANNEL, MEASURED
   ------------------------------------------------------------------------
   Desaturate the five and they collapse into TWO bands, not five:

     entry   #00f2fe  L .7066      caution #ffcf5c  L .6666
     master  #69f0ae  L .6838      build   #b388ff  L .3441
                                   halt    #ff8095  L .3887

   entry vs master is 1.031:1, master vs caution 1.024:1, build vs halt
   1.113:1. Those are not differences, they are rounding. So hue NEVER carries
   a distinction on its own here. Three structural channels do:

     · RULE WEIGHT   --sem-rule-ladder (3px) vs --sem-rule-interrupt (5px).
                     A 1.67x step, read before hue is processed at all.
     · ICON SHAPE    ladder icon tiles are circles (--r-full), interrupt tiles
                     are squircles (--r-sm), and the templates pair them with
                     shape-distinct glyphs (triangle for an interrupt).
     · PIP COUNT     .rd-rung. Ordered rungs cannot be expressed by hue at
                     all. One / two / three filled discs against hollow rings.
                     Interrupts take no pips and a glyph instead, because they
                     are not ON the ladder.

   BORDER TIER · WHY -edge IS NOT USED EVERYWHERE
   ------------------------------------------------------------------------
   The contract's -edge alphas are calibrated to ~3:1 against the PAGE black.
   Measured against the lighter surfaces this file actually paints on, two of
   them miss the 1.4.11 floor:

     --sem-build-edge  page 3.01 · sf-1 3.00 · sf-2 2.96 · sf-3 2.85
     --sem-halt-edge   page 3.05 · sf-1 3.04 · sf-2 3.00 · sf-3 2.90

   So -edge is used ONLY where the element sits on --sf-1 or the page, and
   anything on --sf-2 or brighter takes the SOLID hue, which measures 6.05:1
   (build, the worst case) to 13.44:1 everywhere. This is a call-site
   decision, not a token change: site-tokens.css is not touched by this pass.
   ============================================================================= */

/* =============================================================================
   0 · THE LEVEL MODIFIER
   -----------------------------------------------------------------------------
   The default lives at :root, and that is load-bearing. Custom properties
   INHERIT, so a default declared here is beaten by a .rd-lv--* on any ancestor
   AND by one on the element itself. Declaring the same defaults on .rd-note
   etc. instead would make every component immune to its own section's level,
   because a direct declaration always beats an inherited one however far up.
   (Same reasoning, and the same mistake avoided, as guide-system.css § 0.)
   ============================================================================= */
:root {
  --rd-lvl:      var(--sem-entry);
  --rd-lvl-wash: var(--sem-entry-wash);
  --rd-lvl-line: var(--sem-entry-line);
  --rd-lvl-edge: var(--sem-entry-edge);
  --rd-lvl-rule: var(--sem-rule-ladder);
  --rd-lvl-icon-r: var(--r-full);   /* circle = on the ladder */
}

.rd-lv--entry {
  --rd-lvl:      var(--sem-entry);
  --rd-lvl-wash: var(--sem-entry-wash);
  --rd-lvl-line: var(--sem-entry-line);
  --rd-lvl-edge: var(--sem-entry-edge);
  --rd-lvl-rule: var(--sem-rule-ladder);
  --rd-lvl-icon-r: var(--r-full);
}

.rd-lv--build {
  --rd-lvl:      var(--sem-build);
  --rd-lvl-wash: var(--sem-build-wash);
  --rd-lvl-line: var(--sem-build-line);
  --rd-lvl-edge: var(--sem-build-edge);
  --rd-lvl-rule: var(--sem-rule-ladder);
  --rd-lvl-icon-r: var(--r-full);
}

.rd-lv--master {
  --rd-lvl:      var(--sem-master);
  --rd-lvl-wash: var(--sem-master-wash);
  --rd-lvl-line: var(--sem-master-line);
  --rd-lvl-edge: var(--sem-master-edge);
  --rd-lvl-rule: var(--sem-rule-ladder);
  --rd-lvl-icon-r: var(--r-full);
}

/* The two interrupts take the HEAVIER rule and a SQUARED icon tile. That pair
   is the non-colour half of "this is not a rung on the path, this is a stop". */
.rd-lv--caution {
  --rd-lvl:      var(--sem-caution);
  --rd-lvl-wash: var(--sem-caution-wash);
  --rd-lvl-line: var(--sem-caution-line);
  --rd-lvl-edge: var(--sem-caution-edge);
  --rd-lvl-rule: var(--sem-rule-interrupt);
  --rd-lvl-icon-r: var(--r-sm);
}

.rd-lv--halt {
  --rd-lvl:      var(--sem-halt);
  --rd-lvl-wash: var(--sem-halt-wash);
  --rd-lvl-line: var(--sem-halt-line);
  --rd-lvl-edge: var(--sem-halt-edge);
  --rd-lvl-rule: var(--sem-rule-interrupt);
  --rd-lvl-icon-r: var(--r-sm);
}

/* -----------------------------------------------------------------------------
   THE RUNG · pips, the ordered channel
   -----------------------------------------------------------------------------
   Hue cannot express order: entry / build / master are 1.03:1 and 1.02:1 apart
   in luminance. A countable filled-disc-vs-hollow-ring run is pure shape and
   survives greyscale, 1-bit rendering and every kind of colour blindness.
   Always aria-hidden: the heading beside it already carries the same meaning in
   words, and a screen reader announcing "three bullets" is noise.
   -------------------------------------------------------------------------- */
.rd-rung {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: var(--sem-pip-gap);
}

.rd-pip {
  width: var(--sem-pip);
  height: var(--sem-pip);
  border-radius: var(--r-full);
  /* SOLID hue, not -edge: an unfilled ring is a 1.5px hairline and is the
     ONLY thing carrying the "rung not reached" state, so it takes the tier
     that measures 6.05:1 at worst rather than the ~3:1 boundary tier. */
  border: 1.5px solid var(--rd-lvl);
  background: transparent;
}

.rd-pip--on { background: var(--rd-lvl); }

/* The interrupt marker. NOT a pip run — interrupts are off-ladder, so they get
   a glyph in a squared tile instead, which is a shape difference from the
   round pips before any hue is read. */
.rd-rung--interrupt {
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: var(--r-sm);
  background: var(--rd-lvl-wash);
  border: 1px solid var(--rd-lvl);
  color: var(--rd-lvl);
  font-size: var(--type-m3);
}

/* =============================================================================
   1 · SHELL
   -----------------------------------------------------------------------------
   Replaces the Bootstrap grid these pages used for layout (`container`, `row`,
   `col-xl-8 col-lg-10 col-md-11`) — about 20 class instances across all five,
   and the ONLY thing Bootstrap was doing on any of them. Zero Bootstrap JS
   components were in use.
   ============================================================================= */
.rd-shell {
  width: 100%;
  max-width: 1160px;
  margin: 0 auto;
  padding:
    clamp(28px, 4vw, 56px)
    clamp(20px, 5vw, 48px)
    clamp(64px, 8vw, 112px);
}

/* -----------------------------------------------------------------------------
   1.1 · THE DOCUMENT SHELL   (2026-08-19, /terms-of-service + /privacy-policy)
   -----------------------------------------------------------------------------
   THE DEFECT IT FIXES, MEASURED AT 1920x1000
   --------------------------------------------------------------------------
   shell 380-1540 · rail 428-613 · prose 653-1267 · title block 428-1492. The
   rail and the prose are a single visual object, and that object ran
   428 -> 1267. So the page had 428px of margin on the left and 653px on the
   right: a reading group that was not centred on anything, sitting inside a
   title block that was 1064px wide while the text under it was 615px. The
   client's words were "so much empty space on both sides" and "scrambled
   everything into this one small rectangle in middle". Both are accurate.

   The cause was that TWO different things were being centred independently.
   `.rd-shell` centred a 1064px content box on the page; `.rd-prose` centred a
   615px column inside that content box; and then the rail was hung in the left
   gutter by `position: absolute`, which by definition cannot be seen by either
   centring. Every element was individually centred and the composition was
   not.

   THE RULE THIS APPLIES, AND IT IS ONE RULE
   --------------------------------------------------------------------------
       THE SHELL'S CONTENT BOX IS THE READING GROUP.

   `--rd-group` is rail + gap + measure. The shell's max-width is that plus its
   own horizontal padding, so the content box and the group are the same box.
   Everything else falls out of it with no further arithmetic:

     · the group is centred on the page, because `.rd-shell` centres itself;
     · the title block is the content box, so it is exactly the group's width
       and shares its left and right edges;
     · `.rd-wide` (the three /privacy-policy tables) is bounded by the prose
       and can no longer break out into the gutter the rail lives in, which
       retires the collision recorded as platform trap 18 on these two pages;
     · there is no residual slack for anything to drift into.

   Measured after, at 1920: rail 488-718, prose 762-1432, title block 488-1432,
   margins 488 left and 488 right. Three shared edges and a symmetric frame.

   WHY THE SHELL IS DERIVED RATHER THAN WIDENED
   --------------------------------------------------------------------------
   Letting the shell keep growing past 1160px on a large viewport was the
   other option and it is the wrong one HERE, because once every element is
   pinned to the group there is nothing to put in the extra width. A wider
   shell would only re-open the gap between the title block and the text,
   which is the defect. So the number is derived from the type instead: change
   `--measure` or `--rd-rail-w` and the frame follows. 1160px was a magic
   number; `--rd-group` is not.

   The 96px is `.rd-shell`'s own `clamp(20px, 5vw, 48px)` at its ceiling, which
   is reached at a 960px viewport, i.e. everywhere this cap can bind. Below
   that the shell is narrower than the cap anyway and the value is inert.
   -------------------------------------------------------------------------- */
.rd-shell--doc {
  /* 2026-08-19 · 66ch -> 72ch, PAGE-SCOPED, NOT A TOKEN CHANGE.
     `--measure` is declared here rather than in site-tokens.css so /faq,
     /contact and /pitch-deck keep the 66ch they were designed at.

     Read the number carefully, because the previous docblock in section 6.6
     got it wrong. `ch` resolves against the element the length is USED on, and
     `max-width: var(--measure)` is used on `.rd-prose`, whose own font-size is
     the inherited 16px UI face, NOT the 19px reading face that `.rd-prose p`
     sets. So `66ch` was 66 x 9.31px = 614.6px, which at the 19px reading face
     is 55.6 reading-`ch`. It was never 66 characters of anything.

     What matters for WCAG 1.4.8 is the count of characters actually on a line,
     so that is what was measured, by walking the rendered text of every body
     paragraph and grouping the per-character client rects into visual lines:
     at 614.6px the worst line was 68 characters on /terms-of-service and 72 on
     /privacy-policy. /privacy-policy was already within 8 of the 80 ceiling,
     which is the real constraint on how far this can go.

     The candidates were swept by re-measuring every body paragraph and list
     item at each width, at 1920. Worst line on the page:

       declared   px      /terms-of-service   /privacy-policy
       66ch     614.6           77                  75      (before)
       68ch     633.2           78                  77      <- chosen
       69ch     642.5           80                  77
       70ch     651.8           80                  81      over
       72ch     670.5           83                  82      over

     69ch is the true maximum, and it lands /terms-of-service EXACTLY on the
     80 ceiling with no tolerance at all for a font-metric difference on
     another machine. 68ch buys two characters of headroom for nine pixels of
     width, which is not a trade worth arguing about. The measure is therefore
     only 3% wider than it was: nearly all of the space this pass recovers
     comes from the composition, not from the line length, and that is the
     honest answer. A legal document does not get to be wide. */
  --measure: 68ch;

  /* The rail. 230px is up from 184.5px, which is worth two things at once: the
     19-item /terms-of-service list drops from seven wrapped labels to two, and
     a shorter list is a list that overflows a short viewport less often. */
  --rd-rail-w: 230px;
  --rd-rail-gap: 44px;
  --rd-group: calc(var(--measure) + var(--rd-rail-gap) + var(--rd-rail-w));

  max-width: calc(var(--rd-group) + 96px);
}

/* THE TITLE BLOCK IS ALWAYS EXACTLY AS WIDE AS THE READING GROUP.
   That is the whole rule, and it holds at every width because the definition
   of "the group" degrades cleanly: at and above the rail breakpoint the group
   is rail + gap + prose and the shell's content box already IS that, so the
   head needs no cap; below it there is no rail beside the text, the group is
   the prose alone, and the head takes the measure.

   Before: title block 1064px over 615px of text, a 1.73:1 mismatch, which is
   most of why the page read as a small rectangle inside a big frame. After:
   1.00:1 at >= 1160px and 1.00:1 below it. */
.rd-shell--doc .rd-head {
  max-width: var(--measure);
  margin-inline: auto;
}

@media (min-width: 1160px) {
  .rd-shell--doc .rd-head {
    max-width: none;
  }
}

/* Prose column. `ch` so the line length follows the rendered face. */
.rd-prose {
  max-width: var(--measure);
  margin-inline: auto;
}

/* Prose that carries grids, cards or tables rather than sentences. */
.rd-wide {
  max-width: 1040px;
  margin-inline: auto;
}

/* =============================================================================
   2 · PAGE HEAD
   -----------------------------------------------------------------------------
   Was a `.privacy-hero` / `.terms-hero` / `.faq-hero` block with a radial
   violet+cyan wash, four absolutely-positioned particles on an infinite 8s
   `float`, and a gradient-clipped h1. The particles are gone: continuous
   motion behind reading text is a WCAG 2.3.3 failure and they were purely
   decorative. The wash is kept, cyan only, and static.
   ============================================================================= */
.rd-head {
  position: relative;
  padding-block: clamp(24px, 4vw, 48px) clamp(28px, 4vw, 44px);
  text-align: center;
  isolation: isolate;
}

.rd-head::before {
  content: '';
  position: absolute;
  /* No horizontal bleed. -12% on each side pushed documentElement.scrollWidth
     20px past clientWidth at a 390px viewport — invisible only because
     site-base.css sets `overflow-x: clip` on <body>, which is a safety net, not
     a licence. The gradient is transparent by 60% anyway, so a flush inset has
     no visible edge. */
  inset: -20% 0 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(ellipse 60% 70% at 30% 20%, var(--accent-wash) 0%, transparent 60%),
    radial-gradient(ellipse 50% 60% at 78% 70%, rgba(88, 185, 255, .05) 0%, transparent 62%);
}

.rd-eyebrow {
  display: inline-block;
  margin-bottom: var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--type-m2);
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--accent);
}

.rd-title {
  font-family: var(--font-display);
  font-size: var(--type-d2);
  font-weight: 600;
  line-height: var(--lh-display);
  letter-spacing: var(--track-display);
  color: var(--ink-1);
  margin: 0;
}

.rd-lede {
  max-width: 46ch;
  margin: var(--space-5) auto 0;
  font-size: var(--type-u3);
  line-height: var(--lh-snug);
  color: var(--ink-3);
}

/* =============================================================================
   3 · SECTIONS
   -----------------------------------------------------------------------------
   Prose, not cards. Sections are separated by a hairline and rhythm rather
   than by 24px-radius panels with a 4px gradient top-rule.
   ============================================================================= */
.rd-section {
  padding-block: clamp(32px, 4vw, 48px);
}

.rd-section + .rd-section {
  border-top: 1px solid var(--line-1);
}

.rd-section > h2 {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin: 0 0 var(--space-5);
  font-family: var(--font-ui);
  font-size: var(--type-u1);
  font-weight: 600;
  line-height: var(--lh-tight);
  letter-spacing: var(--track-tight);
  color: var(--ink-1);
}

.rd-section > h3,
.rd-prose h3 {
  margin: var(--space-6) 0 var(--space-3);
  font-family: var(--font-ui);
  font-size: var(--type-u2);
  font-weight: 600;
  line-height: var(--lh-tight);
  color: var(--ink-1);
}

/* The section numeral. Was a 32px circle filled with the banned
   #764ba2 -> #00f2fe gradient and white bold text. Now a mono numeral on a
   surface fill with its own hairline: it is differentiated by SHAPE and FILL,
   not by hue, so it survives being read in greyscale. */
.rd-num {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: var(--r-full);
  background: var(--sf-2);
  border: 1px solid var(--line-2);
  font-family: var(--font-mono);
  font-size: var(--type-m2);
  font-weight: 600;
  letter-spacing: var(--track-data);
  color: var(--ink-2);
}

/* =============================================================================
   4 · PROSE
   ============================================================================= */
.rd-prose p,
.rd-section p {
  margin: 0 0 var(--space-4);
  font-size: var(--type-r1);
  line-height: var(--lh-read);
  color: var(--ink-2);
}

.rd-prose p:last-child,
.rd-section p:last-child {
  margin-bottom: 0;
}

.rd-prose strong,
.rd-section strong {
  color: var(--ink-1);
  font-weight: 600;
}

.rd-prose ul,
.rd-section ul {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--space-4);
}

.rd-prose li,
.rd-section li {
  position: relative;
  padding-left: 26px;
  margin-bottom: var(--space-3);
  font-size: var(--type-r1);
  line-height: var(--lh-read);
  color: var(--ink-2);
}

.rd-prose li::before,
.rd-section li::before {
  content: '\2192';           /* -> ; the same marker privacy_policy already
                                 used. terms_of_service and pitch_deck both
                                 carried a literal '?' here, a mojibaked copy
                                 of this glyph. */
  position: absolute;
  left: 0;
  top: 0;
  color: var(--accent);
  font-weight: 700;
}

/* -----------------------------------------------------------------------------
   Inline links · WCAG 1.4.1
   -----------------------------------------------------------------------------
   The underline is NOT decoration here, it is the non-colour channel that makes
   the link identifiable. Do not remove it, and do not replace it with a hover-
   only underline: 1.4.1 is about the resting state.

   2026-08-19 · `:not(.rd-back)` IS LOAD-BEARING, DO NOT DROP IT.
   `.rd-backrow` sits INSIDE `.rd-prose` on both legal pages, so `.rd-prose a`
   at (0,1,1) beat section 7's `.rd-back` at (0,1,0) and painted the button's
   label `var(--accent)` on top of `background: var(--accent)`. Measured:
   colour rgb(0,242,254) on background rgb(0,242,254), a 1:1 ratio. It rendered
   as a solid cyan lozenge with no readable text, on /terms-of-service,
   /privacy-policy, /faq and /pitch-deck.

   The fix EXCLUDES the control rather than out-specifying it, because the rule
   below is wrong for it in every respect and not just in its colour: `.rd-back`
   is a button, not a link inside a run of text, so 1.4.1's underline
   requirement does not apply to it and the underline recipe would be noise.
   Excluding it also leaves section 7's resting / hover / focus-visible trio at
   the plain single-class specificity it was written for.

   `:not(:where(.rd-back))` AND NOT `:not(.rd-back)`. THIS IS NOT STYLE.
   A bare `:not(.rd-back)` adds (0,1,0), lifting these selectors from (0,1,1)
   to (0,2,1). That is enough to beat `.rd-toc .rd-toc__link` at (0,2,0), and
   the first build of this fix did exactly that: every one of the 19 contents
   rail items turned cyan and grew an underline, caught in the screenshot pass.
   `:where()` has zero specificity and passes that zero through `:not()`, so
   the selector keeps the (0,1,1) it has always had and nothing downstream
   moves. Any future exclusion added here must be wrapped the same way.
   -------------------------------------------------------------------------- */
.rd-prose a:not(:where(.rd-back)),
.rd-section a:not(:where(.rd-back)),
.rd-link {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .18em;
  text-decoration-color: rgba(0, 242, 254, .45);
  transition: text-decoration-color var(--dur-2) var(--ease-out),
              color var(--dur-2) var(--ease-out);
}

.rd-prose a:not(:where(.rd-back)):hover,
.rd-section a:not(:where(.rd-back)):hover,
.rd-link:hover {
  color: var(--accent);
  text-decoration-color: var(--accent);
}

/* =============================================================================
   5 · NOTICES & CALLOUTS
   -----------------------------------------------------------------------------
   Both components take their colour from the inherited level. The LEFT RULE is
   the level's own weight token, so a caution or a halt is 5px where a ladder
   note is 3px — a difference you read before you resolve the hue. Never text
   colour alone, and never hue alone.

   The two pigments this block used to carry (#f5b13c on .rd-note--warn,
   #ef6a5a on .rd-callout--warn) were invented at this call site and measured
   by nobody. They are gone; --sem-caution / --sem-halt replace them and are
   measured in the contract.
   ============================================================================= */
.rd-note {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  padding: var(--space-5);
  margin-bottom: var(--space-5);
  background: var(--sf-1);
  border: 1px solid var(--line-1);
  border-left: var(--rd-lvl-rule) solid var(--rd-lvl);
  border-radius: var(--r-md);
  font-size: var(--type-u5);
  line-height: var(--lh-snug);
  color: var(--ink-2);
}

/* An interrupt note also takes its level's WASH as the fill. Ladder notes stay
   on the neutral --sf-1. Fill-vs-no-fill is the second structural channel
   between "a note" and "a stop", on top of the 3px/5px rule step. */
.rd-note.rd-lv--caution,
.rd-note.rd-lv--halt {
  background: var(--rd-lvl-wash);
  border-color: var(--rd-lvl-line);
  border-left-color: var(--rd-lvl);
}

.rd-note__icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  /* CIRCLE on the ladder, SQUIRCLE on an interrupt. Shape, set by the level. */
  border-radius: var(--rd-lvl-icon-r);
  background: var(--rd-lvl-wash);
  /* SOLID hue, not -edge. This tile can sit on a level wash over --sf-1, and
     --sem-build-edge / --sem-halt-edge both fall under 3:1 on anything lighter
     than --sf-1 (2.96 / 3.00 on --sf-2). The solid hue is 6.05:1 at worst. */
  border: 1px solid var(--rd-lvl);
  color: var(--rd-lvl);
  font-size: var(--type-u5);
}

.rd-note__meta {
  display: block;
  margin-top: var(--space-2);
  font-size: var(--type-u6);
  color: var(--ink-4);
}

.rd-callout {
  padding: var(--space-5);
  margin: var(--space-5) 0;
  background: var(--sf-1);
  border: 1px solid var(--line-1);
  border-left: var(--rd-lvl-rule) solid var(--rd-lvl);
  border-radius: var(--r-md);
}

.rd-callout p {
  margin-bottom: 0;
  font-size: var(--type-u5);
  line-height: var(--lh-snug);

  /* 2026-08-21 · THE MEASURE IS DECLARED HERE, ON THE 14px PARAGRAPH.
     This is the ONLY place on the reading pages where a WCAG SC 1.4.8 breach
     was live, and it is a direct consequence of the trap section 1 documents:
     `ch` resolves against the font-size of the element the length is USED on.

     `--measure: 68ch` is used on `.rd-prose`, an element at the 16px UI face,
     so it is 633.2px, and it was swept against the 19px READING face that
     `.rd-prose p` sets. That sweep is correct and unchanged. But a callout
     paragraph is 14px, and it inherits the same 633.2px column: minus the
     callout's own 24px padding on each side and its 5px + 1px borders, the
     text box is 579.2px, which at 14px holds 98 characters on
     /privacy-policy and 85 on /terms-of-service. Both over the 80 ceiling.
     The original sweep never saw this because it measured the body copy
     only; the callouts were not in its scope. Nothing about the 19px column
     is wrong, and it is NOT widened or narrowed by this rule.

     Declaring the measure here makes it self-correcting, because 56ch on a
     14px element is 456.3px and on a 19px element would be 619px: the
     declaration is in characters, and characters is what the ceiling is
     counted in.

     SWEPT with the method of record (rendered per-character rects grouped
     into visual lines), at 768/1024/1280/1440/1920/2560/3440 — every one of
     which produced the same number, since the column is fixed above 768.
     Worst line on the page, with the 19px body copy included:

       declared   px      /terms-of-service   /privacy-policy
       none     579.2           85                  98      (before, both over)
       59ch     480.7           79                  80      at the ceiling
       58ch     472.6           79                  80      at the ceiling
       57ch     464.4           77                  77
       56ch     456.3           77                  76      <- chosen
       54ch     440.0           77                  76      no further gain

     56ch is the point where the callout stops being the page's constraint at
     all: at and below it the worst line on either page is the 19px body copy
     (77 on /terms-of-service, 76 on /privacy-policy), so narrowing further
     buys literally nothing and only costs width. Isolated to the callouts,
     56ch measures 69 and 75 respectively.

     58ch is the true maximum and it lands /privacy-policy EXACTLY on 80 with
     no tolerance for a font-metric difference on another machine — which is
     the same trap section 1 rejected 69ch for. The same answer is given
     here for the same reason. */
  max-width: 56ch;
}

/* Same rule as .rd-note: interrupts get the wash, the ladder does not. On
   .rd-callout this matters more, because a callout has no icon tile and so no
   glyph channel — rule weight and fill are the only two it has. That is why no
   page is allowed to put two different LADDER rungs of .rd-callout on one
   screen: they would be separated by hue alone. Privacy's run is
   caution / master / caution, i.e. interrupt / ladder / interrupt, which the
   rule-weight and fill channels both carry. */
.rd-callout.rd-lv--caution,
.rd-callout.rd-lv--halt {
  background: var(--rd-lvl-wash);
  border-color: var(--rd-lvl-line);
  border-left-color: var(--rd-lvl);
}

/* =============================================================================
   6 · CARDS  (contact blocks, info panels)
   ============================================================================= */
.rd-card {
  padding: var(--space-5);
  margin: var(--space-5) 0;
  background: var(--sf-1);
  border: 1px solid var(--line-1);
  border-radius: var(--r-lg);
  transition: background var(--dur-2) var(--ease-out),
              border-color var(--dur-2) var(--ease-out);
}

.rd-card:hover {
  background: var(--sf-2);
  border-color: var(--line-2);
}

.rd-card > h3 {
  margin-top: 0;
}

/* =============================================================================
   6.5 · TABLES  (added 2026-08-18 for /privacy-policy)
   -----------------------------------------------------------------------------
   The privacy policy is the first page in this archetype whose source copy
   contains tables: legal bases (§ 4), retention periods (§ 6) and
   international transfers (§ 7). There was no table pattern here before, so
   this is it, and it is deliberately small: a scroll box, a header band, a row
   hairline. No zebra striping, no cell borders, no hue.

   THE SCROLL BOX IS THE POINT
   --------------------------------------------------------------------------
   `.rd-table` owns the border, the radius and the fill; the <table> inside it
   is bare. That inversion exists so `overflow-x: auto` can live on the same
   element as the radius (border-collapse and border-radius do not co-operate),
   and so a table that cannot fit scrolls INSIDE ITS OWN BOX. The page must
   never gain a horizontal scrollbar because of a table. `--rd-table-min` is the
   width below which columns stop being readable and scrolling starts. It is
   440px, and that number was measured rather than picked: at a 390px viewport
   the scroll box is 348px wide, and 440px is the largest value that still
   leaves the LAST column starting inside the visible area (46px of it for the
   retention table, 81px for transfers). At the 520px first tried, the third
   column began at 357px, i.e. one pixel-row past the fold with no peek at all,
   and a table that is clipped flush at its own edge does not look scrollable.
   The cost is taller rows on mobile, which is the right way to spend it.

   THE BREAKOUT, AND WHY IT IS A FIXED-PX MEDIA QUERY
   --------------------------------------------------------------------------
   `.rd-wide` is the existing hook for "prose that carries grids, cards or
   tables rather than sentences", but on the other four pages it is a SIBLING
   of `.rd-prose`, so its 1040px cap does something. Nested inside `.rd-prose`
   it is capped by the 66ch measure (614.6px measured) and would be inert, so
   the rule below lets it step back out.

   The step-out is only taken at >= 1160px, where `.rd-shell` has stopped
   growing and its content box is a constant 1064px: 1040px of table then
   leaves exactly 12px of slack each side, with no arithmetic to get wrong.
   Below that the shell tracks the viewport, and the only ways to compute the
   available width are `vw` (which includes the classic scrollbar and would
   overflow by its width) or a container query on `.rd-shell` (which would put
   containment on a shell four other pages share). Neither is worth it for a
   few hundred pixels, so under 1160px the table stays in the measure and
   scrolls inside itself instead.

   Nesting is one-way: /faq and /pitch-deck put `.rd-prose` INSIDE `.rd-wide`,
   which the descendant selector below does not match. Only this page is
   affected.
   ============================================================================= */
.rd-table {
  --rd-table-min: 440px;
  overflow-x: auto;
  margin: var(--space-5) 0;
  background: var(--sf-1);
  border: 1px solid var(--line-1);
  border-radius: var(--r-md);
}

.rd-table table {
  width: 100%;
  min-width: var(--rd-table-min);
  border-collapse: collapse;
  font-size: var(--type-u5);
  line-height: var(--lh-snug);
  color: var(--ink-2);
}

.rd-table th,
.rd-table td {
  padding: var(--space-3) var(--space-4);
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--line-1);
}

/* The header band is told apart from the body by FILL and WEIGHT, not by hue —
   same rule as every other component in this file. */
.rd-table thead th {
  font-family: var(--font-ui);
  font-weight: 600;
  color: var(--ink-1);
  background: var(--sf-2);
  border-bottom: 1px solid var(--line-2);
}

.rd-table tbody tr:last-child td,
.rd-table tbody tr:last-child th {
  border-bottom: 0;
}

/* 2026-08-18 · THE ROW HEADER IS A SEMANTIC CHANGE, NOT A VISUAL ONE.
   The first cell of every body row became `<th scope="row">` so a cell-by-cell
   screen-reader pass announces the ROW as well as the column: without it,
   "Article 6(1)(b)" is read against "Legal Basis" and against nothing else,
   and which purpose it applies to is lost. A <th> defaults to bold, which
   would restyle three tables the client has already signed off, so the weight
   and colour are pinned back to the <td> values. The header BAND keeps its
   own fill and 600 weight from the rule above; it is a `thead th` and this
   selector does not reach it. */
.rd-table tbody th {
  font-weight: 400;
  color: var(--ink-2);
}

/* The scroll box is now `tabindex="0"`, which is what makes it operable
   without a mouse (WCAG SC 2.1.1, Level A). site-base.css's global
   `:focus-visible` paints the ring; this only stops the 2px offset ring being
   clipped by the box's own overflow on the focused edge. */
.rd-table:focus-visible {
  outline-offset: -2px;
}

@media (min-width: 1160px) {
  .rd-prose .rd-wide {
    width: 1040px;
    max-width: none;
    margin-inline: calc((100% - 1040px) / 2);
  }
}

/* =============================================================================
   6.6 · CONTENTS RAIL   (added 2026-08-18 for /terms-of-service, /privacy-policy)
   -----------------------------------------------------------------------------
   THE PROBLEM IT SOLVES, MEASURED
   --------------------------------------------------------------------------
   At a 1440px viewport these two pages rendered: shell 1160px, title block
   1064px, tables 1040px, and body text 615px with 413px of EMPTY GUTTER on
   each side. A 615px column was the only thing inside a 1064px frame, so one
   of its two gutters is given a job.

   2026-08-19 · TWO CLAIMS IN THIS BLOCK'S FIRST VERSION WERE WRONG. It said
   "66ch at 19px is 66 characters per line". It is not: `ch` resolves on
   `.rd-prose`, whose font is the inherited 16px UI face, so 66ch was 614.6px,
   which is 55.6 `ch` of the 19px reading face and, measured against the
   rendered text, 67 to 70 actual characters per line. And it said giving one
   gutter a job was enough. It was not: hanging the rail in the LEFT gutter
   made the reading group asymmetric about the page, which is what the client
   rejected. Section 1.1 is the fix, and it is the piece that centres this
   rail; read it before changing any geometry here.

   ONE ELEMENT, TWO LAYOUTS. NEVER TWO COPIES.
   --------------------------------------------------------------------------
   `.rd-toc` is a single <nav> sitting in the DOM between the metadata note and
   the first section.

     <  1160px   normal document flow: a bordered block above the document.
     >= 1160px   `position: absolute` in the left gutter, `sticky` inner.

   Below the breakpoint it is IN FLOW AND VISIBLE, not hidden. The pattern
   research measured Vercel's sticky rail rendering at x=407 in a 390px
   viewport, i.e. entirely outside the viewport, invisible and unreachable.
   GitHub is the one that gets it right, by relocating the same list inline.
   This follows GitHub. `display: none` below the breakpoint was rejected for
   the same reason: the reader who most needs a jump list on a 10,000px
   document is the one holding a phone.

   WHY 1160px, AND WHY `position: absolute`
   --------------------------------------------------------------------------
   1160px is the width at which the group (230 + 44 + 670.5 = 944.5px) plus the
   shell's 96px of padding plus a real page margin all fit. It is also the
   threshold section 6.5 uses, so the two cannot disagree. Since 2026-08-19 the
   shell's content box IS the group at and above this width, so the geometry
   below has no gutter arithmetic left in it at all.

   `absolute` rather than a grid or flex column is deliberate: an out-of-flow
   box cannot change the prose's own height or line breaking, so the measure is
   set in exactly one place and the rail can never push it. The prose's
   HORIZONTAL position is now set explicitly rather than by `auto` margins,
   which is what makes the pair centre as one object.

   `sticky` needs a tall containing block to travel inside, so `.rd-toc` is
   stretched `top: 0; bottom: 0` over the full height of `.rd-prose` and only
   `.rd-toc__inner` is `position: sticky`. The inner caps its own height and
   scrolls itself, because 19 items do not fit a short laptop viewport.

   SECTION 6.5's TABLE BREAKOUT
   --------------------------------------------------------------------------
   Section 6.5 centres `.rd-wide` on the PROSE column at 1040px, which reached
   212.5px into the left gutter, 172.5px further left than the rail's right
   edge. A 1040px prose-centred table and a left rail cannot both exist. The
   2026-08-18 answer was a 839.5px rightward breakout; the 2026-08-19 answer is
   that there is no breakout at all, because the shell's content box and the
   group are now the same box. See the rule at the foot of this section.

   THE SCROLL-SPY   (added 2026-08-19 · defect 1)
   --------------------------------------------------------------------------
   It is built, and the argument for leaving it out was wrong. It lives in
   static/js/reading-toc.js, is the only script these two pages load, and is
   `defer`red, same-origin and handler-free so `script-src 'self'` covers it
   with no CSP change. It sets `aria-current="true"` and `.is-active` on one
   link and does nothing else to the document. With JavaScript off there is no
   highlight and nothing else is affected: the rail is server-rendered, every
   item is a plain in-page anchor, and no rule in this file depends on the
   script having run.
   ============================================================================= */

/* --- the flow layout, which is also the small-screen layout ---------------- */
/* `--rd-rail-gap` is NOT declared here. It used to be, and that was a real
   4px bug: the gap is read in two places, by `.rd-toc`'s own `right` offset and
   by `.rd-prose--rail`'s `margin-left`, and a copy on `.rd-toc` is invisible to
   `.rd-prose--rail` because the prose is the ANCESTOR. The two therefore
   disagreed about how wide the gap was and the rail landed 4px right of the
   content-box edge. It lives on `.rd-shell--doc` (section 1.1) now, which is an
   ancestor of both. The fallback is for a `.rd-toc` on a shell without the
   modifier, which no page currently renders. */
.rd-toc {
  margin: 0 0 var(--space-6);
  padding: var(--space-5);
  background: var(--sf-1);
  border: 1px solid var(--line-1);
  border-radius: var(--r-md);
}

/* The skip link. NOT site-base.css's `.sr-only`: that recipe deliberately has
   no focus escape, and a control whose entire job is to be found by the
   keyboard has to become visible the moment it is reached. Same clip-rect
   recipe, plus the escape. */
.rd-toc__skip {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.rd-toc__skip:focus {
  position: static;
  display: inline-block;
  width: auto;
  height: auto;
  overflow: visible;
  clip: auto;
  margin: 0 0 var(--space-4);
  padding: 6px 12px;
  border: 1px solid var(--line-2);
  border-radius: var(--r-sm);
  background: var(--sf-2);
  color: var(--ink-1);
  font-family: var(--font-ui);
  font-size: var(--type-u6);
  text-decoration: none;
}

/* An <h2>, so the list is reachable from a screen reader's heading list and
   can name its own <nav> through aria-labelledby. The specificity is doubled
   because `.rd-prose p` and `.rd-prose h3` sit in the same cascade. */
.rd-toc .rd-toc__label {
  margin: 0 0 var(--space-4);
  /* Matches the item's own padding-left, so the label sits over the numeral
     column rather than hanging left of the spine. */
  padding-left: var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--type-m2);
  font-weight: 500;
  line-height: 1;
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--ink-4);
}

.rd-toc__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* `.rd-prose li` gives every list item 26px of indent, 12px of bottom margin,
   the 19px reading size and an arrow ::before. None of that belongs on a nav
   item, and a single class cannot outrank a descendant selector, so these two
   rules are written at 0,2,0. */
.rd-toc .rd-toc__item {
  margin: 0;
  padding: 0;
  break-inside: avoid;
}

.rd-toc .rd-toc__item::before {
  content: none;
}

/* 2026-08-19 · the vertical rhythm was tightened from 7px/1.45 to 5px/1.35.
   Nineteen items at the old values are 653px of list, which overflows a
   sticky box on any viewport under 779px tall. At the new values they are
   508px and the threshold drops to 634px. This is half of defect 2's fix; the
   other half is the scrollbar styling in the rail block below. */
.rd-toc .rd-toc__link {
  display: grid;
  grid-template-columns: 1.7em 1fr;
  gap: var(--space-2);
  padding: 5px 0 5px var(--space-4);
  border-left: 1px solid var(--line-1);
  font-family: var(--font-ui);
  font-size: var(--type-u6);
  line-height: 1.35;
  color: var(--ink-3);
  text-decoration: none;
  transition: color var(--dur-2) var(--ease-out),
              background-color var(--dur-2) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out),
              border-left-color var(--dur-2) var(--ease-out);
}

/* Two channels, not one. --ink-3 to --ink-1 is a luminance step that survives
   greyscale; the spine going accent is the second. Never hue alone, the same
   rule as everything else in this file. Nav links carry no resting underline
   because 1.4.1 is about a link inside a run of text, and there is no
   non-link text in this list to tell them apart from. */
.rd-toc .rd-toc__link:hover,
.rd-toc .rd-toc__link:focus-visible {
  color: var(--ink-1);
  border-left-color: var(--accent);
  text-decoration: none;
}

.rd-toc__n {
  font-family: var(--font-mono);
  font-size: var(--type-m3);
  letter-spacing: var(--track-data);
  color: var(--ink-4);
  text-align: right;
}

/* --- THE ACTIVE SECTION  (defect 1, 2026-08-19) ---------------------------
   The 2026-08-18 docblock argued a persistent rail did not need this because
   the heading beside the text already answers "which clause am I in". That
   reasoning does not hold. A jump list you scroll past for ten thousand pixels
   and which never says where you are is not an index, it is a menu, and it
   makes the reader do the mapping the rail exists to do for them.

   THREE CHANNELS, ONLY ONE OF WHICH IS HUE.
   --------------------------------------------------------------------------
     · FILL      transparent -> --sf-1. A luminance change on the row itself.
     · SPINE     1px --line-1 -> 3px accent. The border stays 1px and the extra
                 2px is an inset box-shadow, so the marker triples in weight
                 with ZERO reflow. A `border-left-width` change would move
                 every label 2px sideways as the reader scrolls.
     · INK       --ink-3 -> --ink-1, plus --ink-4 -> --ink-2 on the numeral.
                 A luminance step that survives greyscale.

   Desaturate all three and the active row is still the only one with a fill,
   the only one with a thick spine and the only one at full ink. That is the
   same "never hue alone" rule the rest of this file is built on.

   `font-weight` was considered as a fourth channel and REJECTED. General Sans
   500 -> 600 at 13px changes the label's measured width, and in a 184px text
   column several of the 19 /terms-of-service headings sit within a few pixels
   of their wrap point. Bolding the active row would re-wrap it, change the
   list's height and shift every item below it, on every section boundary, for
   the whole length of the document.

   Selectors are written at (0,2,0) to clear `.rd-prose a`, and the attribute
   and the class are BOTH matched: the attribute is the accessible state and
   the class is what CSS should key on, and neither may drift from the other.
   With JavaScript off neither is ever set, so nothing here applies and nothing
   else changes. */
.rd-toc .rd-toc__link.is-active,
.rd-toc .rd-toc__link[aria-current] {
  color: var(--ink-1);
  background: var(--sf-1);
  border-left-color: var(--accent);
  box-shadow: inset 2px 0 0 var(--accent);
}

.rd-toc .rd-toc__link.is-active .rd-toc__n,
.rd-toc .rd-toc__link[aria-current] .rd-toc__n {
  color: var(--ink-2);
}

/* Two columns between 560px and the rail breakpoint. Nineteen single-column
   items put a full phone screen of navigation in front of section 1; at 768px
   the block is 615px wide and carries two comfortably. Below 560px one column
   is the only honest option and the height is accepted. */
@media (min-width: 560px) and (max-width: 1159.98px) {
  .rd-toc__list {
    columns: 2;
    column-gap: var(--space-6);
  }
}

/* --- the rail ------------------------------------------------------------- */
@media (min-width: 1160px) {
  /* 2026-08-19 · THE GROUP IS WHAT GETS CENTRED, NOT THE PROSE.
     Section 1.1 already made the shell's content box exactly `--rd-group`
     wide, so the group is centred on the page for free. All that is left is
     to put the prose at the group's RIGHT-hand end and leave the rail's
     column to its left. There is no `(100% - group) / 2` term because there
     is no slack to divide: the group fills the content box.

     `margin-right: 0` is written out because `.rd-prose`'s `margin-inline:
     auto` would otherwise centre the column inside the leftover space and
     undo this. Over-constrained margins on a block resolve by ignoring the
     right one in LTR, so the column lands exactly at `margin-left`. */
  .rd-prose--rail {
    position: relative;
    margin-left: calc(var(--rd-rail-w, 230px) + var(--rd-rail-gap, 44px));
    margin-right: 0;
  }

  .rd-prose--rail .rd-toc {
    position: absolute;
    top: 0;
    bottom: 0;
    /* 100% is the prose column, so this puts the rail's RIGHT edge one gap to
       the left of the prose's LEFT edge, which is the shell's content-box left
       edge plus nothing. Rail, title block and every paragraph therefore share
       one left edge and one right edge. */
    right: calc(100% + var(--rd-rail-gap, 44px));
    width: var(--rd-rail-w, 230px);
    margin: 0;
    padding: 0;
    background: none;
    border: 0;
    border-radius: 0;
    /* The stretched box covers the whole left gutter for the height of the
       document. Only the sticky inner should take a pointer. */
    pointer-events: none;
  }

  /* THE SCROLLBAR.  (defect 2, 2026-08-19)
     -----------------------------------------------------------------------
     19 items do not fit a short viewport, so this box has to be able to
     scroll, and Chromium's default scrollbar is a 15px light-grey channel
     down the middle of a black page. The client called it out by name.

     Fixed on both fronts rather than one, because either alone is a half
     answer. TIGHTENING (the 230px rail above, plus the item padding and
     leading below) drops the /terms-of-service list from 653px to 508px, so
     the box now only overflows below a 634px viewport instead of below 779px,
     which covers essentially every laptop. STYLING makes the remaining case
     acceptable rather than ugly: a 6px thumb on a transparent track, in
     --line-3, which is the same hairline the rest of this file draws with.

     It is NOT fixed by clipping. `overflow-y: auto` stays, the thumb is
     draggable, and every item is a real <a>, so a keyboard reaches the
     content by tabbing and the box scrolls to follow the focus ring. The
     scroll-spy also nudges `scrollTop` so the active item stays in view.

     Both syntaxes are declared on purpose. Chromium >= 121 and Firefox honour
     the standard `scrollbar-width`/`scrollbar-color` pair; older Chromium and
     Safari only understand the WebKit pseudo-elements. Whichever engine reads
     which, the result is the same thin hairline. */
  .rd-prose--rail .rd-toc__inner {
    position: sticky;
    top: calc(var(--sh-anchor, 62px) + var(--space-5));
    max-height: calc(100vh - var(--sh-anchor, 62px) - var(--space-6) - var(--space-5));
    overflow-y: auto;
    overscroll-behavior: contain;
    pointer-events: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--line-3) transparent;
  }

  .rd-prose--rail .rd-toc__inner::-webkit-scrollbar {
    width: 6px;
  }

  .rd-prose--rail .rd-toc__inner::-webkit-scrollbar-track {
    background: transparent;
  }

  .rd-prose--rail .rd-toc__inner::-webkit-scrollbar-thumb {
    background: var(--line-3);
    border-radius: var(--r-pill);
  }

  .rd-prose--rail .rd-toc__inner:hover {
    scrollbar-color: var(--ink-4) transparent;
  }

  .rd-prose--rail .rd-toc__inner:hover::-webkit-scrollbar-thumb {
    background: var(--ink-4);
  }

  /* 2026-08-19 · THE TABLE BREAKOUT IS RETIRED ON RAILED PAGES.
     Section 6.5 centres `.rd-wide` on the prose at 1040px; the version of this
     block dated 2026-08-18 replaced that with a 839.5px rightward breakout to
     keep it out of the rail's gutter. Neither is needed now. The shell's
     content box IS the group, so there is nothing to the right of the prose to
     break out INTO, and the composition gains a single unbroken right edge
     shared by the title block, every paragraph and all three tables.

     The cost is measured and small: the three /privacy-policy tables go from
     837px to 670px. None of them has ever scrolled at any width, including at
     613px below the rail breakpoint, because `.rd-table table` is `width:
     100%` and the cells wrap. They get taller, not clipped, and the
     `overflow-x: auto` scroll box stays as the safety net it already was.

     The explicit `max-width: none` is not decoration. Without it a future
     `--measure` past 1040px would silently re-arm section 6.5's cap. */
  .rd-prose--rail .rd-wide {
    width: auto;
    max-width: none;
    margin-inline: 0;
  }
}

/* --- anchor targets -------------------------------------------------------
   The site header is `position: fixed`. Without this the browser's own hash
   jump parks the heading BEHIND it. --sh-anchor is the header's settled
   height, declared once on :root in site-chrome.css; the extra --space-5 is
   air, so the heading lands under the bar rather than against it. */
.rd-prose h2[id],
.rd-prose h3[id],
.rd-prose .rd-section[id] {
  scroll-margin-top: calc(var(--sh-anchor, 62px) + var(--space-5));
}

/* =============================================================================
   7 · BACK LINK
   -----------------------------------------------------------------------------
   Was a pill filled with the banned violet->cyan gradient. Now the site's own
   accent fill with --_ink-on-accent's role colour, which is what site-chrome
   uses on every other accent button.
   ============================================================================= */
.rd-backrow {
  padding-top: clamp(32px, 5vw, 56px);
  text-align: center;
}

.rd-back {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: 13px 26px;
  border-radius: var(--r-pill);
  background: var(--accent);
  color: #04121a;
  font-family: var(--font-ui);
  font-size: var(--type-u5);
  font-weight: 600;
  letter-spacing: var(--track-tight);
  text-decoration: none;
  transition: transform var(--dur-2) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out);
}

.rd-back:hover {
  color: #04121a;
  transform: translateY(-2px);
  box-shadow: 0 10px 28px -10px var(--accent-glow);
}

.rd-back:focus-visible {
  color: #04121a;
}

/* =============================================================================
   8 · REDUCED MOTION
   ============================================================================= */
@media (prefers-reduced-motion: reduce) {
  .rd-card,
  .rd-back,
  .rd-prose a,
  .rd-section a,
  .rd-link,
  .rd-toc .rd-toc__link {
    transition: none;
  }
  /* The scroll-spy's active state is a colour, a fill and a spine width. None
     of them may animate here. This selector is (0,2,0), the same as the rule
     that declares the transition, and it wins on order. */
  .rd-toc .rd-toc__link.is-active {
    transition: none;
  }
  .rd-back:hover {
    transform: none;
  }
}
