/* GENERATED — do not edit. Your change will be overwritten by the next build.
 *
 * Assembled from apps/web/styles/ in the order declared by
 * apps/web/styles/order.txt. Edit the section file that owns the rule; the
 * order file owns the sequence, and the sequence is behavioural — CSS resolves
 * specificity ties by source position.
 *
 * Regenerate:  rangeday build web      (also on every serve-web rebuild)
 * Verify:      rangeday check css      (fails if this file and its parts differ)
 */
/* ==== _legacy-base · reset + the first utility wave · owner: NOBODY ==== */
/* UNOWNED · DELETE-ONLY. No lane owns this file, so no lane may add to it: new
   CSS goes in that lane's own section file, listed in order.txt. Any lane MAY
   delete what its own change stranded. The pool only shrinks — that is the whole
   mechanism by which these files disappear, and an addition here rebuilds the
   shared address the split removed.

   FIRST in order.txt, and that position is load-bearing: the reset and these
   utilities are what every section below overrides. Move it and equal-specificity
   ties across the whole stylesheet flip, because CSS resolves them by source
   order.

   ⚠️ There are TWO utility waves in this stylesheet, not one — the rest are in
   _legacy-pre-kit.css, in the same `bg-* / text-* / gap-*` idiom. They are not
   duplicates and they must NOT be merged into one file: they occupied different
   positions in the single stylesheet, and merging them would move roughly half
   of these declarations past everything between the two, changing which rule
   wins. Two files here is the honest shape of what was already there. */
/* Range Day Pro PWA — minimal utility CSS.
   Basic/functional styling; visual parity effort is a separate future item. */

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

body { font-family: system-ui, -apple-system, sans-serif; line-height: 1.5; }

/* Layout */
.min-h-screen { min-height: 100vh; }
.flex { display: flex; }
.flex-col { flex-direction: column; }
.flex-wrap { flex-wrap: wrap; }
.flex-1 { flex: 1; }
.items-center { align-items: center; }
.items-start { align-items: flex-start; }
.justify-center { justify-content: center; }
.justify-between { justify-content: space-between; }
.gap-1 { gap: 0.25rem; }
.gap-2 { gap: 0.5rem; }
.gap-4 { gap: 1rem; }
.text-center { text-align: center; }
.text-left { text-align: left; }
.block { display: block; }
.hidden { display: none; }
.w-full { width: 100%; }
.h-full { height: 100%; }
.relative { position: relative; }

/* Spacing */
.p-2 { padding: 0.5rem; }
.p-3 { padding: 0.75rem; }
.p-4 { padding: 1rem; }
.p-8 { padding: 2rem; }
.px-3 { padding-left: 0.75rem; padding-right: 0.75rem; }
.px-4 { padding-left: 1rem; padding-right: 1rem; }
.py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; }
.py-3 { padding-top: 0.75rem; padding-bottom: 0.75rem; }
.py-4 { padding-top: 1rem; padding-bottom: 1rem; }
.py-8 { padding-top: 2rem; padding-bottom: 2rem; }
.mb-1 { margin-bottom: 0.25rem; }
.mb-2 { margin-bottom: 0.5rem; }
.mb-4 { margin-bottom: 1rem; }
.mb-6 { margin-bottom: 1.5rem; }
.mt-1 { margin-top: 0.25rem; }
.mt-2 { margin-top: 0.5rem; }
.mt-4 { margin-top: 1rem; }

/* Sizing */
.max-w-md { max-width: 28rem; }
.max-w-lg { max-width: 32rem; }
.mx-auto { margin-left: auto; margin-right: auto; }

/* Typography */
.text-xs { font-size: 0.75rem; line-height: 1rem; }
.text-sm { font-size: 0.875rem; line-height: 1.25rem; }
.text-base { font-size: 1rem; line-height: 1.5rem; }
.text-lg { font-size: 1.125rem; line-height: 1.75rem; }
.text-2xl { font-size: 1.5rem; line-height: 2rem; }
.font-medium { font-weight: 500; }
.font-semibold { font-weight: 600; }
.font-bold { font-weight: 700; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Colors */
.bg-gray-50 { background-color: #f9fafb; }
.bg-gray-100 { background-color: #f3f4f6; }
.bg-white { background-color: #fff; }
.bg-blue-600 { background-color: #2563eb; }
.bg-green-50 { background-color: #f0fdf4; }
.bg-red-50 { background-color: #fef2f2; }
.bg-yellow-50 { background-color: #fffbeb; }
.text-gray-400 { color: #9ca3af; }
.text-gray-500 { color: #6b7280; }
.text-gray-600 { color: #4b5563; }
.text-gray-700 { color: #374151; }
.text-gray-900 { color: #111827; }
.text-white { color: #fff; }
.text-blue-600 { color: #2563eb; }
.text-blue-700 { color: #1d4ed8; }
.text-green-700 { color: #15803d; }
.text-red-600 { color: #dc2626; }
.text-red-700 { color: #b91c1c; }
.text-yellow-800 { color: #92400e; }
.border { border-width: 1px; border-style: solid; }
.border-t { border-top-width: 1px; border-top-style: solid; }
.border-gray-200 { border-color: #e5e7eb; }
.border-gray-300 { border-color: #d1d5db; }
.border-green-200 { border-color: #bbf7d0; }
.border-red-200 { border-color: #fecaca; }
.border-yellow-200 { border-color: #fde68a; }

/* Components */
.rounded { border-radius: 0.25rem; }
.rounded-lg { border-radius: 0.5rem; }
.shadow { box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06); }

/* Forms */
input[type="text"], input[type="email"], input[type="password"], input[type="number"], textarea, select {
    border: 1px solid #d1d5db;
    outline: none;
    font-size: 1rem;
}

/* ⚠️ THE BLUE IS DELIBERATE HERE. Do not retokenise it to a kit colour.
   Checked 2026-08-11 and left as-is.

   The audience decides, not the value. These are bare-element selectors, so
   they reach every input in the app — and the pages that still have bare
   inputs are the UNMIGRATED ones, which paint fixed-white cards
   (`bg-white` is a flat #fff at :67, with no theme override). A kit accent
   here resolves to #A89968 in the DEFAULT DARK theme and lands on #ffffff:
   a focus ring you cannot see, on every form that has not migrated yet.

   #3b82f6 on #ffffff is off-palette. It is not broken. The defect is one
   level up — the pages have not migrated — and this ring rides that
   migration, converting when the page it serves does.

   ⚠️ THE WHOLE CLASS OF FIX IS DEAD, not just the variant someone tried.
   A bare-element selector necessarily applies ONE value across BOTH
   audiences — migrated and unmigrated — so no token is correct on both
   grounds and no in-place edit here can be right. Deleting the rules is
   also out: that removes focus visibility product-wide, which is an
   accessibility regression, not a cleanup. Both attempts and their
   reasons are recorded on #617; read it before reaching for the obvious
   edit a third time.

   Same reasoning as the Pro door's fixed values (:186 block), which names
   the identical failure mode and its own removal trigger. That comment
   stopped this exact regression twice.

   TRACKER: #617. That issue covers BOTH halves of this backfill — the blue
   focus ring and the raw #d1d5db border on the same selectors — and it stays
   OPEN. Its resolution is per-page migration, not a stylesheet edit, so
   nothing here closes it and neither half converts alone.

   Sweep note for whoever tokenises focus properly: 60 focus rules in this
   sheet, 25 already carry a kit token. The remaining 35 are not one job —
   sort them by the ground they land on before touching any. */
input:focus, textarea:focus, select:focus {
    box-shadow: 0 0 0 2px #3b82f6;
    border-color: #3b82f6;
}

/* Buttons */
button { cursor: pointer; border: none; font-size: 1rem; }
.hover\:bg-blue-700:hover { background-color: #1d4ed8; }
.hover\:bg-gray-100:hover { background-color: #f3f4f6; }
.hover\:underline:hover { text-decoration: underline; }
.disabled\:opacity-50:disabled { opacity: 0.5; }
.disabled\:cursor-not-allowed:disabled { cursor: not-allowed; }

/* Links */
a { text-decoration: none; color: inherit; }

/* The pre-kit tab bar lived here — `.tab-bar`, `position: fixed`, paid for by a
   hard-coded `pb-20` (80px) on the content region. Both are DELETED in the Shell
   migration: `RdTabBar` is a flow sibling of the scroll region, so it takes its
   own space and there is no constant to keep in step (03-tshell.md §13 trap 5).
   `.overflow-auto`, `.shadow-top` and `.pb-20` went with it — all four were used
   by nothing else. */

/* Offline banner — in normal flow at the top of a shell's own column, so it
   pushes that shell's content down instead of overlaying the header.

   ⚠️ `--rd-offline-banner-h` LIVED HERE AND IS DELETED, 2026-09-01 (James,
   option 2). It existed because the banner rendered ABOVE the shells, as a
   sibling out of `ConnectivityShell`, and four rules across three parts had to
   subtract it from a `100dvh` frame. The banner is mounted INSIDE each frame
   now — `OfflineBanner`, first in the shell's flex column, the arrangement the
   kit draws (`rd-train-kit.jsx:147-166`) — so there is nothing left to subtract
   and no constant to keep in step. The four consumers went with it: the two
   `calc` rules below and in `s02-tcard.css`, the third in `adm0-mobile.css`,
   and the splash re-top in `s02-splash.css`. */

/* THE AT-REST TONE, not an error. Row 17 of `03 (Mobile) Training`: *"this is
   not an error state and must not look like one … a receipt for something that
   was always happening. No red, no warning triangle"* — the amber this replaces
   (`#fffbeb` / `#fde68a` / `#92400e`) was a fixed-light warning card in a
   themed app, which is both wrong tones at once. Values are `TBanner`'s
   (`rd-train-kit.jsx:186-190`), the same transcription `.rd-squad__banner`
   already carries in `s09-board.css`. */
.offline-banner {
    flex-shrink: 0;
    background: var(--rd-surf2);
    border-bottom: 1px solid var(--rd-line);
    padding: 9px 18px;
    /* ⚠️ A FLOOR, NOT A HEIGHT — ruled 2026-09-01 (James, option 2). It was
       `height: var(--rd-offline-banner-h)` with `overflow: hidden`, because the
       shells subtracted the number and an exact box was what kept the
       subtraction true; a third sentence line was clipped rather than shown.
       Nothing subtracts it now — the strip is a `flex-shrink: 0` child of the
       shell's own column — so the box may grow with wrapped copy and the column
       absorbs it by normal flow. 63 is the same arithmetic the token carried:
       9 + 9 padding, an 11px label line, a 3px gap, TWO 15px sentence lines and
       the 1px hairline; it stays as the floor so a one-line render centres in
       the drawn box (`justify-content: center` below) rather than sitting in a
       shorter one. */
    box-sizing: border-box;
    min-height: 63px;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

/* The label: what happened. Display type, the drawn 8.5px tracked caps. */
.offline-banner__label {
    font-family: 'Archivo', system-ui, sans-serif;
    font-weight: 700;
    font-stretch: 78%;
    text-transform: uppercase;
    letter-spacing: .12em;
    font-size: 8.5px;
    line-height: 11px;
    color: var(--rd-fg2);
}

/* The sentence: what still works. */
.offline-banner__body {
    margin-top: 3px;
    font-family: 'Archivo', system-ui, sans-serif;
    font-size: 10.5px;
    line-height: 15px;
    color: var(--rd-fg3);
    text-wrap: pretty;
}

/* ⚠️ `.offline-banner ~ .rd-shell { height: calc(100dvh - …) }` STOOD HERE AND
   IS DELETED, 2026-09-01 (James, option 2). It was a measured fix for a real
   regression: with the banner rendering ABOVE the shell out of
   `ConnectivityShell`, a `100dvh` frame plus a 63px strip overflowed the glass
   by exactly the strip and the tab bar — the app's PRIMARY NAVIGATION — sat
   below the fold while offline (measured on the built app: bar bottom 1395,
   viewport 1357). The ruling removes the cause instead: the strip is the first
   child of `.rd-shell__frame` now, so the frame's own column shrinks the
   content region and the bar never moves. `.rd-shell` is plainly `100dvh` again
   in both states. */


/* ==== ADM0 · docs/design/kit/ADM0-chrome.md · owner: spine ==== */

/* ---------------------------------------------------------------------------
   The Pro door — the "Switch To Pro" row on Profile.
   Drawn: rd-pro-shell.jsx:36-60 (ScreenProDoor). Spec: ADM0-chrome.md §6.

   ⚠️ THE ADM0 §6 MIGRATION THIS BLOCK KEPT DESCRIBING IS TAKEN, 2026-08-26.
   The block's own history — three stacked corrections, each one re-pinning a
   literal and re-deferring the migration — is left below the rule because the
   reasoning it retires is the reasoning a later reader would otherwise
   reconstruct. What it asked for, verbatim: "--rd-surf ground, --rd-acc border,
   chamfer 8".

   THE GROUND, THE HAIRLINE AND THE CHAMFER NOW COME FROM THE KIT SUBSTRATE, not
   from this file. `profile.rs` wraps the row's contents in `RdChamferBox`
   (`components/kit/chamfer_box.rs`) at `chamfer: 8.0, border: 1.0,
   Stroke::Accent, Fill::Surface` — the same fixture `.rd-prof__code-btn` two
   rows above it already mounts, and the same two-layer clip geometry
   `RdSwitchDoor` takes at both rail feet. That is what deletes
   `border-radius: 0.5rem`: nothing in this kit is rounded, and re-deriving a
   chamfer in CSS is the thing `kit-switch-door.css`'s own header refuses
   ("no colour and no chamfer is re-derived in this file"). Ground and hairline
   ride the box's `Fill`/`Stroke` roles for the same reason.

   WHAT IS LEFT HERE IS LAYOUT, TYPE, AND THE TWO STATES — and every colour in
   it is a role:
     ground / hairline  -> RdChamferBox (Fill::Surface / Stroke::Accent)
     hover ground       -> --rd-surf2   (the house hover, `.rd-prog-qual:hover`)
     focus ring         -> --rd-acc     (`.rd-door__link:focus-visible`, 2px/2px)
     title              -> --rd-fg      (`.rd-door__name`)
     sub                -> --rd-fg3     (`.rd-door__sub`)
     arrow              -> --rd-acc
   No token was minted. `#ffffff`, `#fbfdf5`, `#111827` and `#6b7280` are gone
   rather than translated: the first two were the ground this block no longer
   paints, and the last two were legacy Tailwind greys standing in for `--rd-fg`
   and `--rd-fg3`, which is what the drawing calls them (`t.fg` / `t.fg3`).

   ⚠️ AND THAT IS WHY THIS ROW SURVIVED THE 2026-08-29 REHEARSAL DEFECT WHILE
   THE KIT FIXTURE DID NOT. `.pro-door` itself is `color: inherit`, and what it
   inherits is not a token: the document body carries the pre-kit Tailwind
   utility `text-gray-900` (`apps/web/index.html:279` → `_legacy-base.css:94`,
   a flat `color: #111827`), and nothing between it and these spans sets
   `color`. Every LEAF here declares its own ink — title `--rd-fg`, sub
   `--rd-fg3`, mark and arrow `--rd-acc` — so the inherited literal never
   reaches a glyph. `.rd-door__name` in `kit-switch-door.css` declared none, and
   #111827 painted the tablet rail's door name near-black on the dark ground.
   Checked here as part of that fix; no change needed on this row's ink. Do not
   "simplify" a leaf's `color` away on the grounds that it looks inherited.

   ⚠️ THE ARROW AND THE TITLE VOICE ARE UNTOUCHED, DELIBERATELY. `44 The Doors`
   retired the one-way arrow at both rail feet for the two-opposed-arrow swap
   mark, but it rules only that the Profile row STAYS as the discoverable
   location — not whether it stays in its old clothes. Whether this row takes the
   swap mark and the "Switch to" label voice is a live designer question, so this
   pass moves colour and geometry and words nothing.

   ⚠️ THE SUB NO LONGER CUTS WITH AN ELLIPSIS — C-44.1 CLOSED THE OPEN QUESTION
   THIS BLOCK USED TO RECORD. It read: "AND THE SUB STILL CUTS WITH AN ELLIPSIS,
   ALSO DELIBERATELY. The fixture's name line wraps instead (`.rd-door__name`,
   `overflow-wrap: anywhere`) under 'a door that names the destination must not
   cut the name off'. Whether that rule reaches this row's identity line is the
   same open question, one joint over. Not decided here."

   It is decided now, and it is decided AGAINST this row. C-44.1 (ruled 27 Aug)
   is written into export 30's drawing in as many words (`rd-doors.jsx:62-65`):
   "Ruled 27 Aug: that rule stands and long names WRAP — the fixture's old
   nowrap/ellipsis contradicted the rule it demonstrates, and the shipped
   Profile row's ellipsis is drift to fix." This row IS that Profile row, and
   the org's name is what its identity line carries (`{org} · {role}`,
   profile.rs), so the cut lands on the destination's name — the exact thing the
   rule forbids. `ScreenProDoor` (`rd-pro-shell.jsx:46`) draws the line with no
   truncation control of any kind, so removing the three declarations is
   transcription as well as ruling.

   `overflow-wrap: anywhere` for the reason `.rd-door__sub`'s sibling name line
   gives: a single unbreakable token (a long one-word org name) would otherwise
   overflow the phone gutter rather than break inside it.

   THE TITLE VOICE AND THE ARROW ARE STILL UNTOUCHED — that question (above) is
   about the swap mark and the "Switch to" label and stays open. C-44.1 rules
   truncation, not clothes.

   ---- RETIRED REASONING, kept for the reader who wonders why it took so long --

   AS WRITTEN (superseded): the reason is the page rather than the row.
   profile.rs is pre-kit: fixed bg-white cards, zero kit tokens, no theme
   awareness at all. Themed values here would put a near-black card among white
   ones in dark mode.

   CORRECTION, 2026-08-15: the trigger named above had already fired and nobody
   came back for this block. profile.rs is no longer pre-kit — it renders
   .rd-prof__* (profile-community.css), which paints on var(--rd-fg) /
   var(--rd-acc) and follows data-theme. So the premise "a white page" was false,
   and the consequence was visible rather than academic: in dark theme this row
   was a #ffffff slab on a --rd-bg ground, the one card on the page that did not
   follow the theme.

   WHY IT STILL STAYED (2026-08-15, superseded): only ONE literal had an exact
   token — #5c5030 IS the light --rd-acc — and swapping it alone would make the
   border and arrow the DARK accent on a white card. True as far as it went, and
   the error is in the premise it shares with the line below: it treats the white
   ground as fixed and asks which accent survives it. Once the ground is
   var(--rd-surf) the question does not arise, and the ground was always the
   thing to move first.

   THE TAN REBRAND RE-PINNED THE VALUE AND DECLINED THE DE-LITERALISATION,
   2026-08-25 (superseded): export 28 moved the light accent from #3e4e10 to
   #5c5030 and the literals moved with it, on the reasoning that this is a
   FIXED-LIGHT card so it needs the LIGHT accent whatever theme is active. That
   is the same premise again — and it is the one this pass drops. The card is not
   fixed-light any more; it is `--rd-surf`, so `--rd-acc` is correct in both
   themes and the hand-maintained copy of a token is gone.
   --------------------------------------------------------------------------- */

/* The link is the whole object; the box inside it carries ground, hairline and
   chamfer. `display: block` so the box fills the column rather than shrinking to
   its text — `.rd-door__link`'s reasoning, and the ring sits on the link for the
   reason stated there too: an outline on the chamfered box would be clipped by
   its own clip-path. */
/* ⚠️ THE GAP ABOVE IS THE DOOR'S OWN, 2026-08-28 (James, phone web). Reported:
   on Profile the door was TOUCHING the header block — avatar, name, handle —
   with no air at all. Neither side declared any: `.rd-prof__header`
   (profile-community.css) carries a horizontal `gap` and no bottom margin, and
   this rule carried `margin-bottom` only. The page's other blocks are spaced by
   `.rd-prof__section { margin-top: 16px }`, so the door was the ONE block on the
   page with nothing above it.

   RULED: the fixture carries the kit's block gap from the header, at both
   widths — `--rd-banner-gap` (p1.css, 16px phone / 20px from 1024 up), which is
   the token `.rd-banner` made the primitive's own after twenty-seven call sites
   had spelled the same gap eleven ways. A number here would be the
   twenty-eighth spelling; a rule in profile-community.css would hand the gap
   back to the page, which is the defect that ruling names.

   THE DRAWING GIVES 18px AND IT IS NOT THE VALUE. `rd-pro-shell.jsx:40`
   (`ScreenProDoor`) spaces the door with `marginBottom: 18` on the fragment's
   "Profile" TITLE — a title, not this page's avatar header, since the drawing is
   "a fragment of the shooter Profile, not the whole screen" (:31). So 18 is the
   gap under a drawn block this page does not mount, and it lands between the
   token's own two values rather than contradicting either. The ruling names the
   token; the drawn number is recorded, not shipped.

   `margin-bottom` stays 14px — drawn at `rd-pro-shell.jsx:42`
   ("marginBottom: 14"), and untouched by a ruling about the gap ABOVE.

   ⚠️ NOT `.rd-door`, AND THAT IS NOT AN OVERSIGHT. The kit fixture
   (`kit-switch-door.css`) carries `margin-top: auto` — the drawn flex spacer
   that pushes it to a rail's foot — and every one of its sites is a foot. A
   fixed top margin there would overwrite the drawn spacer and lift the fixture
   off the bottom of both rails. This row is the door's other drawing
   (`ScreenProDoor`, kept as a separate object by `44 The Doors.html`: "the
   Profile 'Switch To Pro' row stays as the discoverable, explainable
   location"), it sits mid-column rather than at a foot, and the gap is its
   own. */
.pro-door {
    display: block;
    margin-top: var(--rd-banner-gap);
    margin-bottom: 14px;
    text-decoration: none;
    color: inherit;
}

.pro-door:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* The row proper, mounted as the box's inner layer. */
.pro-door__inner {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px;
}

/* The house hover ground for a whole-row link — `--rd-surf2`, as
   `.rd-prog-qual:hover` (`adm-prog.css`) carries it. It has to out-specify the
   fill role the box's inner layer paints, which two classes and a pseudo do.

   ⚠️ THE HOVER ITSELF IS CARRIED OVER, NOT RE-ARGUED. `adm-prom.css` §10 rules
   the Pro phone row's hover a DEFECT rather than a variant — "a form factor
   where a thumb never leaves the surface and `:hover` sticks" — and this row is
   on the shooter's phone Profile, so the same argument reaches it. Whether it
   applies here is a question about behaviour, not colour; this pass is colour
   and geometry, so the state ships as it already did, in a token. Flagged. */
.pro-door:hover .pro-door__inner { background: var(--rd-surf2); }

/* The org mark's ink now comes from `.rd-pro-mark__text--on` (adm0-chrome.css)
   via the kit `OrgMark`, not this file (#1402). */

.pro-door__text { flex: 1; min-width: 0; display: block; }

.pro-door__title {
    display: block;
    font-family: 'Archivo', system-ui, sans-serif;
    text-transform: uppercase;
    font-weight: 800;
    font-stretch: 78%;
    letter-spacing: .04em;
    font-size: 13px;
    color: var(--rd-fg);
}

.pro-door__sub {
    display: block;
    font-family: 'Archivo', system-ui, sans-serif;
    font-size: 11px;
    color: var(--rd-fg3);
    margin-top: 3px;
    overflow-wrap: anywhere;
}

.pro-door__arrow { flex: none; color: var(--rd-acc); font-size: 13px; }

/* ---------------------------------------------------------------------------
   The console's vertical scrollbar — MOVED TO THE KIT, 2026-08-30.

   This block carried the 2026-08-13 console spec on `html[data-app="pro"]`
   (thin, transparent track, square `ctrl` thumb, `fg3` on hover, 11px), scoped
   on the root's `data-app` stamp per the 2026-08-14 ruling. James ruled on
   2026-08-30 that there is ONE scrollbar treatment for every scroller in both
   apps, so the rule now lives in kit-shell.css §8 — the standard pair on `*`
   for engines without `::-webkit-scrollbar`, the bare pseudo-elements for the
   rest — with theme tokens for its colours. `data-app` is still stamped
   (`pages::set_app_identity`); no sheet rule reads it today.
   --------------------------------------------------------------------------- */

/* ==== _legacy-events-list · ADM1 events list, §2-§3 · owner: NOBODY ==== */
/* UNOWNED · DELETE-ONLY. No lane owns this file, so no lane may add to it: new
   CSS goes in that lane's own section file, listed in order.txt. Any lane MAY
   delete what its own change stranded. The pool only shrinks — that is the whole
   mechanism by which these files disappear, and an addition here rebuilds the
   shared address the split removed.

   ⚠️ THE FILENAME NAMES THE CONTENT, NOT THE POSITION — and here the two
   disagree. These are ADM1 rules, and in the single stylesheet they sat under
   the ADM0 banner (lines 301-459 at `9e5b26b`, beneath
   `==== ADM0 · docs/design/kit/ADM0-chrome.md · owner: spine ====`). The split
   kept them exactly where they were: it re-files by POSITION, and correcting a
   mislabelling inside a change-nothing PR hides a real change inside a
   mechanical one.

   Moving them under adm1-pr2.css is #662, and it is NOT a pure move: it changes
   CSS source order, which decides specificity ties. It needs verification of its
   own — do not treat it as the same class of safe move the split was.
   ===========================================================================
   ADM1 — the events list. Spec: docs/design/kit/ADM1-events.md §2-§3
   Drawn: tmp/design/snapshot/rd-pro-events.jsx (ScreenEvents, StatusChip).
   --------------------------------------------------------------------------- */

/* ProTop's action. VOLT as a fill, not ACC — rd-pro-kit.jsx:229-230: in light
   mode acc is the deep warm brown reserved for accent TEXT, and using it as a fill
   puts black on 2.3:1. The status chip below uses acc as text+border and is also
   correct; the two are different tans on purpose. */
/* `inline-flex` + `gap`, and the gap is a LOADING requirement rather than a
   typographic one. `43 Loading States.html:53` retires the busy-label swap: the
   word now holds still and a 13px sweep joins it (rd-loading.jsx:197, `gap: 10`).
   While the label swapped, this button never had a second child to space; every
   ProTop action in the Pro console now does, and under `inline-block` the mark
   sits jammed against the label on the text baseline. Slate 1 made exactly this
   edit to `.rd-evl__more` for exactly this reason (`s15.css`, "the row needs a
   gap it never needed while the word was swapped"). One rule, every ProTop
   action: admin/location_editor, admin/event_form, admin/drill_form. */
.rd-pro-top__btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: var(--rd-volt);
  color: var(--rd-on-volt);
  padding: 10px 18px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 11.5px;
  text-decoration: none;
  clip-path: polygon(0 0, calc(100% - 7px) 0, 100% 7px, 100% 100%, 7px 100%, 0 calc(100% - 7px));
}

.rd-pro-top__btn:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* ---------------------------------------------------------------------------
   The status chip. OUTLINED: one colour drives text AND a 1px border, no fill.
   Three colours, and they must stay three — a chip that painted Draft, Active
   and Completed alike would look composed and say nothing.
   --------------------------------------------------------------------------- */


/* ---------------------------------------------------------------------------
   The list.
   --------------------------------------------------------------------------- */

.rd-adm-ev { flex: 1; min-height: 0; padding: 4px 32px 32px; }

/* `.rd-adm-ev__row` and its :hover / :focus-visible went with the batch-15
   migration onto `ProTable` / `ProRow` — `.rd-prow`,
   `.rd-ptable--dens-16-gap-20 .rd-prow` (16px 0 / gap 20) and `.rd-prow--link:*`
   in kit-pro-table.css now carry every declaration they held. 🟡 One value
   moved: the hover ground surf -> surf2, spec 15 §11 Q3's interim answer, not
   this screen's choice. The CELLS below are UNTOUCHED — they mount in
   `CellRole::Slot` cells, which carry no type. See `events_columns()`. */

.rd-adm-ev__when {
  width: 108px;
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 10.5px;
  color: var(--rd-fg3);
}

.rd-adm-ev__name {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 14.5px;
  color: var(--rd-fg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ⚠️ ONLY the name steps down for a past event — NOT an opacity on the row.
   rd-pro-events.jsx:208-212: an opacity multiplier dims fg2 and fg3 with it, and
   those two greys are calibrated to clear ~5:1 on small copy in sunlight; a
   blanket .62 threw that away and put a 9px chip at 2.9:1. Do not "tidy" this
   into `.rd-adm-ev__row--past { opacity: … }`. */
.rd-adm-ev__name--past { color: var(--rd-fg2); }

.rd-adm-ev__enrolled {
  width: 96px;
  flex: none;
  text-align: right;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg2);
}

/* States the drawing does not draw — kept because dropping them is a
   regression, and marked so a later slice with a drawn one replaces rather than
   reconciles. */

/* `.rd-adm-ev__loading` was the loading SENTENCE's rule and went with it —
   `43 Loading States.html:40` retires loading text as a treatment, so the body
   now mounts the kit's skeleton stack and this screen owns no loading type. */

.rd-adm-ev__error {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-danger);
  border: 1px solid var(--rd-danger);
  background: var(--rd-danger-wash);
  padding: 12px 14px;
  margin: 14px 0;
}

.rd-adm-ev__more { padding: 18px 0; text-align: center; }

.rd-adm-ev__more-btn {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg2);
  background: none;
  border: 1px solid var(--rd-line);
  padding: 9px 18px;
  cursor: pointer;
}

.rd-adm-ev__more-btn:hover:not(:disabled) { color: var(--rd-fg); border-color: var(--rd-ctrl); }
.rd-adm-ev__more-btn:disabled { color: var(--rd-fg3); cursor: default; }
.rd-adm-ev__more-btn:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* The empty state, drawn at rd-pro-events.jsx:199-204. */
.rd-adm-ev__empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 32px;
}

.rd-adm-ev__empty-head {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 75%;
  letter-spacing: .02em;
  font-size: 19px;
  color: var(--rd-fg);
  margin-bottom: 12px;
}

.rd-adm-ev__empty-body {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  line-height: 1.6;
  color: var(--rd-fg3);
  max-width: 430px;
}

/* ==== ADM1-PR2 · docs/design/kit/ADM1-events.md §5 · owner: spine ==== */

/* ---------------------------------------------------------------------------
   ADM1 PR 2 — event detail: tabs, lifecycle, fields, delete confirm.
   Drawn: rd-pro-events.jsx:66-79, :75-96, :98-105, :187-218.

   ⚠️ 2026-08-10 export: `EvTabs` (:57) left this drawing for the kit's `ProTabs`
   (rd-pro-kit.jsx:278) and `ScreenEventOverview` moved to :223 with a role
   parameter. These numbers resolve but describe replaced content; the CSS below
   is superseded when the kit PR migrates the components. Dated pointer, not a
   renumber — docs/design/corpus-resync.md step 9.
   --------------------------------------------------------------------------- */

/* The tab strip. ⚠️ This underline is #467's first cluster: `border-b-2` and
   `border-blue-600` are undefined in the pre-kit page, so the active tab is
   currently carried by text colour alone. Drawn properly here. */

/* The lifecycle strip. Three states, one forward action — and a line saying it
   is NOT a visibility control, which is what a reader assumes it is. */
.rd-ev-life { border: 1px solid var(--rd-line); background: var(--rd-surf); padding: 18px 20px; }
.rd-ev-life__track { display: flex; align-items: center; gap: 0; }
.rd-ev-life__step { display: flex; align-items: center; gap: 9px; flex: none; }

/* The connector, and it STRETCHES — `flex: 1` in the drawing (:91). Without it
   the three steps huddle at the left and the strip stops reading as a track. */
.rd-ev-life__link { flex: 1; height: 1px; margin: 0 14px; background: var(--rd-line); }
.rd-ev-life__link--on { background: var(--rd-acc); }

.rd-ev-life__dot {
  width: 9px; height: 9px; flex: none;
  background: transparent;
  border: 1.5px solid var(--rd-ctrl);
}
.rd-ev-life__dot--on { background: var(--rd-acc); border-color: var(--rd-acc); }

.rd-ev-life__label {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 600; font-stretch: 78%; letter-spacing: .08em;
  font-size: 10px; color: var(--rd-fg3);
}
.rd-ev-life__label--on { font-weight: 800; color: var(--rd-fg); }

/* ⚠️ ~30px OF TARGET ON THE EVENT'S ONE ADVANCING ACT (8px of pad over a 10.5px
   display line) — James, NTOA rehearsal 2026-08-29. Fixed by the transparent
   `::after` below rather than by re-sizing the outline, so `Set Active` and
   `Mark Completed` are drawn exactly as they were. `position: relative` is the
   containing block that pseudo needs. */
.rd-ev-life__next {
  margin-left: 24px; flex: none;
  background: none;
  position: relative;
  border: 1.5px solid var(--rd-ctrl);
  color: var(--rd-fg);
  padding: 8px 15px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 10.5px; cursor: pointer;
}
/* 44px of target on both lifecycle acts, painting nothing and moving nothing —
   `.rd-ev-life__track` and `.rd-ev-life__reverse` are both horizontal rows, so
   the band above and below each act belongs to no other target. The reverse
   link needs this more than the advance does: it is a text link with a 1px
   underline and `padding: 0 0 2px`, i.e. ~18px of box, and the underline is the
   drawing — a real `min-height` there would drop the rule away from the word.
   Shared note: `kit-hit.css`. */
.rd-ev-life__next::after,
.rd-ev-life__back::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 44px;
  /* `margin-top`, not a `transform`: a transform would make the pseudo a
     containing block for fixed descendants, which is a hazard the pool already
     polices by name in three places. Half of 44. */
  margin-top: -22px;
}

.rd-ev-life__next:hover { border-color: var(--rd-acc); }
.rd-ev-life__next:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

.rd-ev-life__note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px; color: var(--rd-fg3);
  margin-top: 14px; line-height: 1.5;
}

/* The reverse transition — DRAWN as of the 10 Aug export (32 Pro Events, Going
   Backwards), and these rules stopped being a deviation with it. Two things
   changed and both are the drawing's:

   1. POSITION. This block used to sit between the track and the note, beside the
      forward primary's row. The caption is explicit: "The reverse sits BELOW the
      hairline, not beside the primary. Two boxes side by side read as a pair of
      equals and make going backwards look like half the normal path; it is not,
      it is the recovery. Text, no box, under the sentence that explains the
      strip." Hence the border-top here and the source order in the component —
      .rd-ev-life__note precedes this block in the markup.
   2. WEIGHT. The label is 12.5px in --rd-fg with a --rd-ctrl underline, not
      11.5px in --rd-fg3. It is quieter than the forward action because it has no
      box, not because it is greyed; the grey belongs to the sentence beside it.

   `align-items: baseline` is what makes a 12.5px label and an 11.5px sentence
   sit on one line rather than centring two different type sizes against each
   other. */
.rd-ev-life__reverse {
  display: flex; align-items: baseline; gap: 14px;
  margin-top: 15px; padding-top: 14px;
  border-top: 1px solid var(--rd-line);
}

.rd-ev-life__back {
  flex: none;
  background: none;
  /* The containing block for the 44px hit band declared above — 2026-08-29. */
  position: relative;
  border: 0;
  padding: 0 0 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
  border-bottom: 1px solid var(--rd-ctrl);
  cursor: pointer;
}
.rd-ev-life__back:hover { border-bottom-color: var(--rd-acc); }
.rd-ev-life__back:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* The missing case: same slot, same size, no underline and no cursor — it is a
   statement, not a control that has been switched off. The drawing's whole
   argument for this frame is that a greyed-out button explains nothing, so this
   must not look like one. */
.rd-ev-life__denied {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg3);
}

/* The line that says what the reverse costs, or who to ask. Wraps; the label
   beside it does not. */
.rd-ev-life__why {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px; color: var(--rd-fg3); line-height: 1.5;
}

/* Field pairs. */
.rd-ev-fields { display: flex; flex-wrap: wrap; gap: 28px; margin-top: 26px; }
.rd-ev-field { flex: 1 1 200px; min-width: 0; }
.rd-ev-field--wide { flex: 1 1 100%; }

/* A field pair while the event is still on the wire. It takes `.rd-ev-field`'s
   own column and adds only the separation the warm pair gets from
   `.rd-ev-field__label`'s `margin-bottom` — which the skeleton marks do not
   carry, being kit blocks rather than this screen's type. Nothing else: the
   skeleton ink is the only thing that should be visible here. */
.rd-ev-field--skel { display: grid; gap: 7px; }

.rd-ev-field__label {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .11em;
  font-size: 9px; color: var(--rd-fg3); margin-bottom: 7px;
}
.rd-ev-field__value { font-family: 'Archivo', system-ui, sans-serif; font-size: 14px; color: var(--rd-fg); }

/* Delete. A text link, not a button, and 40px below a rule — "completing an
   event and destroying one are different acts". */
/* A flex row inside the rule, because the slot carries a label AND the sentence
   beside it in all three of its states — 13 Aug, Delete Is Draft Only. Same
   geometry as `.rd-ev-life__reverse` above, which is the treatment this one was
   asked to match. */
.rd-ev-danger {
  margin-top: 40px; padding-top: 20px;
  border-top: 1px solid var(--rd-line);
  display: flex; align-items: baseline; gap: 14px;
}

.rd-ev-danger__link {
  flex: none;
  background: none; border: 0; padding: 0 0 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-danger);
  border-bottom: 1px solid var(--rd-danger);
  cursor: pointer;
}
.rd-ev-danger__link:focus-visible { outline: 2px solid var(--rd-danger); outline-offset: 2px; }

/* The refused case's head. Same slot and same size as the link, with no
   underline, no danger colour and no cursor — it is a statement, not a control
   that has been switched off. Identical argument to `.rd-ev-life__denied`. */
.rd-ev-danger__denied {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg3);
}

/* The line beside either one: why this can be deleted, or why it cannot.
   `max-width` is the drawing's on the refused sentences (the long ones); the
   offered sentence is short enough that it never reaches the limit. */
.rd-ev-danger__why {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px; color: var(--rd-fg3); line-height: 1.5;
  max-width: 54ch;
}

/* ⚠️ **CORRECTED 2026-08-25.** This read: "`position: relative` is
   load-bearing, not tidiness: it is what makes this the containing block for
   the confirm's absolute scrim. Still required after the migration to
   `RdConfirmModal` — that component's own docs state the contract it cannot
   enforce from inside: 'Absolute positioning resolves against the nearest
   positioned ancestor; if a caller supplies none, the scrim escapes to the
   viewport and covers the rail too.' This IS that ancestor."

   `.rd-confirm` is `position: fixed` now (adm1-5-1.css) and owes this element
   nothing — export 27 also moved `position: relative` off every Pro content
   pane and onto `ProShell` (`rd-pro-kit.jsx:326`), so even the general shape
   of this claim (a pane is the confirm's containing block) is gone, not just
   the specific mechanism. The declaration stays as the reversal path. */
.rd-ev-overview { flex: 1; min-height: 0; overflow: auto; padding: 24px 32px; position: relative; }

/* The action-error strip's dismiss control. ⚠️ UNDRAWN — the corpus draws no
   error state on this screen at all, so the strip is an engineering invention
   following `events.rs`'s precedent that a failed action must not blank content
   already on screen.

   The block it belongs to (`.rd-ev-msg`) is defined in the ADM1-PR3 section,
   where the detail page's loading/error/not-found messages landed. This modifier
   sits with the screen that renders it instead of being appended there, and the
   split is safe because the two selectors never collide: nothing here overrides
   a PR3 declaration, so the concatenation order between the sections decides
   nothing. */
.rd-ev-msg__dismiss {
  margin-left: 14px;
  background: none; border: 0; padding: 0 0 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
  border-bottom: 1px solid var(--rd-line);
  cursor: pointer;
}
.rd-ev-msg__dismiss:hover { color: var(--rd-fg); border-bottom-color: var(--rd-ctrl); }
.rd-ev-msg__dismiss:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* ==== ADM1-PR3 · docs/design/kit/ADM1-events.md §6 · owner: spine ==== */

/* ---------------------------------------------------------------------------
   ADM1 PR 3 — the Roster tab and its add-members picker.
   Drawn: rd-pro-events.jsx:494-533.
   --------------------------------------------------------------------------- */

.rd-ev-roster { flex: 1; min-height: 0; overflow: auto; padding: 18px 32px 32px; position: relative; }

.rd-ev-roster__row {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 14px 0;
  border-bottom: 1px solid var(--rd-line);
}

.rd-ev-roster__name {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 14px;
  color: var(--rd-fg);
}

/* 130px and 150px are the drawn widths — fixed, so the three columns line up
   down the list instead of ragging with name length. */
.rd-ev-roster__role {
  width: 130px;
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .1em;
  font-size: 9px;
  color: var(--rd-fg3);
}

.rd-ev-roster__when {
  width: 150px;
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
}

.rd-ev-roster__remove {
  flex: none;
  background: none;
  border: 0;
  padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
  cursor: pointer;
}
.rd-ev-roster__remove:hover { color: var(--rd-danger); }
.rd-ev-roster__remove:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* In flight: the label STAYS ("Remove") and a 13px `RdSweep` joins it instead
   of the retired "Removing…" swap — `43 Loading States.html:53`, the #1042
   treatment. Modifier-only, not the base rule: a permanent flex row would
   change every render of this button for a mark that is usually absent, the
   same restraint `.rd-btn__face--inflight` takes in `kit-loading.css`. 10px
   gap is that primitive's own drawn number (`rd-loading.jsx:197`). */
.rd-ev-roster__remove--inflight {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

/* The per-event scorer assignment (`47 (Mobile) RO Range Day`, row 01;
   migration 0059). Sits beside Remove and wears the same quiet shape — it is a
   toggle, not a destination.

   ⚠️ It marks a JOB, not a permission: James ruled 21 Aug 2026 that any org RO,
   instructor or admin may score any event. The lit state is therefore
   informational, and the accent (never the danger colour) is the whole
   difference between on and off — nothing here can lock anybody out. State is
   carried on `aria-pressed` rather than a modifier class so the button says what
   it is to a screen reader and to the stylesheet in one place. */
.rd-ev-roster__scorer {
  flex: none;
  background: none;
  border: 0;
  padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
  cursor: pointer;
}
.rd-ev-roster__scorer:hover { color: var(--rd-fg); }
.rd-ev-roster__scorer[aria-pressed="true"] { color: var(--rd-acc); font-weight: 700; }
.rd-ev-roster__scorer:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* Same in-flight treatment as `.rd-ev-roster__remove--inflight` above, for the
   retired "Saving…" swap. The label stays "Scorer" / "Make scorer". */
.rd-ev-roster__scorer--inflight {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

.rd-ev-roster__foot {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
  margin-top: 18px;
  line-height: 1.55;
  max-width: 66ch;
}

.rd-ev-roster__empty,
.rd-ev-roster__note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg3);
  padding: 8px 0 4px;
}

.rd-ev-roster__error {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-danger);
  border: 1px solid var(--rd-danger);
  padding: 10px 12px;
  margin-bottom: 14px;
}

/* The picker. Same scrim as the delete confirm. 520px here against the
   confirm's 460: this one holds a scrollable list, that one holds a sentence.

   ⚠️ **`fixed`, NOT `absolute` — 2026-08-25, and it moved because the confirm
   moved.** This block used to read "absolute inside the tab body, so the rail
   and the page head stay lit". That was the drawing's arrangement and it stopped
   surviving the fixed-rail scroll model: an `inset: 0` scrim resolves against
   the nearest positioned ancestor, which here is the tab body — a scroller that
   starts a third of the way down the glass — so the panel centred on THAT box
   rather than on the viewport, and scrolled away with the list. The whole
   argument, the measurements and the deviation it costs are recorded once, at
   `.rd-confirm` in adm1-5-1.css; this is the same defect on a hand-built scrim
   and it takes the same answer rather than a second opinion about it.

   The phone arm (`.rd-evrm .rd-ev-pick`, adm-evd-mob.css) already flipped this
   to `fixed` for its own reasons and now restates rather than overrides. Left in
   place: it is another owner's part, and a redundant declaration is cheaper than
   a cross-part deletion. */
.rd-ev-pick {
  position: fixed; inset: 0; z-index: 40;
  background: rgba(6, 6, 6, .82);
  display: flex; align-items: center; justify-content: center;
}

.rd-ev-pick__panel {
  width: 520px;
  max-width: calc(100vw - 32px);
  max-height: calc(100% - 32px);
  display: flex;
  flex-direction: column;
  background: var(--rd-surf);
  border: 1px solid var(--rd-line);
}

.rd-ev-pick__head { flex: none; padding: 20px 22px 14px; border-bottom: 1px solid var(--rd-line); }

.rd-ev-pick__title {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 75%; letter-spacing: .02em;
  font-size: 17px;
  color: var(--rd-fg);
}

.rd-ev-pick__sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
  margin-top: 8px;
}

/* The list scrolls; the head and foot do not. An org of three hundred is the
   case this panel exists to handle. */
.rd-ev-pick__list { flex: 1 1 auto; min-height: 0; overflow-y: auto; }

.rd-ev-pick__row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 12px 22px;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  background: none;
  text-align: left;
}
.rd-ev-pick__row--pick { cursor: pointer; }
.rd-ev-pick__row--pick:hover { background: var(--rd-chip); }
.rd-ev-pick__row--pick:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: -2px; }

/* 16x16, --rd-acc border when selectable, --rd-ctrl border + --rd-chip fill when
   already enrolled. */
.rd-ev-pick__box {
  width: 16px; height: 16px; flex: none;
  border: 1.5px solid var(--rd-acc);
  background: transparent;
}
.rd-ev-pick__box--on { background: var(--rd-acc); }
.rd-ev-pick__box--had { border-color: var(--rd-ctrl); background: var(--rd-chip); }

.rd-ev-pick__name {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
}
.rd-ev-pick__name--had { color: var(--rd-fg3); }

.rd-ev-pick__tag {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .1em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

.rd-ev-pick__more { padding: 14px 22px 18px; text-align: center; }

.rd-ev-pick__more-btn {
  background: none;
  border: 1.5px solid var(--rd-ctrl);
  color: var(--rd-fg);
  padding: 9px 16px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 10.5px;
  cursor: pointer;
}
.rd-ev-pick__more-btn:hover { border-color: var(--rd-acc); }
.rd-ev-pick__more-btn:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

.rd-ev-pick__foot { flex: none; padding: 16px 22px 20px; }

.rd-ev-pick__hint {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
  line-height: 1.5;
  margin-bottom: 16px;
}

.rd-ev-pick__acts { display: flex; align-items: center; gap: 14px; }

/* volt, not acc — a FILL, per the kit's own rule. */
.rd-ev-pick__add {
  flex: none;
  background: var(--rd-volt);
  color: var(--rd-on-volt);
  border: 0;
  padding: 10px 18px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 11px;
  cursor: pointer;
}
.rd-ev-pick__add:focus-visible { outline: 2px solid var(--rd-fg); outline-offset: 2px; }

.rd-ev-pick__cancel {
  flex: none;
  background: none;
  border: 0;
  padding: 0 0 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
  border-bottom: 1px solid var(--rd-line);
  cursor: pointer;
}
.rd-ev-pick__cancel:hover { color: var(--rd-fg); }
.rd-ev-pick__cancel:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* Loading / error / not-found, inside the kit page so opening an event does not
   flash the legacy island's light ground before the dark screen arrives. */
.rd-ev-msg {
  padding: 24px 32px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg3);
}
.rd-ev-msg--error { color: var(--rd-danger); }

/* `.rd-pro-tabbody-legacy` lived here for exactly two PRs. It gave the pre-kit
   Roster and Scores tabs the light ground their greys were drawn against, inside
   a page that had already migrated — the ADM0 island failure arriving from the
   opposite direction. PR 3 migrated Roster, PR 4 migrated Scores, and the
   wrapper went with the last tab that needed it, as its own comment promised.
   The removal trigger was written down when the block was written; this is it
   being honoured rather than the block quietly becoming permanent. */

/* ==== _legacy-pre-kit · the pre-kit app screens + the second utility wave · owner: NOBODY ==== */
/* UNOWNED · DELETE-ONLY. No lane owns this file, so no lane may add to it: new
   CSS goes in that lane's own section file, listed in order.txt. Any lane MAY
   delete what its own change stranded. The pool only shrinks — that is the whole
   mechanism by which these files disappear, and an addition here rebuilds the
   shared address the split removed.

   ⚠️ READ THE NAME AS "PRE-KIT", NOT AS "UTILITIES". The obvious guess from the
   filename — that this is the app's utility layer — is wrong by measurement, and
   it is wrong in the direction that matters: a lane looking for "where the
   utilities live" would add here. Measured over the 214 selector blocks in this
   file (217 opening braces, 3 of them at-rules), it is two populations:

     the app's own pre-kit screen CSS — .toggle, .drill-*, .score-*, .zone-*,
       .stage-*, .event-*, .queue-*, .se-*, .pass-*, .void-*, .tab-*
     a SECOND wave of utilities — .bg-*, .text-*, .w-*, .h-*, .gap-*, .py-*,
       .mt-*, .grid-*, .max-*, .border-*, in the same idiom as _legacy-base.css

   The two waves are not duplicates and must not be merged — see the note in
   _legacy-base.css for why merging them changes which rule wins.

   Displaced but NOT misfiled: it sat beneath `==== ADM1-PR3 ==== ` in the single
   stylesheet, alongside the two blocks that ARE #662, but it names no screen its
   banner contradicts, so there is no correct banner to move it under. */

/* Void confirmation overlay */
.void-confirm-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.5);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 200;
    padding: 1rem;
}
.void-confirm-dialog {
    background: #fff;
    border-radius: 0.75rem;
    padding: 1.5rem;
    width: 100%;
    max-width: 24rem;
    box-shadow: 0 20px 25px rgba(0, 0, 0, 0.15);
}

/* Grid layout */
.grid { display: grid; }
.grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.aspect-square { aspect-ratio: 1 / 1; }
.object-cover { object-fit: cover; }
.overflow-hidden { overflow: hidden; }

/* Group hover utilities */
.group:hover .group-hover\:opacity-100 { opacity: 1; }
.transition-opacity { transition: opacity 0.2s; }

/* Opacity utilities */
.opacity-0 { opacity: 0; }
.bg-opacity-50 { --tw-bg-opacity: 0.5; }
.bg-opacity-60 { --tw-bg-opacity: 0.6; }
.bg-opacity-80 { --tw-bg-opacity: 0.8; }
.bg-black { background-color: rgba(0, 0, 0, var(--tw-bg-opacity, 1)); }

/* Blue tones for leaderboard highlight */
.bg-blue-50 { background-color: #eff6ff; }

/* Hover utilities */
.hover\:bg-red-50:hover { background-color: #fef2f2; }
.hover\:bg-gray-200:hover { background-color: #e5e7eb; }
.bg-gray-800 { background-color: #1f2937; }
.bg-red-600 { background-color: #dc2626; }
.border-b { border-bottom-width: 1px; border-bottom-style: solid; }

/* Additional spacing */
.mt-6 { margin-top: 1.5rem; }
.mb-3 { margin-bottom: 0.75rem; }
.gap-3 { gap: 0.75rem; }
.px-2 { padding-left: 0.5rem; padding-right: 0.5rem; }
.py-0\.5 { padding-top: 0.125rem; padding-bottom: 0.125rem; }
.py-1 { padding-top: 0.25rem; padding-bottom: 0.25rem; }
.w-6 { width: 1.5rem; }
.h-6 { height: 1.5rem; }
.w-8 { width: 2rem; }
.mx-4 { margin-left: 1rem; margin-right: 1rem; }
.rounded-full { border-radius: 9999px; }
.inline-flex { display: inline-flex; }

/* Absolute positioning */
.absolute { position: absolute; }
.fixed { position: fixed; }
.inset-0 { top: 0; right: 0; bottom: 0; left: 0; }
.top-1 { top: 0.25rem; }
.right-1 { right: 0.25rem; }
/* Full-screen overlays sit on the modal layer (200, same as the dialog rule
   above). At 50 they tied with .tab-bar and LOST on DOM order — ShellLayout
   renders the tab bar after the Outlet — so the bar drew over the lightbox. */
.z-modal { z-index: 200; }

/* Drill authoring (shooter-drill-authoring chunk 1) */
.bg-yellow-500 { background-color: #eab308; }
.p-1 { padding: 0.25rem; }
.text-xl { font-size: 1.25rem; line-height: 1.75rem; }
.w-20 { width: 5rem; }
.hover\:bg-red-700:hover { background-color: #b91c1c; }
/* Avatar component (avatar.rs) — display + initials-fallback sizing. These
   utilities were referenced by PR-4's avatar.rs but never added here, so the
   fallback background and non-default sizes silently no-op. Additive. */
.h-8 { height: 2rem; }
.w-10 { width: 2.5rem; }
.h-10 { height: 2.5rem; }
.w-16 { width: 4rem; }
.h-16 { height: 4rem; }
.bg-gray-300 { background-color: #d1d5db; }
.shrink-0 { flex-shrink: 0; }

/* Backfill (check-css launch): classes the rsx already used that were
   silently no-oping — standard Tailwind v3 definitions. Found by
   `rangeday check css`, which now gates CI. */
.-right-1 { right: -0.25rem; }
.-top-1 { top: -0.25rem; }
.bg-gray-200 { background-color: #e5e7eb; }
.bg-green-600 { background-color: #16a34a; }
.bg-red-100 { background-color: #fee2e2; }
.bg-yellow-100 { background-color: #fef9c3; }
.border-blue-200 { border-color: #bfdbfe; }
.border-gray-100 { border-color: #f3f4f6; }
.cursor-pointer { cursor: pointer; }
.flex-shrink-0 { flex-shrink: 0; }
.h-2 { height: 0.5rem; }
.h-3 { height: 0.75rem; }
.inline-block { display: inline-block; }
.items-baseline { align-items: baseline; }
.justify-end { justify-content: flex-end; }
.max-w-sm { max-width: 24rem; }
.max-w-xs { max-width: 20rem; }
.mb-8 { margin-bottom: 2rem; }
.min-w-0 { min-width: 0; }
.ml-auto { margin-left: auto; }
.mt-3 { margin-top: 0.75rem; }
.mt-8 { margin-top: 2rem; }
.p-6 { padding: 1.5rem; }
.pt-3 { padding-top: 0.75rem; }
.py-1\.5 { padding-top: 0.375rem; padding-bottom: 0.375rem; }
.py-6 { padding-top: 1.5rem; padding-bottom: 1.5rem; }
.text-3xl { font-size: 1.875rem; line-height: 2.25rem; }
.text-amber-600 { color: #d97706; }
.text-amber-700 { color: #b45309; }
.text-gray-800 { color: #1f2937; }
.text-green-600 { color: #16a34a; }
.tracking-wide { letter-spacing: 0.025em; }
.uppercase { text-transform: uppercase; }
.w-2 { width: 0.5rem; }
.w-3 { width: 0.75rem; }
.whitespace-pre-wrap { white-space: pre-wrap; }
.hover\:bg-blue-50:hover { background-color: #eff6ff; }
.hover\:bg-gray-50:hover { background-color: #f9fafb; }
.hover\:bg-green-50:hover { background-color: #f0fdf4; }
.hover\:bg-green-700:hover { background-color: #15803d; }
.hover\:text-blue-800:hover { color: #1e40af; }
.focus\:outline-none:focus { outline: none; }
.focus\:ring-2:focus { outline: none; box-shadow: 0 0 0 2px var(--rdp-ring, rgba(59, 130, 246, 0.5)); border-color: var(--rdp-ring, #3b82f6); }
/* ⚠️ Deliberate, third of three. This class EXISTS to serve pre-kit markup —
   its 32 call sites are all unmigrated pages — so the audience argument above
   applies to it unchanged. Note the shape: this rule SETS the ring variable,
   so retokenising only the `var(..., fallback)` in the rule above would move
   nothing on any site that uses this class. A fix that changes no pixel while
   reading as done is worse than no fix. Tracker: #617. */
.focus\:ring-blue-500:focus { --rdp-ring: #3b82f6; }
@media (min-width: 640px) { .sm\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.space-y-3 > * + * { margin-top: 0.75rem; }
/* Media gallery (PR-4): section stack + square thumbs. */
.bg-blue-100 { background-color: #dbeafe; }
.bg-green-100 { background-color: #dcfce7; }

/* Score-history trend chart (score-history-trends F5) — the core-emitted SVG
   scales to the container width; its viewBox keeps the aspect, same contract
   as the stage diagram's scene rule in sx-shared.css. (It named `.stage-diagram`
   here, which this file no longer defines: that rule moved to the
   shared-components part with the re-skin, and a cross-reference to a deleted
   selector is how the next reader concludes the contract is gone.

   ⚠️ The replacement names the FILE, not the selector, and that is deliberate:
   the class sweeps in `pages/stage_diagram.rs` search the assembled sheet for
   their selector, and this part concatenates ahead of sx-shared.css — so
   spelling it here made a comment satisfy a rule-body assertion. Found by the
   assertion failing, which is the direction that works.) */
.pb-3 { padding-bottom: 0.75rem; }
.trend-chart {
    width: 100%;
    /* The 360x180 viewBox scales with its container, so an uncapped chart was
       1865x933 on a desktop viewport (measured on the staging drive). Cap the
       CONTAINER's width rather than the svg's height: capping height leaves the
       box full-width with the plot letterboxed in ~676px of dead space either
       side, whereas this keeps the aspect with no wasted width, and phones —
       narrower than the cap — are unaffected. */
    max-width: 32rem;
    border-radius: 0.25rem;
    overflow: hidden;
}
.trend-chart svg {
    display: block;
    width: 100%;
    height: auto;
}

/* ── Admin surfaces (merged from the dashboard's stylesheet) ─────────────
 *
 * Appended verbatim when the admin pages moved into this crate. These apps
 * have NO Tailwind build: an unlisted class silently no-ops rather than
 * failing, so a moved page whose classes stayed behind renders wrong with
 * nothing to notice. `rangeday check css` guards literal classes in CI; it
 * cannot see dynamically-composed class strings, which is what the admin
 * staging drive is for.
 */
.items-end { align-items: flex-end; }
.col-span-2 { grid-column: span 2 / span 2; }
.py-2\.5 { padding-top: 0.625rem; padding-bottom: 0.625rem; }
.pb-2 { padding-bottom: 0.5rem; }
.ml-1 { margin-left: 0.25rem; }
.max-w-3xl { max-width: 48rem; }
.max-w-7xl { max-width: 80rem; }
.tabular-nums { font-variant-numeric: tabular-nums; }
.text-red-500 { color: #ef4444; }
.rounded-t-lg {
    border-top-left-radius: 0.5rem;
    border-top-right-radius: 0.5rem;
}
input[type="text"], input[type="email"], input[type="password"] { border: 1px solid #d1d5db; outline: none; }
/* ⚠️ Deliberate, and the second of two — see the pin at the Forms block above
   for the reasoning. Retokenising this to a kit accent puts #A89968 on the
   fixed-white cards of every unmigrated page in the default dark theme.
   It converts when those pages migrate, not before. Tracker: #617. */
input:focus { box-shadow: 0 0 0 2px #3b82f6; border-color: #3b82f6; }
.bg-amber-50 { background-color: #fffbeb; }
.border-amber-200 { border-color: #fde68a; }
.gap-0 { gap: 0; }
.gap-0\.5 { gap: 0.125rem; }
.gap-6 { gap: 1.5rem; }
.grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
.grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.h-32 { height: 8rem; }
.leading-none { line-height: 1; }
.list-disc { list-style-type: disc; }
.object-contain { object-fit: contain; }
.pl-4 { padding-left: 1rem; }

.mr-1 { margin-right: 0.25rem; }
.pr-3 { padding-right: 0.75rem; }
.pt-4 { padding-top: 1rem; }
.text-gray-300 { color: #d1d5db; }

.w-64 { width: 16rem; }
.hover\:bg-gray-300:hover { background-color: #d1d5db; }
.hover\:bg-red-200:hover { background-color: #fecaca; }
.hover\:text-gray-600:hover { color: #4b5563; }
.hover\:text-gray-900:hover { color: #111827; }
.hover\:text-red-700:hover { color: #b91c1c; }
.disabled\:opacity-30:disabled { opacity: 0.3; }
.space-y-2 > * + * { margin-top: 0.5rem; }
.space-y-4 > * + * { margin-top: 1rem; }
.space-y-6 > * + * { margin-top: 1.5rem; }

/* Responsive admin rules. A `^`-anchored sweep of the source sheet drops
 * these entirely — they fail only on narrow viewports, which is a
 * miserable way to find out. */
@media (min-width: 768px) {
    .md\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
    .md\:col-span-3 { grid-column: span 3 / span 3; }
}

/* Archivo, self-hosted. No CDN: the app must boot and render from service-worker
   cache with no network at all, which is the whole point of self-hosting — see
   tmp/design/specs/A1-font-pipeline.md for the measurements behind these ranges.
   Declared ranges MUST match the file's real fvar axes, or the browser
   synthesises the out-of-range values instead of interpolating them. */
@font-face {
  font-family: 'Archivo';
  src: url('/fonts/archivo-var-subset.woff2') format('woff2');
  font-weight: 400 900;
  font-stretch: 62% 100%;
  font-display: swap;
}

/* ==== _legacy-tokens · the design-kit token layer · owner: NOBODY ==== */
/* UNOWNED · DELETE-ONLY. No lane owns this file, so no lane may add to it: new
   CSS goes in that lane's own section file, listed in order.txt. Any lane MAY
   delete what its own change stranded. The pool only shrinks — that is the whole
   mechanism by which these files disappear, and an addition here rebuilds the
   shared address the split removed.

   Displaced but NOT misfiled — and the difference matters, because this file
   sat under the same wrong banner as one that IS. In the single stylesheet four
   blocks lived beneath `==== ADM1-PR3 ==== ` (this one at lines 1547-1806 at
   `9e5b26b`). Two of those four are #662: they name a SCREEN their banner
   contradicts. This one names no screen at all — a token layer belongs to every
   section and therefore to none, so there is no correct banner to move it under
   and nothing here for #662 to fix. Do not sweep it up with them.
   ==========================================================================
   Design-kit token layer — see tmp/design/specs/00-substrate.md
   ==========================================================================
   Two themes because the app is used in direct sunlight. Quoting the kit's own
   reasoning, because it is a design decision rather than a code comment:

     "volt stays the fill in both, text on volt is always black, and what
      changes is everything around it. Light mode swaps volt for a dark olive
      wherever volt would be TEXT — volt on white is unreadable."

   ⚠️ THE QUOTE ABOVE IS VERBATIM AND STILL SAYS "OLIVE". It is unchanged in
   export 28 (`rd-train-kit.jsx:1-4`, byte-identical to the previous export) and
   is reproduced as written, not corrected — the accent moved from lime to TAN in
   that export and the designer left the paragraph standing. The RULE it states
   is what survived; only the two values under it moved. Do not "fix" the quote.

   Hence --rd-volt and --rd-acc are NOT the same token and must not be merged:
   --rd-volt is the accent as a FILL and is identical in both themes;
   --rd-acc is the accent as TEXT or STROKE and goes a deep warm brown in light.
   Name the role, never the colour: a border is a stroke, so it is --rd-acc.

   THE TAN REBRAND (export 28, 2026-08-25). `acc` and `volt` were #CEFF51 lime
   in dark and `acc` #3E4E10 olive in light. `rd-train-kit.jsx:19` now reads
   `acc: '#A89968', volt: '#A89968'` and `:20` `acc: '#5C5030', volt: '#A89968'`.
   Nothing was renamed and no other value in the palette moved — census across
   the two exports: 97 #CEFF51 -> 5 (all doc chrome), 57 #3E4E10 -> 0, 0 #A89968
   -> 94, 0 #5C5030 -> 58. --rd-on-volt stays #000000 in both themes: tan is
   mid-luminance where lime was near-white, and the designer kept black ink
   anyway (`rd-train-kit.jsx:19,20`, `onVolt: '#000'`). Do not "fix" it.

   --rd-acc-wash moved to the tan family — SETTLED, no longer flagged. Ruled by
   James 28 Aug 2026 (design-reply-2026-08-28-acc-wash.md, export 32; all 25
   corpus sites swept): dark #1A1D12 -> #1D1A12, light #F0F5DC -> #F5F0DC. The
   same channel values reordered so the warm channel leads (matching volt's
   R > G > B), so relative luminance is unchanged in both themes and every
   contrast pairing that passed still passes. The wash is the fill inside an
   `acc` stroke at every site, so it follows the accent.
   The C and D scoring-zone greens (#8FA83A / #5E6B29) STAY, by the same
   ruling: zone colours are a legibility ramp on a target diagram, not brand
   tokens, and were never in scope for the rebrand. The A zone is pinned at
   #A89968 on the ramp's own authority — if a zone-colour table is ever built
   here it takes the LITERAL, never var(--rd-acc), so the next accent move
   leaves the ramp alone (the pin is noted at rd-train-run.jsx's ZONES table).

   Each [data-theme] block defines the COMPLETE set, so re-scoping a subtree is
   total. A partial block would inherit half its tokens from the outer theme,
   which is precisely the bug the theme swatch would hit — it renders a
   miniature of the theme it is OFFERING, inside the theme currently active.

   No @media (prefers-color-scheme) here, deliberately. Theme is an explicit
   stored preference:

     "a phone that flips to dark at sunset while someone is still on the line is
      worse than one that stays where they put it."

   A media query would reintroduce exactly that flip. "Match my device" is a
   stored preference VALUE resolved in Rust, not a CSS query.
   ========================================================================== */

:root,
[data-theme="dark"] {
  --rd-bg: #0B0B0B;
  --rd-surf: #151515;
  --rd-surf2: #1E1E1E;
  --rd-line: #2A2A2A;
  /* The boundary of anything TAPPABLE that is not filled with accent.
     --rd-line divides; --rd-ctrl bounds a control. The distinction is not
     cosmetic: a divider may be quiet because the content either side carries
     the meaning, while a control's boundary is often the ONLY thing saying the
     control is there.

     ⚠️ This value is a FLOOR, not a preference. WCAG 1.4.11 asks 3:1 of the
     visual information identifying a component; #6E6E6E clears it on #0B0B0B.
     "If your ground differs from mine the number moves; the 3:1 does not."

     It exists because one box failed three times (designer, 2026-08-04):
     --rd-line (the divider token, 1.4:1), then --rd-chip (WORSE, 1.2:1, on
     circular reasoning — the flanking chips "lend their affordance", but they
     are the same chip and read only because of the glyphs inside them), then
     this. Anywhere --rd-line or --rd-chip is the SOLE indicator that something
     is interactive is the same bug — see docs/roadmap/items/kit-control-boundary. */
  --rd-ctrl: #6E6E6E;
  --rd-fg: #FFFFFF;
  --rd-fg2: #B6B6B6;
  --rd-fg3: #8C8C8C;
  --rd-acc: #A89968;
  --rd-volt: #A89968;
  --rd-on-volt: #000000;
  --rd-danger: #FF5A3C;
  /* The DESTRUCTIVE FILL, and it is NOT --rd-danger. `dfill` is its own key in
     the shared TT palette (rd-train-kit.jsx): danger is the colour destructive
     TEXT and hairlines take, dfill is the ground a Delete button sits on with
     white type. In LIGHT the two happen to be equal; in DARK they are not, and
     substituting danger here would ship a brighter button than drawn in the
     theme that is default. */
  --rd-dfill: #A82A12;
  --rd-warn: #FFB020;
  --rd-chip: #232323;
  --rd-field: #1B1B1B;
  /* Derived: the kit branches on a per-theme `tex` flag in three places. A
     per-theme constant is a per-theme token, so the branch is pre-resolved here
     and no component ever tests it.
     ⚠️ --rd-app-bg IS AND STAYS A FLAT COLOUR. The dark theme's page ground is
     the concrete photograph under an Ink scrim, and it is NOT this token — it
     is --rd-app-ground, declared in kit-shell.css §0b because this file is
     UNOWNED · DELETE-ONLY and may not take a new name. The split is not
     tidiness: this token is read by consumers that require a <color> and would
     break outright on an image value — `color-mix(in srgb, var(--rd-app-bg) …)`
     in s02-splash.css, lab1-page-act.css and adm-mob.css, plus
     `--rd-icon-ground` in the RdIcon block and `--rd-chip-on-label` in the
     light theme below. An image in this token makes those declarations INVALID
     AT COMPUTED-VALUE TIME — the splash dim would drop out in the theme that is
     default, which no light-theme screenshot can see. */
  --rd-app-bg: #0B0B0B;
  --rd-nav-bg: rgba(8, 8, 8, .94);
  /* OPAQUE, and matched to OUR ground by hand. An outlined control is two
     layers and the inner layer's backdrop is the OUTER one — the stroke — not
     the page. So a translucent fill composites against the stroke and tints
     toward it: the .72 black this used to be lands on a dark warm brown inside a
     volt stroke and on rust inside a danger stroke. (Arithmetic, not a render:
     .72x#0B0B0B over #A89968 = ~#373325, over #FF5A3C = ~#4F2018.

     RECOMPUTED FOR THE TAN REBRAND, not re-quoted. The lime figure was
     ~#414F1F over #CEFF51 and is now wrong by construction; the same channel
     arithmetic — .72*0x0B + .28*channel, rounded — gives 54.96/50.76/37.04 over
     #A89968, i.e. #373325. The danger figure is untouched because --rd-danger
     did not move. The POINT is unchanged and is why the number is carried at
     all: a translucent inner fill is not the ground you think it is.)

     The kit's value is #111111 because that is the composited value of its dark
     scrim over the concrete photograph — `rd-train-kit.jsx:41`,
     `hollow = (t) => (t.tex ? '#111111' : t.bg)`.

     ⚠️ THIS MOVED #0B0B0B -> #111111 WITH THE GROUND, AND THAT WAS THE STANDING
     INSTRUCTION. The previous note read *"We ship a FLAT ground … so ours is
     #0B0B0B. The rule travels; the number is per-ground. If the texture ever
     ships, this becomes the composited value, NOT var(--rd-app-bg)."* The
     texture ships (--rd-app-ground, kit-shell.css §0b), so the number follows,
     and the "NOT var(--rd-app-bg)" half is honoured to the letter: this is a
     LITERAL, because binding it to the ground token would paint the
     photograph inside the button, which is the thing the two-layer construction
     exists to prevent.

     Light already obeyed this: --rd-hollow-fill == --rd-app-bg below. */
  --rd-hollow-fill: #111111;
  /* Washes and scrims. Values measured from the corpus; the accent wash is
     RULED (28 Aug 2026, tan family — see the banner above), no longer the
     provisional most-used lime. Panel scrim collapses three alphas
     (.88/.90/.92). */
  --rd-acc-wash: #1D1A12;
  --rd-warn-wash: #1E1804;
  /* The wash family's third member, and it was DISCOVERED rather than invented.
     S2-squad.md §6 asked whether the squad screen's danger block was a gap or an
     existing token under another name; it is a gap, and the corpus fills it at
     TWO independent sites with the identical pair — rd-squad.jsx:126 (the empty
     event roster) and rd-courses.jsx:197 — both `stroke={t.danger}` over
     `fill={t.tex ? '#1C0A06' : '#FDECE7'}`. Two witnesses at one literal is a
     role, and acc/warn already had theirs. chamfer_box.rs's Fill::Danger doc had
     even named this sibling before it existed. */
  --rd-danger-wash: #1C0A06;
  /* ⚠️ STILL TRANSLUCENT, DELIBERATELY NOT FIXED HERE. This is exposed as
     Fill::PanelScrim, i.e. as an INNER fill, so the rule above applies to it
     identically — but it has zero call sites today, and the 08-04 re-diff found
     that its stated consumer (the Stage Editor) needs alphas this token cannot
     express: .72/.78/.86 as well as the .88/.90/.92 collapsed here, and it stays
     DARK in both themes at half its sites, where ours flips to opaque #FFFFFF in
     light. Flipping the value blind would be guessing at a spec that does not
     exist yet. Blocked on the editor wave; see tmp/design/rediff-2026-08-04.md
     section F. Do not use Fill::PanelScrim on a stroked box before then. */
  --rd-panel-scrim: rgba(11, 11, 11, .90);
  /* Stage-diagram paper is deliberately THEME-INVARIANT — a diagram is drawn on
     cardboard in both themes; it is content, not chrome. The full 11-colour
     stage palette is domain-specific and rides with the Stage Editor migration,
     not Foundations. */
  --rd-paper: #F2EFE6;
  --rd-paper-edge: #C9C6BA;
  --rd-swatch-ground: #0B0B0B;
  /* Designer #17: the filled control needs a silhouette on paper. Carried as
     a TOKEN VALUE, not a structural difference — transparent IS a value, so
     the component is identical in both themes (architect ruling 2). */
  --rd-btn-edge: transparent;
  /* Selected chip — dark is the drawn treatment, unchanged. */
  --rd-chip-on-fill: var(--rd-volt);
  --rd-chip-on-label: var(--rd-on-volt);
}

[data-theme="light"] {
  --rd-bg: #F1F1EC;
  --rd-surf: #FFFFFF;
  --rd-surf2: #E7E7E0;
  --rd-line: #CDCDC2;
  /* Floor on paper, same 3:1 rule — see the dark block. */
  --rd-ctrl: #82827B;
  --rd-fg: #0B0B0B;
  --rd-fg2: #3F4039;
  --rd-fg3: #63645B;
  --rd-acc: #5C5030;
  --rd-volt: #A89968;
  --rd-on-volt: #000000;
  --rd-danger: #B32D0C;
  --rd-dfill: #B32D0C;
  --rd-warn: #7A4E00;
  --rd-chip: #E2E2D8;
  --rd-field: #FFFFFF;
  --rd-app-bg: #F1F1EC;
  --rd-nav-bg: #FFFFFF;
  --rd-hollow-fill: #F1F1EC;
  --rd-acc-wash: #F5F0DC;
  --rd-warn-wash: #FBF3DC;
  /* The light half of the same two-witness pair. Uncontested, unlike the light
     accent wash three lines up — both corpus sites say #FDECE7 and neither
     disagrees. */
  --rd-danger-wash: #FDECE7;
  --rd-panel-scrim: #FFFFFF;
  --rd-paper: #F2EFE6;
  --rd-paper-edge: #C9C6BA;
  --rd-swatch-ground: #F1F1EC;
  /* Light only: volt-on-near-white is ~1.3:1 at the edge, so the shape
     dissolves and the chamfer is invisible. */
  --rd-btn-edge: var(--rd-acc);
  /* Inverted on paper: ink fill, page-coloured label (#12). */
  --rd-chip-on-fill: var(--rd-fg);
  --rd-chip-on-label: var(--rd-app-bg);
}

/* --------------------------------------------------------------------------
   Type roles (substrate §2.3). `tdisp` is ALWAYS uppercase — the transform
   lives in the helper, so a source string's casing never reaches the render.
   Width is 62 for display and 75-78 for labels and controls — two values, as a
   rule rather than a list of instances. Body is 100 BY ABSENCE: body sites
   author no font-stretch at all, so 100 arrives as the CSS initial value, which
   is why the shipped subset clamps to 62-100 and not to 78. (66 was drift
   across five corpus sites, collapsed to 62 by the designer on 2026-08-04; the
   allowed set is enforced by ALLOWED_STRETCH in cli/src/css.rs.)
   -------------------------------------------------------------------------- */
.rd-disp {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
}
.rd-h1 { font-family: 'Archivo', system-ui, sans-serif; text-transform: uppercase; font-weight: 900; font-stretch: 62%; letter-spacing: 0; font-size: 30px; line-height: .92; color: var(--rd-fg); }
.rd-h1-alt { font-family: 'Archivo', system-ui, sans-serif; text-transform: uppercase; font-weight: 900; font-stretch: 62%; letter-spacing: .01em; font-size: 19px; line-height: .98; color: var(--rd-fg); }
.rd-lab { font-family: 'Archivo', system-ui, sans-serif; text-transform: uppercase; font-weight: 700; font-stretch: 78%; letter-spacing: .2em; font-size: 8.5px; color: var(--rd-fg3); }
.rd-lab-acc { color: var(--rd-acc); }
.rd-btn-label { font-family: 'Archivo', system-ui, sans-serif; text-transform: uppercase; font-weight: 800; font-stretch: 78%; letter-spacing: .14em; font-size: 12.5px; }
.rd-title { font-family: 'Archivo', system-ui, sans-serif; text-transform: uppercase; font-weight: 700; font-stretch: 78%; letter-spacing: .16em; font-size: 10px; color: var(--rd-fg2); }
.rd-p { font-family: 'Archivo', system-ui, sans-serif; font-size: 13px; line-height: 1.5; color: var(--rd-fg2); text-wrap: pretty; margin: 0; }
/* Numeric role, §P2 RESOLVED 2026-08-04 in favour of Foundations: Archivo 800 /
   stretch 75% / tabular. The kit's own `mono` now says the same — "not a system
   mono. Numbers are the loudest thing on a scoring surface, so they speak in the
   display voice."
   ONE qualification Foundations got wrong: NOT "always volt". Colour is --rd-fg
   by default; volt is reserved for the one number that IS the screen. Where the
   accent is right the token is --rd-acc, because a number is text. */
/* ADJUDICATED — designer answer #2. Numbers are the loudest thing on a scoring
   surface and must be in the display voice, not the system's. ONE weight: 800
   (he stripped 29 inline overrides, so a 600 or 700 here is a bug, not a
   variant). Colour is --rd-fg by DEFAULT — "always volt" was wrong and
   collides with #6; volt is for the one number that is the point of the
   screen. ui-monospace survives elsewhere for verbatim technical strings
   (server URLs, WASM error text) — just never for numbers. */
/* The numeric role. Archivo 800 / width 75 / tabular — NOT a system mono
   (substrate §P2, adjudicated 2026-08-04). Colour is --rd-fg by default; volt is
   reserved for the one number that IS the screen.

   NO letter-spacing here. The kit's shared `mono` carries none — the -.01em is
   applied at TNum's call site only (rd-train-kit.jsx:240), and TStat's value
   (:266) is untracked. It used to ride this rule, which silently tracked every
   stat value in the app. */
.rd-num { font-family: 'Archivo', system-ui, sans-serif; font-variant-numeric: tabular-nums; font-weight: 800; font-stretch: 75%; font-size: 17px; color: var(--rd-fg); }

/* --------------------------------------------------------------------------
   /kit review surface. Unlinked route; exists to show tokens and type resolving
   in both themes, and to make a broken token VISIBLE (the falsification rule).
   -------------------------------------------------------------------------- */
.rd-kit { min-height: 100vh; background: var(--rd-app-bg); color: var(--rd-fg); padding: 24px; font-family: 'Archivo', system-ui, sans-serif; }
.rd-kit-head { margin-bottom: 24px; }
.rd-kit-panels { display: flex; flex-wrap: wrap; gap: 20px; align-items: flex-start; }
.rd-kit-panel { flex: 1 1 320px; min-width: 300px; background: var(--rd-app-bg); border: 1px solid var(--rd-line); padding: 18px; }
.rd-kit-sec { margin-top: 20px; }
.rd-kit-sw-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); gap: 10px; margin-top: 8px; }
.rd-kit-sw { border: 1px solid var(--rd-line); }
.rd-kit-sw-chip { height: 44px; }
.rd-kit-sw-meta { padding: 6px 7px; background: var(--rd-surf); }
.rd-kit-sw-name { font-size: 10px; color: var(--rd-fg); word-break: break-all; }
.rd-kit-sw-val { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 9.5px; color: var(--rd-fg3); }
.rd-kit-spec { border-top: 1px solid var(--rd-line); padding: 10px 0; }
.rd-kit-spec-note { font-size: 9.5px; color: var(--rd-fg3); margin-top: 3px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.rd-kit-row { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 8px; align-items: flex-start; }
/* A container demo's inner box needs a size to be visible; the primitives supply
   their own. Review-surface chrome, not a kit rule. */
.rd-kit-boxdemo { min-height: 44px; padding: 0 14px; display: flex; align-items: center; }
/* Icon review rows. The captions matter as much as the marks: two of the twelve
   are not guessable unlabelled (the designer's own argument for mandatory tab
   labels), so a fidelity pass needs to know which one it is looking at. */
/* ⚠️ `color` IS SET HERE, and it has to be. RdIcon paints `currentColor`, and
   `color` is an ordinary inherited property — it inherits a COMPUTED value, so
   re-scoping `data-theme` further down does NOT re-resolve a colour set above.
   `.rd-kit` sets `color: var(--rd-fg)` under the document theme; without this
   line the light panel's icons inherited the DARK theme's white and rendered
   white-on-white, invisible. Caught by the render, not by a test — the tests
   cannot see a stylesheet. Every type role in the kit escapes this by setting
   its own colour; the icons are the first thing here that did not. */
.rd-kit-icons { display: grid; grid-template-columns: repeat(auto-fill, minmax(62px, 1fr)); gap: 10px; margin-top: 8px; padding: 10px; color: var(--rd-fg); }
/* The second row's ground, and it must be the token the icons are TOLD they sit
   on — if these two ever disagree the checklist's knockout is wrong and the page
   stops being evidence. */
.rd-kit-icons--surf { background: var(--rd-surf); }
.rd-kit-icon { display: flex; flex-direction: column; align-items: center; gap: 5px; }
.rd-kit-icon-name { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 9px; color: var(--rd-fg3); }
.rd-kit-warn { border-left: 2px solid var(--rd-warn); padding-left: 12px; margin-top: 18px; }

/* ==== _legacy-kit-chamfer-button · 01-chamfer-box + 02-button · owner: NOBODY ==== */
/* UNOWNED · DELETE-ONLY. No lane owns this file, so no lane may add to it: new
   CSS goes in that lane's own section file, listed in order.txt. Any lane MAY
   delete what its own change stranded. The pool only shrinks — that is the whole
   mechanism by which these files disappear, and an addition here rebuilds the
   shared address the split removed.

   THE NAME `_legacy-kit-primitives.css` IS RETIRED, NOT INHERITED. This file and
   `_legacy-kit-components-and-screens.css` are the two unowned fragments of it,
   and neither took the old name. A rename that hands an established name to
   different contents is the worse failure of the two: a broken path fails loudly
   on the next grep, while a re-pointed name fails at the moment somebody trusts
   it. The old name is now absent from the tree.

   It had been the largest part by a wide margin — 2663 lines, more than a third
   of the built stylesheet, a merge of many primitives rather than one section
   that grew. A byte-identical three-way re-partition cut it in place: the shell
   block was promoted to the owned `kit-shell.css`, the rest went to
   `_legacy-kit-components-and-screens.css`, and nothing was added, edited or
   reordered — the assembled stylesheet did not change by one byte.

   What is here is 252 lines and exactly two blocks: 01-chamfer-box and
   02-button, which is what the name says. Note the pool did not grow:
   re-partitioning re-files existing rules, so "nothing new ever lands here"
   still holds. The invariant is that the pool only shrinks, not that its files
   are immutable — and it may also re-file and rename what it already holds.

   Displaced but NOT misfiled, on the same reasoning as _legacy-tokens.css: it
   sat beneath `==== ADM1-PR3 ==== ` in the single stylesheet (lines 1807-4455 at
   `9e5b26b`), but primitives belong to every screen and therefore to none. There
   is no correct banner to move it under, so nothing here is #662's.
   ==========================================================================
   DESIGN-KIT PRIMITIVES — merged from tmp/design/kit-css/ (PR B)
   ==========================================================================
   One block per primitive, in spec order. Authored as separate files so
   eleven concurrent authors could not collide in this hand-curated
   stylesheet, then merged through two checkers:
     check-classes.py   used     -> defined
     check-css-dupes.py defined  -> defined ONCE
   The second exists because the first is blind to two authors defining the
   same selector differently, where the cascade silently last-write-wins.
   ========================================================================== */

/* ---- 01-chamfer-box.css ---- */
/* RdChamferBox — the two-layer chamfer container. Spec: docs/design/kit/01-tout.md
   The outer IS the border: a solid box filled with the stroke colour, clipped at
   `r`, padded by the border width. The inner is the surface, clipped at
   max(r - w(2 - √2), 2) — NOT r - 2w, which makes the two cuts collinear and
   the border vanish along the diagonal. Geometry arrives as custom properties (the scale is 8 chamfer
   values x 4 widths — enumerating the product as classes would be noise);
   colour stays here, where the token roles belong. */
.rd-box {
  background: var(--rd-line);
  padding: var(--rd-box-w, 1px);
  box-sizing: border-box;                 /* padding must INSET, not grow: without
                                             this every box renders 2w larger than
                                             its slot and overflows its row */
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--rd-box-r)), calc(100% - var(--rd-box-r)) 100%, 0 100%);
}
.rd-box__inner {
  background: var(--rd-surf);
  height: 100%;                           /* no-op on an auto-height card; load-bearing
                                             when the OUTER gets a definite height from
                                             a stretch row — without it the stroke colour
                                             shows as a solid slab below the content */
  box-sizing: border-box;                 /* lets the inner carry its own padding (84/84
                                             sites do) without fighting height:100% */
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--rd-box-ri)), calc(100% - var(--rd-box-ri)) 100%, 0 100%);
}

/* Stroke roles. A stroke is --rd-acc, NEVER --rd-volt (substrate §1.1). */
.rd-box--stroke-line   { background: var(--rd-line); }
.rd-box--stroke-acc    { background: var(--rd-acc); }
.rd-box--stroke-muted  { background: var(--rd-fg3); }
.rd-box--stroke-danger { background: var(--rd-danger); }
.rd-box--stroke-warn   { background: var(--rd-warn); }
/* --rd-line divides; --rd-ctrl BOUNDS A CONTROL (substrate §1.5). Not a lighter
   line: where the boundary is the only thing saying the control is there, it has
   to clear 3:1. Added with RdTextField, which AU5 §13 declares blocked on it. */
.rd-box--stroke-ctrl   { background: var(--rd-ctrl); }
/* Reserves the border's space without painting it, so a state change to a
   visible stroke costs no layout shift. */
.rd-box--stroke-none   { background: transparent; }
.rd-box--stroke-btn-edge { background: var(--rd-btn-edge); }

/* Fill roles. Never `transparent` on an outlined control — the outer layer is
   the border, so a see-through inner lets the border colour flood it. */
.rd-box--fill-surf      { background: var(--rd-surf); }
.rd-box--fill-surf2     { background: var(--rd-surf2); }
.rd-box--fill-field     { background: var(--rd-field); }
.rd-box--fill-hollow    { background: var(--rd-hollow-fill); }
.rd-box--fill-acc-wash  { background: var(--rd-acc-wash); }
.rd-box--fill-warn-wash { background: var(--rd-warn-wash); }
.rd-box--fill-danger-wash { background: var(--rd-danger-wash); }
.rd-box--fill-scrim     { background: var(--rd-panel-scrim); }
.rd-box--fill-paper     { background: var(--rd-paper); }
.rd-box--fill-volt      { background: var(--rd-volt); }
/* The gate's fault mark (AU1 §3.1). SOLID status colour, not a wash: this box IS
   the symbol and holds nothing, where the washes above tint a container that
   holds copy. `--rd-warn` is the grace variant and the only variant-conditional
   geometry-or-colour in the whole screen — "amber, not red — recoverable,
   nothing lost". */
.rd-box--fill-danger    { background: var(--rd-danger); }
.rd-box--fill-warn      { background: var(--rd-warn); }

/* ---- 02-button.css ---- */
/* RdButton — spec docs/design/kit/02-tbtn.md. Component: apps/web/src/components/kit/button.rs

   Three layers, outside in:

     .rd-btn          the real <button> AND the focus ring (§5.3). Always present.
     .rd-box          the border layer — OUTLINED KINDS ONLY, drawn by RdChamferBox.
     .rd-btn__face    the label box: the .rd-box__inner when outlined, a <span> when filled.

   The filled kinds (primary, and primary while disabled/loading) are ONE layer
   by construction (§1), so they do not go through RdChamferBox at all — there is
   nothing two-layer about them. The two-layer clip and the max(r - w(2 - √2), 2)
   inner chamfer stay in exactly one place: chamfer_box.rs + 01-chamfer-box.css.

   The type role (.rd-btn-label, tdisp(800,78,'.14em') at 12.5px, substrate §2.3)
   is DELIBERATELY NOT REDEFINED HERE — it already ships in the Foundations layer
   at apps/web/style.css:981 and the face element carries both classes. */

/* --------------------------------------------------------------------------
   Layer 3 — the focus ring, which is also the button.

   A ring cannot be an `outline` or a `box-shadow`: clip-path clips both, so
   either one is erased on exactly the element that needs it (§5.3). It has to be
   its own box outside the clip. Making that box the <button> pays for itself
   three times — it is the element that takes focus, the element that takes the
   click, and (because it is always rendered) 2px of permanent hit box on every
   side. That last one is what lifts the dense filled primary from 46 to 50 and
   clears Android's 48dp WITHOUT moving a painted pixel (§3.5; the architect's
   ruling is that platform minimums are met by hit-area extension, not by
   redrawing the designer's geometry). Guarded by hit_target_height_px's tests.
   -------------------------------------------------------------------------- */
.rd-btn {
  /* 13, RULED — `01 Foundations` §03, re-ruled 26 Aug 2026: 13px on controls,
     14px on containers, superseding that section's original 14/20. This was
     PROVISIONAL against a Foundations that said 14, and the note here used to
     call it "the one-line flip if the designer rules 14"; he ruled the other
     way, for the value every TBtn variant already drew. Mirrored by
     CONTROL_CHAMFER_PX (kit/chamfer_box.rs, re-exported by button.rs), which
     feeds the outlined kinds' r, and by --rd-chamfer-control on :root
     (kit-p0.css). All of them move together. VALUE UNCHANGED by the ruling. */
  --rd-btn-r: 13px;
  /* 13 + 3: the cut stays proportional to the element (substrate §3.2) and 16 is
     already on the kit's tcham scale. NOT 17 — see the_focus_ring_does_not_
     vanish_along_the_cut. Mirrors FOCUS_RING_CHAMFER_PX. */
  --rd-btn-ring-r: 16px;
  display: inline-block;      /* the UA default for <button>; stated because the
                                 `wide: false` case depends on it, below */
  width: 100%;                /* wide is the default: 50 of 52 corpus sites */
  vertical-align: top;        /* an inline-block sits on the baseline, which hangs
                                 a descender's worth of blank under the button */
  padding: 2px;               /* the ring's thickness on the straight edges, and
                                 the whole of the hit-area extension. Mirrors
                                 FOCUS_RING_PAD_PX in button.rs */
  margin: 0;
  box-sizing: border-box;
  background: transparent;    /* the ring, unlit. Always occupying its space, so
                                 focus costs no layout shift */
  border: 0;                  /* style.css:112 already zeroes it; explicit here so
                                 this component does not depend on that rule */
  appearance: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;  /* the UA highlight is a RECTANGLE and
                                                ignores the chamfer, which reads as
                                                the geometry breaking on tap */
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--rd-btn-ring-r)), calc(100% - var(--rd-btn-ring-r)) 100%, 0 100%);
}

/* §3.4 / §9.2. `width: auto` is exactly what the kit does (`width: undefined`).
   Under a stretching flex parent it is a no-op — which is why the corpus's two
   wide={false} sites render full width anyway. Routed to the designer; we do NOT
   add `align-self: start` here, because that is a visible change from the
   preview and needs his sign-off, not our judgement. */
.rd-btn--auto { width: auto; }

.rd-btn:disabled { cursor: default; }

/* 🟡 PROVISIONAL pending Claude Design (§5.3). Focus in this system is an
   --rd-acc stroke — that is the kit's own idiom for inputs (TNum draws focus as
   stroke={focus ? t.acc : t.line}, rd-train-kit.jsx:103). The ring stays
   --rd-acc on the DANGER kind too: focus is a system affordance, not a severity
   signal, and a red ring on a red button is invisible.
   `outline: none` only where we replace it — and note the UA outline was already
   being clipped away, so this documents intent rather than doing work. */
.rd-btn:focus-visible {
  background: var(--rd-acc);
  outline: none;
}

/* --------------------------------------------------------------------------
   The label box — the INNER layer of the two-layer control.

   ADJUDICATED. This previously carried min-height 46 and treated the resulting
   3px asymmetry (46 filled / 49 outlined) as the spec's intent. The designer
   ruled it a bug and gave the rule that explains it:

     "height is a property of the painted box, and on a two-layer control the
      inner layer's min-height is not it."

   The wrapper adds 2w outside this box, so the painted height is
   inner + 2 x 1.5. Every kind is two-layer since #17, so 45 + 3 = 48 painted
   everywhere — and 48 is itself adjudicated: the dense control is the most
   tapped in the system, used outdoors in gloves, so it is 48 PAINTED rather
   than 48 by hit area.
   -------------------------------------------------------------------------- */
.rd-btn__face {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 20px;
  min-height: 45px;           /* + 2 x 1.5 border = 48 painted */
  box-sizing: border-box;
}
.rd-btn--tall .rd-btn__face { min-height: 49px; }   /* + 3 = 52 painted */

/* The single-layer filled construction is GONE — designer answer #17 made every
   kind two-layer, so the fill now arrives through RdChamferBox's Fill role
   (.rd-box--fill-volt live, .rd-box--fill-surf2 disabled) and the clip comes from
   .rd-box. The rules that used to live here (--filled, --volt, --surf2) were
   deleted rather than left: a rule matching nothing is invisible to both
   checkers, and one describing a construction that no longer exists is worse
   than absent.

/* Label colours (§2). A stroke is --rd-acc and never --rd-volt (substrate §1.1);
   volt appears here only as a FILL, above. */
.rd-btn__face--on-volt { color: var(--rd-on-volt); }
.rd-btn__face--acc     { color: var(--rd-acc); }
.rd-btn__face--danger  { color: var(--rd-danger); }
.rd-btn__face--fg2     { color: var(--rd-fg2); }
/* Every kind's inactive label. --rd-fg3 was lifted for sunlight legibility
   (rd-train-kit.jsx:6-10) — it is a quiet grey, not a dim one. */
.rd-btn__face--fg3     { color: var(--rd-fg3); }

/* --------------------------------------------------------------------------
   🟡 PROVISIONAL pending Claude Design — hover (§5.2) and pressed (§5.4).

   The kit specifies neither: the frames are iOS, which has no hover, and the
   corpus has zero `transition`, zero `:active` and zero `focus-visible`. So
   MOTION IS NOT PART OF THIS SYSTEM, and these are fill changes only — no
   transform, no scale, no elevation. A chamfered box that scales makes its
   diagonal crawl against the pixel grid.

   The system has no alpha ramp, and as of 2026-08-04 it has no translucent
   fill either — --rd-hollow-fill went opaque because an inner layer composites
   against its stroke, not the page. color-mix is what keeps the remaining
   compositing inside the token layer: "volt under an 8% --rd-on-volt scrim" is
   expressible without a hex literal, and volt is never re-hued.

   Geometry must not change in either state — a thicker border or a size step
   would shift layout and shimmer the diagonal.
   -------------------------------------------------------------------------- */
@media (hover: hover) and (pointer: fine) {
  .rd-btn:not(:disabled):hover .rd-box--fill-volt {
    background: color-mix(in srgb, var(--rd-on-volt) 8%, var(--rd-volt));
  }
  /* One opaque step up. Border and label untouched, and the fill must never move
     TOWARD the stroke colour — that is the volt-on-volt flood §7.2 rules out. */
  .rd-btn:not(:disabled):hover .rd-box--fill-hollow { background: var(--rd-surf); }
}
.rd-btn:not(:disabled):active .rd-box--fill-volt {
  background: color-mix(in srgb, var(--rd-on-volt) 16%, var(--rd-volt));
}
.rd-btn:not(:disabled):active .rd-box--fill-hollow { background: var(--rd-surf2); }

/* Empty and error states are N/A for this primitive (§5.7): a button has no
   empty state, and errors belong to the field or the banner. Recorded rather
   than omitted. Loading (§5.6) has no styling of its own — it takes the
   per-kind disabled treatment, which is why there is no .rd-btn--loading. */

/* ==== kit-ok · the success token pair + its one fill role · owner: lane-3 ==== */

/* THE NEW TOKEN OF EXPORT 28, AND THE ONLY KEY THE TAN REBRAND ADDED.
   `rd-train-kit.jsx:19` gained `ok: '#4EB56D'` in the dark theme object and
   `:20` `ok: '#2E6B3C'` in the light one. Nothing was renamed and no other
   palette value moved with it — the census across the two exports is 0 -> 5
   `#4EB56D` and 0 -> 3 `#2E6B3C`, against 97 -> 5 `#CEFF51` and 57 -> 0
   `#3E4E10`. It is a sibling of `danger` and `warn`, not of `acc`/`volt`: a
   STATUS colour, spent on a verdict, never on chrome.

   WHY IT EXISTS AT ALL — the reason is written down outside the kit file.
   `48 Kit Rules.html`, §Games (55 Band), the rule "Celebratory motion is the
   green flood, and green means what it already meant": *"A new personal best
   washes the screen t.ok (#4EB56D dark / #2E6B3C light — the Auth success
   token, no new colour) for a beat"*. That sentence does two things at once. It
   names the values, and it says the token was ALREADY the auth success colour —
   so `ok` is not a games colour that leaked outward, it is the success colour
   the auth screens were already using under another name, given a key.

   ⚠️ THE PAIR IS DECLARED HERE AND NOT IN `_legacy-tokens.css`. That file is
   UNOWNED / DELETE-ONLY — "no lane owns this file, so no lane may add to it" —
   so a new pair goes in the part that owns it. This is the arrangement
   `kit-loading.css` records for `--rd-skel-ink`, in those words, and it is the
   only one available: the four VALUE edits of this rebrand are edits to
   declarations that already exist in the token layer and are legal there; a
   fifth and sixth NAME are not. The flip mechanism is still that file's — both
   selectors below are its two, character for character, so a `[data-theme]`
   re-scope of a subtree carries `ok` exactly as it carries `acc`.

   ⚠️ `--rd-on-ok` IS OUR NAME, NOT THE CORPUS'S. The theme objects carry no
   `onOk` key; the rule exists only as prose on the button primitive, at
   `rd-train-kit.jsx:207` ("Success fill"): *"Success fill — a chosen/confirmed
   PASS verdict only. Text follows contrast: black on the dark theme's lighter
   green, white on the light theme's deep one."* — implemented at `:210`
   ("kind === 'ok'") as `color: t.tex ? '#000' : '#fff'`, where `t.tex` is the
   dark theme. So the VALUES are the corpus's and only the identifier is ours;
   it is spelled after `--rd-on-volt`, the one companion-ink token that already
   existed, because a second naming scheme for the same idea is a worse cost
   than a name the designer did not choose.

   ⚠️ AND IT INVERTS `--rd-on-volt`. Volt takes black ink in BOTH themes; `ok`
   takes black in dark and white in light, because the light theme's success
   green is the DEEP one. That is the one genuinely new contrast rule in this
   export, and it is why the ink needs a token rather than a literal: a single
   theme-invariant `#000` would be unreadable on `#2E6B3C`.
   --------------------------------------------------------------------------- */
:root,
[data-theme="dark"] {
  --rd-ok: #4EB56D;
  --rd-on-ok: #000000;
}

[data-theme="light"] {
  --rd-ok: #2E6B3C;
  --rd-on-ok: #FFFFFF;
}

/* The success MARK, filled solid — `Fill::Ok`, and the shipped surface behind
   the token. `rd-auth-screens.jsx:353` ("Email verified") moved the
   email-verified frame's 46x46 mark from `c: RD.volt` to `c: RD.ok`, so the one
   screen in the app that draws a success symbol now draws it green.

   It sits with `.rd-box--fill-danger` and `.rd-box--fill-warn` in role, not in
   file: those two are the gate's fault marks and live in the DELETE-ONLY pool
   (`_legacy-kit-chamfer-button.css`), which is why the third member of the set
   is here instead. Same construction as both — this box IS the symbol and holds
   nothing, where the washes tint a container that holds copy.

   ⚠️ NO `TBtn` KIND `ok` IS BUILT — **AND THE FILL IS NOW REACHED ANYWAY.**
   Export 28 added the kind (`rd-train-kit.jsx:210`) and its single drawn call
   site is `rd-assess.jsx:244` ("sel === 'pass'"), the Pass/Fail "Your call"
   pair. This note used to end there, reasoning that the pair lived only on the
   assessment DECLARE screen — a screen this app does not ship
   (`pages/assessment_quals.rs` records at its head that no
   `declare`/`qualification_status`/`passed_at` RPC or field exists anywhere,
   and pins the absence with a test) — so the kind was STUBBED as a fill nothing
   could reach.

   **Corrected 2026-08-27 (audit r7 doc 08, D4).** The conclusion about the
   *kind* still holds and the premise about the *fill* did not: the drawn "Your
   call" pair ships as an inline section of `ScoreEntry`
   (`pages/score_inputs.rs`, the `asks_verdict` block), not as the declare
   screen, and its selected-Pass half is now painted `--rd-ok` / `--rd-on-ok` by
   `.rd-scorein__toggle-btn--pass-on` (`adm0-picker.css`). So the token and its
   companion ink are consumed as declared, by a hand-rolled toggle rather than
   by an `RdButton` kind — which is the shape `adm0-picker.css` already argues
   for at that cluster ("No kit primitive serves a stepper or a two-way
   toggle"). The `match` arm and the button class are still unbuilt, and still
   wait on a caller that wants a `TBtn`. */
.rd-box--fill-ok { background: var(--rd-ok); }
/* ==== kit-shell · RdShell, the app shell · owner: spine ==== */
/* OWNED. Promoted out of the unowned pool by a byte-identical re-partition, not
   copied from it — see the commit that cut the old merged primitive pool in three
   (`_legacy-kit-chamfer-button.css` and `_legacy-kit-components-and-screens.css`
   are the two unowned fragments; the pool's former name is retired, not reused).
   The rules below arrived unchanged; the only thing added since is the nested
   inset suppression, which is marked where it sits.

   Spec: docs/design/kit/03-tshell.md. The component is
   apps/web/src/components/kit/shell.rs. */

/* ---- 03-shell.css ---- */
/* RdShell — the app shell. Spec: docs/design/kit/03-tshell.md
   Anatomy, top to bottom, in one flex column: top inset -> banner slot ->
   optional back/title bar -> scrolling content -> tab bar. The order is
   load-bearing (spec §1). Slots 1, 2 and 5 are full-bleed; slots 3 and 4 share
   one gutter, which is why the back label and the first line of content align
   to the same left edge.

   Two elements, not one: the VIEWPORT layer paints the ground, the FRAME is the
   width-capped column. See .rd-shell below for why they cannot be merged. */

/* ---------------------------------------------------------------------------
   0. THE HOME-INDICATOR CLEARANCE — two tokens, declared here, consumed
      everywhere.

   `48 Kit Rules.html`, Shell Geometry, ruled by James on 25 Aug from first-hand
   misfires: **"Nothing tappable sits in the home-indicator zone."** A button
   ending at the bottom edge of a phone screen sits under iOS's swipe-away
   gesture, and the tap is eaten by the system.

   The rule's own sentence says where the clearance lives: *"The kit owns the
   clearance so no screen has to remember it."* That is why these are two tokens
   in the shell's part rather than a number repeated at each of the surfaces
   below — a screen that has to remember a number is a screen that will forget
   it, which is exactly how the four nested shells lost their top inset (§3).

   The four surfaces the rule enumerates, and where each one is satisfied:

     - `TShell` — every NAV-LESS phone screen ends with a 28px spacer.
       `rd-train-kit.jsx:164`: `{nav ? <TNav … /> : <div style={{ height: 28,
       flex: 'none' }} />}`. Ours is §7a below, emitted by `shell.rs`.
     - `TNav` — the tab bar keeps the 18px it was already ruled on 20 Aug
       (`rd-train-kit.jsx:131`, `paddingBottom: 18`). Unchanged, and
       DELIBERATELY not folded into `--rd-home-clear`: it is a different
       number with a different ruling date, and one token holding two values is
       how a later edit moves the one nobody was looking at.
     - `RDShell` (auth) — pads 28 (`rd-auth-screens.jsx:55-56`). Our auth
       screens mount `RdShell` with no tab bar (`pages/login.rs:380`), so they
       take §7a's spacer and there is nothing separate to maintain. One
       mechanism, two documents.
     - Every BOTTOM SHEET ends with 40px under its last act
       (`rd-pro-mobile.jsx:124`, `rd-ro-day.jsx:224`, `rd-shell-lab.jsx:474`,
       `rd-editor-patterns.jsx:132` — all four moved from 18/26 to 40 in
       export 29).

   ⚠️ WHICH SHEETS TAKE `--rd-sheet-clear`, AND WHY IT IS NOT ALL OF THEM.
   The clearance is measured from the VIEWPORT's bottom edge, because that is
   where the home indicator is. A sheet anchored to the viewport
   (`.rd-prom__sheet`, `.rd-prom-sheet__panel`, `.rd-board__void-sheet` — each
   inside a `position: fixed; inset: 0` overlay) must carry the whole 40 itself.
   A sheet anchored INSIDE `.rd-shell__content` must not: the content region now
   ends 28px above the viewport bottom (§7a is its flow sibling), so those sheets
   already clear more than 40 and adding the token on top would double-count.
   `.rd-cb__sheet-card` and `.rd-plan__sheet-card` are that second kind — 28 from
   the shell + their drawn 12px offset + 14px of card padding = 54 — and are
   deliberately left alone. If one of them is ever re-anchored to the viewport,
   it takes the token on that day.

   ⚠️ THE 55 BAND'S 96px GAME CONTROLS ARE NOT TOUCHED. The rule carves them
   out in its own words: *"The 55 band's 96px bottom-edge game controls predate
   this rule and keep their own ruling for now."* `.rd-sns__thumbs`
   (`s55-sns.css`) keeps its 18px floor. It gets §7a's 28 underneath it anyway,
   because `rd-shootnoshoot.jsx:111` mounts the run screen in a NAV-LESS
   `TShell` and the designer moved `TShell` knowing that — the carve-out is
   about the thumbs' own number, not about the shell they sit in.

   ⚠️ `max()` AND NOT A CONSTANT, for the reason `04-tnav`'s bar already
   states: the floor is for the case where the browser reports no inset
   (desktop, Android, iOS Safari in a tab); where the platform reports a real
   one (iOS standalone/PWA) the real inset is larger and wins. `viewport-fit=cover`
   in `index.html` is what makes the `env()` half resolve at all.

   ⚠️ DECLARED HERE, ON `:root`, AND NOT IN `_legacy-tokens.css`. That part is
   UNOWNED · DELETE-ONLY and no new NAME may be added there — the same wall
   `kit-ok.css` hit with `--rd-ok` / `--rd-on-ok`, answered the same way. This
   part is the right owner because the shell is what the rule names first and
   what emits the spacer.
   --------------------------------------------------------------------------- */
:root {
  /* Nav-less phone shell (and auth): 28px, or the device's real inset. */
  --rd-home-clear: max(28px, env(safe-area-inset-bottom));

  /* A viewport-anchored bottom sheet, under its last act: 40px, or the
     device's real inset. */
  --rd-sheet-clear: max(40px, env(safe-area-inset-bottom));
}

/* ---------------------------------------------------------------------------
   0b. THE APP GROUND — one token, declared here, consumed by four roots.

   In dark it is the drawn gradient-over-texture; in light it is the flat paper.
   `rd-train-kit.jsx:148` (TShell) and `rd-tablet-kit.jsx:319` (TabShell) draw
   the identical value off the same per-theme `tex` flag:

     t.tex ? `linear-gradient(rgba(11,11,11,.74),rgba(11,11,11,.9)),
              url(${TEX}) center/cover` : t.bg

   `01 Foundations` §02 Logo & Texture is why it is a scrim and not wallpaper:
   *"The supplied texture is a low-contrast concrete vignette. It should sit at
   full strength behind auth and splash only; inside the app it drops to ~30%
   over Ink so it reads as material, not wallpaper."* The two gradient stops ARE
   that drop — .74 to .9 of Ink over the photograph. The auth half of that split
   is the separate PSD rebuild in `s02-splash.css`, whose §5 is what keeps this
   ground off those screens.

   ⚠️ WHY IT IS HERE AND NOT IN `_legacy-tokens.css` WITH EVERY OTHER TOKEN.
   That file is UNOWNED · DELETE-ONLY — *"no lane owns this file, so no lane may
   add to it: new CSS goes in that lane's own section file"* — and a new token
   is a new name, so it is an addition however small. Declaring it here is the
   rule working, not an exception to it: this part already declares two tokens
   under the same argument (§0 above), and the ground is the shell's own
   concern before it is anyone else's.

   The tokens sheet's "each [data-theme] block defines the COMPLETE set, so
   re-scoping a subtree is total" invariant is NOT weakened by the move, and
   that was the thing worth checking. The blocks below carry the SAME selectors
   the sheet's do, character for character, so an element that re-scopes
   `data-theme` — `RdThemeSwatch`, which renders a miniature of the theme it is
   OFFERING inside the theme currently active — rebinds this token exactly as it
   rebinds the other fifty. Five parts already do this: `kit-ok.css`,
   `kit-loading.css`, `kit-tablet-rail.css`, `adm2.css`, `s03-drills.css`.

   ⚠️ A SECOND TOKEN, NOT A NEW VALUE FOR `--rd-app-bg`. That one must stay a
   <color>: three parts feed it to `color-mix()` (`s02-splash.css`,
   `lab1-page-act.css`, `adm-mob.css`) and the RdIcon block reads it as
   `--rd-icon-ground`. An image there is invalid at computed-value time, so the
   splash dim would drop out silently in the theme that is default.

   ⚠️ THE COST DECISION THAT BLOCKED THE TEXTURE IS TAKEN, AND IT WAS TAKEN ON A
   DIFFERENT NUMBER. Both this part and the tokens sheet used to refuse it in
   writing — *"BG.jpeg is 419,630 bytes against a precache payload of five small
   entries"*, *"flat ground until it is"*. That was the JPEG master. The
   vendored asset is the same 1080x1920 pixels re-encoded to WebP q60 —
   `assets/brand/bg-concrete.webp`, 54,280 bytes, about 13% of the figure the
   refusal was written against. It arrives with a Trunk copy-file
   (`index.html`, pinned by `brand.rs::every_referenced_asset_is_wired_and_on_disk`)
   and WITHOUT a PRECACHE_ASSETS entry or a CACHE_NAME bump, which is the splash
   imagery's posture and is pinned by
   `brand.rs::the_ground_texture_is_cached_lazily_and_never_precached`.

   ⚠️ NO `background-attachment: fixed`. It is unreliable on iOS Safari, and it
   is unnecessary here besides. A background is painted on the element's own
   padding box and is fixed to it — `background-attachment: scroll` means fixed
   with regard to the element, NOT scrolling with its contents — so a ground on
   a viewport-sized box holds still while the content inside it scrolls, on
   whichever axis that box happens to scroll. That is the whole mechanism, and
   it is why the console's own scroller (`.rd-pro-page`, adm0-chrome.css) had to
   be made transparent rather than given a second copy of this token: painted
   there, `cover` would size to a box that GROWS with the page's content, and
   the texture would stretch and slide.

   The trailing `var(--rd-app-bg)` is the layer UNDER the photograph: on a 404
   or an offline first fetch the two scrim stops would otherwise composite
   against nothing and the page would go translucent.

   ⚠️ FOUR CONSUMERS, AND TWO NAMED NON-CONSUMERS. "Every root that paints a
   page" is what this paragraph wanted to say, and it is not true — so the
   exclusions are listed rather than left for a reader to meet as a bug:

     TAKES IT
       `.rd-shell`      §1 below — the phone shell.
       `.rd-tshell`     kit-tablet-shell.css §1 — the tablet shell.
       `.rd-pro`        adm0-chrome.css — the desktop console frame.
       `.rd-pro-pick`   adm0-picker.css — the pre-org console entry, its own
                        full-viewport root and not inside `.rd-pro`.

     DELIBERATELY FLAT, each with the argument on its own rule
       `.rd-prom`       adm0-mobile.css — the phone Pro shell. `min-height` with
                        the DOCUMENT scrolling, so it is not a still box and
                        `cover` would size to a padding box that grows with the
                        content. Giving it the ground needs a non-scrolling
                        ground layer: a structural change, not this one.
       `.rd-tcard`      s02-tcard.css — the tablet AUTH ground, and the one
                        exclusion that is a design ruling rather than a
                        mechanism. `01 Foundations` §02 puts the photograph at
                        full strength behind auth and at ~30% inside the app,
                        and this token is the in-app half. That part's banner
                        carries the argument, including why a token here would
                        split one auth family in two.
   --------------------------------------------------------------------------- */
:root,
[data-theme="dark"] {
  /* The scrollbar's two colours — §8 below is the one rule that reads them.
     Thumb: a low-contrast ink tint over the dark ground; track: none. */
  --rd-scroll-thumb: rgba(255, 255, 255, .22);
  --rd-scroll-track: transparent;
  --rd-app-ground:
    linear-gradient(rgba(11, 11, 11, .74), rgba(11, 11, 11, .9)),
    url("/brand/bg-concrete.webp") center / cover,
    var(--rd-app-bg);
}

/* FLAT ON PAPER, and that is the drawing rather than a simplification: both
   kits gate the texture on the theme's own `tex` flag, `1` in dark and `0` in
   light (`rd-train-kit.jsx:19,20`), so the light branch of the same ternary is
   the bare `t.bg`. */
[data-theme="light"] {
  /* Paper tint on the light ground; the track stays none. */
  --rd-scroll-thumb: rgba(0, 0, 0, .22);
  --rd-scroll-track: transparent;
  --rd-app-ground: var(--rd-app-bg);
}

/* ---------------------------------------------------------------------------
   0c. THE ROOT CANVAS — the surface BEHIND every root above.

   ⚠️ NOTHING PAINTED IT, AND ON AN INSTALLED iOS PWA THAT IS VISIBLE. Reported
   by James on 2026-08-26: a rubber-band overscroll in the standalone app pulls
   the app's own shell away from the viewport edge and reveals WHITE behind it.

   The white was not a default. `index.html`'s `<body>` carried the pre-kit
   Tailwind utilities `bg-gray-50 text-gray-900 min-h-screen`, and `bg-gray-50`
   is `#f9fafb` (`_legacy-base.css`) — a value that belongs to no token in this
   design system, the same Tailwind pair the manifest shipped until
   `brand.rs::the_manifest_colours_are_the_app_tokens` replaced them. With no
   background on `html`, CSS PROPAGATES the body's background to the canvas, so
   that near-white became the colour of the whole document canvas — the surface
   the overscroll gutter, the iOS status-bar area and the pre-boot page all
   show. Every root listed in §0b paints INSIDE that canvas and so hid it,
   except at the one moment the viewport is dragged past its own content.

   THE FIX IS ONE DECLARATION AND THE TOKEN CARRIES BOTH THEMES. `--rd-app-bg`
   is redeclared per `[data-theme]` block, and the attribute is stamped ON THE
   DOCUMENT ELEMENT (`theme.rs::apply`, resolving `Theme::Auto` through
   `painted()` first) — so this rule is on the very element the theme mechanism
   writes to, and light paints paper where dark paints ink. No media query is
   introduced: `_legacy-tokens.css` rules that out in writing, and none is
   needed when the selector is the themed element itself.

   ⚠️ THE FLAT `--rd-app-bg`, NOT `--rd-app-ground`. The photograph is the
   IN-APP ground — `01 Foundations` §02 puts it at ~30% over Ink inside the app
   and at full strength behind auth — and `.rd-tcard` (s02-tcard.css) exists to
   keep it off the auth family. A copy on the canvas would paint the in-app
   texture behind those screens on exactly the drag that exposes it, and would
   `cover` a canvas that is not a still box. Flat ink is also what the pre-boot
   splash, the `theme-color` meta and the manifest already show for this
   surface, so the overscroll gutter matches the frame the OS draws around it.

   `body` is named as well as `html`, and it is not redundant while the pre-kit
   utilities live on the tag: an element selector loses to a class, so `html`
   alone would still be overpainted by any `bg-*` a future body class carried.
   Both are the same token, so there is no second value to keep in step.

   ⚠️ NO `overscroll-behavior`, DELIBERATELY. The bounce is the platform's and
   James asked for it to show the app's ground, not to stop. Suppressing it
   would hide this defect rather than fix it.
   --------------------------------------------------------------------------- */
html,
body {
  background: var(--rd-app-bg);
}

/* ---------------------------------------------------------------------------
   0d. ROOT SIZING — `html`, `body` and the mount root. THE PAGE NEVER SCROLLS.

   ⚠️ A MEASURED FIELD DEFECT (James, iPad, installed PWA, 2026-08-26): open the
   on-screen keyboard on a form, close it again, and the app HEADER is above the
   fold — the shooter has to scroll the page back up to see it. Also reachable
   in a Safari tab.

   The shell has always said "the REGION scrolls, not the document" (§1's
   `overflow: hidden`, §6's `overflow-y: auto`), but NOTHING SAID IT TO THE
   DOCUMENT. Three facts compounded:

     1. Neither `html`, `body` nor Dioxus's `#main` mount root was SIZED or
        CLIPPED anywhere in this stylesheet. §0c above now names two of the
        three, and it is a different job on the same elements — it PAINTS the
        canvas and sets no box property at all; nothing there makes the
        document any less scrollable. The other rules that reach these
        selectors are `_legacy-base.css:24` (type) and §8 (scrollbars).
     2. `index.html`'s `<body>` carries the legacy utility `min-h-screen`
        (`_legacy-base.css:27`, `min-height: 100vh`), and §0c's pass left it
        there deliberately — `index.html:171`, *"neither propagates"*, which is
        true of the property it was reasoning about. It is still a HEIGHT on the
        document: on iOS `100vh` is the URL-bar-COLLAPSED height — the same trap
        §1 avoids with `dvh` — so in a tab the document is taller than the glass
        before anything is rendered into it, and the page can scroll.
     3. iOS does not shrink the LAYOUT viewport when the keyboard opens, only
        the visual one. To reveal a focused field Safari scrolls the nearest
        scrollable ancestor — and a scrollable document is one. It scrolls the
        page down, and on dismissal it has nothing telling it to come back.

   So the fix is to say it: the root chain is exactly one viewport tall and
   cannot scroll (the mount root keeps a scroller only a page that is not a
   shell can use — see the second rule below), which leaves
   `.rd-shell__content` (§6) — and on the tablet
   `.rd-tpane` (kit-tablet-panes.css) — as the only scroller Safari can find.
   A field-reveal then moves the CONTENT under the chrome, which is the
   behaviour every other platform already had here.

   `height: 100%` and not `100dvh`: the chain's job is to hand a DEFINITE height
   down to `#main` (which `.rd-ccard`/`.rd-gate`'s `min-height: 100%` need), and
   a percentage inherits whatever the viewport actually is. The elements that
   must measure the viewport themselves already say `100dvh` and say why (§1,
   `s02-tcard.css`, `kit-tablet-shell.css` §1). `min-height` is restated so this
   rule outranks `.min-h-screen` (0,1,2 against 0,1,0) rather than merely
   sitting later in the sheet.

   ⚠️ BOTH APPS — AND IT WAS SCOPED TO THE SHOOTER FOR EXACTLY ONE RELEASE.
   The rule shipped as `html[data-app="shooter"]` because the PHONE console
   still needed the document scroller: `.rd-prom` was `min-height: 100dvh` with
   no scroller of its own, so clipping the document there would have made the
   bottom of a long admin list unreachable. That was named at the time as a
   SEPARATE fix, and it is now done — `adm0-mobile.css` §1 gives `.rd-prom` a
   definite height and a clip and makes `.rd-prom__body` the one scroller, which
   is what the drawing has always shown (`rd-pro-mobile.jsx:48` mounts the phone
   console inside `TShell`, and `rd-train-kit.jsx:150` is `height: 100%` +
   `overflow: hidden`). The DESKTOP console never needed the scoping at all:
   `.rd-pro` is already `height: 100dvh; overflow: hidden` with `.rd-pro-page`
   as its one scroller (`adm0-chrome.css`).

   So the selector loses the attribute rather than gaining a second value. One
   document, one rule: an app-scoped clip would have to be restated for every
   future `data-app`, and the day one is added without it is the day this defect
   comes back on that surface alone. Since 2026-08-30 nothing in this part is
   per-app any more (§8's scrollbar is one rule for both apps); the stamp
   itself is unchanged (before first paint, kept current across SPA
   navigation).

   ⚠️ THE RUBBER-BAND IS LEFT NATIVE — SAME RULING AS §0c, RESTATED HERE BECAUSE
   THIS IS THE RULE THAT WOULD ATTRACT THE SUPPRESSION. Clipping a root is
   exactly when someone reaches for the property §0c declines, and the two
   sections are one ruling: the bounce SHOWS THE GROUND — §0c paints that ground
   with `--rd-app-bg` — so it is the surface doing its job rather than a stray
   gesture to stop. `brand.rs` pins the absence across this whole part; do not
   add it back without a new ruling.
   --------------------------------------------------------------------------- */
html,
body {
  height: 100%;
  min-height: 100%;
  overflow: hidden;
}

/* ⚠️ THE MOUNT ROOT IS `auto`, NOT `hidden`, AND THAT IS NOT A HOLE IN THE RULE
   ABOVE — IT IS WHERE THE ONE ROUTE THAT IS NOT A SHELL LIVES.

   Everything mounted here is a viewport-sized box that clips itself: `.rd-shell`
   (§1), `.rd-tshell` (`kit-tablet-shell.css` §1), `.rd-tcard` (`s02-tcard.css`),
   and on the console side `.rd-pro` (`adm0-chrome.css`) and `.rd-prom`
   (`adm0-mobile.css` §1). Every one is `100dvh`, and `dvh` is at most this
   element's `100%` — the LARGE viewport — so on every shelled screen this box
   has nothing to scroll and never scrolls, keyboard or no keyboard. That is the
   property the field defect needed, and it is the reason the reveal now lands on
   `.rd-shell__content` / `.rd-prom__body` instead.

   `/kit` is the exception it is left `auto` for: the design-kit review surface is
   declared OUTSIDE BOTH LAYOUTS on purpose (`main.rs`, "the shell would put its
   own chrome between the reviewer and the thing under review"), so it renders
   `.rd-kit` — `min-height: 100vh`, no shell, no scroller of its own — straight
   into this element. `.rd-pro-pick` (`adm0-picker.css`), the console's pre-org
   entry, is the console's twin of that case: `admin/layout.rs`'s `needs_org_pick`
   arm returns early with no rail and no frame, so it too lands here unshelled. `hidden` here would make the bottom of the token review
   unreachable, and the fix for that would have to be authored in
   `_legacy-tokens.css`, which is UNOWNED · DELETE-ONLY.

   So the clip is on `html`/`body`, where the browser's own scroll lives, and this
   element carries the definite height plus a scroller that only an unshelled page
   can use. */
#main {
  height: 100%;
  overflow: auto;
}

/* ---------------------------------------------------------------------------
   0e. THE FADE / ACT Z-ORDER CONTRACT — two tokens, declared here, consumed by
       every scroll-edge fade and every act that floats over one.

   THE RULING, James, 2026-08-28, off the staging `/admin/members` phone view:
   **acts are always above list fades.** He found the *Invite* act sitting
   BEHIND the list's bottom fade gradient — the scroll-edge fade painting over
   the button that is supposed to float on top of it.

   THE CAUSE, and it is worth writing down because a z-index alone does not
   explain it: `adm-mob.css`'s `.rd-prom-fab` drew its fade as its OWN
   `::before` with `z-index: -1`. Two mechanisms then conspire, and either one
   is enough on its own —

     - `.rd-prom-fab` carries a `clip-path`, and a `clip-path` establishes a
       containing block for `position: fixed` descendants AND clips them. So a
       "full-width, bottom-anchored" pseudo-element is confined to the button's
       own 44px box. The gradient never painted the list end at all.
     - `.rd-prom-fab` also carries a `z-index`, so it forms a stacking context.
       Inside one, a `z-index: -1` child paints ABOVE the context element's own
       background and below its content — so the app-ground gradient landed on
       top of the button's volt fill.

   Together: a bar of app ground over the act, exactly where the act is. The
   part's own comment claimed the pseudo "sits under the act's own stacking
   context". It sat inside it, which is the opposite.

   THE CONTRACT, so that no part has to reason about this again:

     1. A fade is a SEPARATE ELEMENT, never a pseudo of the act. An act may be
        clipped, transformed or filtered — all three break a fixed descendant —
        and the fade must not be able to inherit that.
     2. Every fade is `pointer-events: none`. A gradient that swallowed a tap
        would take the row under it with it.
     3. Every fade sits on `--rd-z-fade`; every act that floats over one sits on
        `--rd-z-act`; and `--rd-z-fade < --rd-z-act`, here, once. No part
        writes a bare number for either.

   ⚠️ WHY 29 / 30 AND NOT 1 / 2. The numbers are the layer the product already
   put its floating page act on — `.rd-prom-fab` shipped at 30 — and they are
   chosen to sit BELOW the overlay band this sheet already uses for scrims,
   drawers, sheets and confirms (40, and 50/60 above that). An act that floats
   over a list must still go under the modal that covers the list, so the two
   tokens are deliberately not the top of anything.

   ⚠️ THE ATTRIBUTE SELECTORS ARE A BELT, NOT THE MECHANISM. `.rd-fade` is what
   a fade element is expected to carry, and each fade's own rule still names the
   token; the `[class$=…]` pair catches a fade named by convention whose author
   forgot the kit class. A suffix match only fires when the fade class is LAST
   in the attribute, which is why it cannot be the mechanism.
   --------------------------------------------------------------------------- */
:root {
  /* A scroll-edge fade: over the list, under everything that acts. */
  --rd-z-fade: 29;

  /* A page act floating over that fade. Under the 40+ overlay band. */
  --rd-z-act: 30;
}

.rd-fade,
[class$="-fade"],
[class$="__fade"] {
  pointer-events: none;
  z-index: var(--rd-z-fade);
}

.rd-act-layer {
  z-index: var(--rd-z-act);
}

/* ---------------------------------------------------------------------------
   1. Viewport layer — paints the ground, centres the capped column.
   --------------------------------------------------------------------------- */
.rd-shell {
  /* PROVISIONAL pending Claude Design (spec §7c): 448px, ONE token, on the
     shell root. 448 because it is the only width cap that already exists in
     our codebase (auth), so it is a decision rather than a new invention.
     Adjudication is a one-line edit HERE. No screen may set its own max width
     — the moment two exist, the flip stops being one line.

     ⚠️ THE TABLET FLIP DOES NOT LIFT THIS, AND THAT IS THE DELIBERATE ACT.
     Ruled 2026-08-17: the app enters tablet layout by viewport breakpoint
     (1024px — `TABLET_MIN_WIDTH_PX` in apps/web/src/device.rs, CSS twin in
     kit-tablet-shell.css). The obvious reading of that ruling is a media query
     here setting `--rd-shell-max-w: none` above the breakpoint. IT WOULD BE
     WRONG, and this comment exists so nobody has to re-derive why:

     `ShellLayout` wraps every shooter route in an `RdShell`, and forty-odd of
     those screens are UNMIGRATED — laid out against a 448px column, most of
     them before the cap existed. Lifting the cap by media query widens all of
     them at once, on the strength of a viewport width, with nothing reviewed.
     The screens do not get better; they get 1194px wide rows of phone markup.
     A 448px column centred on a tablet is plainly a phone rendering, which is
     honest; full-bleed phone markup on a tablet looks like a broken tablet
     screen.

     SO THIS TOKEN IS SCOPED, NOT CHANGED. It is the PHONE shell's cap, and it
     has exactly one value at every viewport width. What happens instead is the
     mechanism that already exists in this codebase: A SCREEN FAMILY LEAVES THE
     CAP BY LEAVING `RdShell`. The range-day family did precisely that on 14 Aug
     and main.rs states the reason in the same words this ruling uses — those
     routes are declared OUTSIDE `ShellLayout` because "the drawn frames carry
     no bottom tab bar and no 448px cap ... a tablet held in one hand, with
     their own bar", and because "no tab bar" must be "a fact about WHERE a
     route is declared ... never a runtime test that can be wrong."

     A migrating family mounts `RdTabletShell` (styles/kit-tablet-shell.css),
     which caps nothing and paints its own ground. That is one deliberate act
     per family, reviewed with the screens it moves, instead of one blanket act
     that moves forty screens nobody looked at.

     WHAT WAS CONSIDERED AND NOT TAKEN, so it is not re-proposed as new:
       - `@media (min-width: 1024px) { --rd-shell-max-w: none }` — the blanket
         lift above.
       - A `.rd-shell--wide` modifier under the query, set by no call site. The
         kit's rule is that a prop does not ship without a consumer, and a
         modifier nothing sets is a name reserved rather than a mechanism built
         (see kit/centered_card.rs, which declined a `flush` modifier on
         exactly this ground).
       - `.rd-shell__frame:has(> .rd-tshell) { max-width: none }`. `:has()`
         scoping was already retired once from this stylesheet — see adm0.css's
         scrollbar block, where asking the DOM what was mounted meant painting
         one way and flipping after mount. The same objection applies, and it
         would be speculative plumbing for a composition no screen performs.

     Pinned by `shell.rs`'s `ruling_no_media_query_lifts_the_phone_shells_cap`,
     which is a designer question and not a number to bump. */
  --rd-shell-max-w: 448px;

  /* PROVISIONAL pending Claude Design (spec §7a): 12px, plus the device
     safe-area inset where one exists. The 12 is the designer's — four framed
     documents pass top={12} to the same screens that get 58 (iOS) / 42
     (Android). The env() term is OURS: it is what makes an installed
     standalone PWA on a notched phone behave. In a normal browser tab env()
     resolves to 0 and the value is plainly 12. */
  --rd-shell-inset-top: calc(12px + env(safe-area-inset-top, 0px));

  /* Belt for a missing modifier class. The Rust `Gutter` enum is a required
     prop, so one of the four modifiers below is always present and always
     wins; this base value only decides what a hypothetical bug looks like,
     and standard is a safer wrong answer than flush. */
  --rd-shell-gutter: 18px;

  /* 100dvh, NEVER 100vh. On mobile browsers 100vh is the URL-bar-collapsed
     height, so a 100vh shell is taller than the visible viewport and its last
     flex child — the tab bar, i.e. the app's PRIMARY NAVIGATION — sits below
     the fold until you scroll (spec §7e). The kit's root is `height: 100%`,
     which needs an ancestor with a height; dvh is that height, and it is
     viewport-relative so it does not care how deeply this element is nested. */
  height: 100dvh;
  width: 100%;
  box-sizing: border-box;
  overflow: hidden;              /* the REGION scrolls, not the document */

  display: flex;
  justify-content: center;       /* centre the capped column */

  /* The ground is painted on the VIEWPORT, not on the capped column (spec
     §7c). Painting it on the column letterboxes the page in flat colour on any
     viewport wider than the cap — which is the whole reason these are two
     elements and not one.

     ⚠️ THE TEXTURE HAS ARRIVED, AND IT ARRIVED WHERE THIS NOTE SAID IT WOULD.
     What stood here read *"--rd-app-bg is currently a FLAT COLOUR … Do NOT add
     a url() here: the token is the one place the texture arrives, and it
     arrives with a Trunk copy-file, a PRECACHE_ASSETS entry and a CACHE_NAME
     bump together or not at all"*. Three of those four still hold — it is a
     token, it is the one place, and the copy-file is in `index.html`. The
     fourth was overtaken on size: the refusal was written against `BG.jpeg` at
     419,630 bytes and the vendored asset is the WebP q60 re-encode at 54,280,
     so it ships lazily instead of not at all. The token is `--rd-app-ground`
     and it is a SECOND token rather than a new value for this one. §0b above
     carries the whole argument; do not restate it here and do not author a
     url() in this rule. */
  background: var(--rd-app-ground);

  /* Body type role (substrate §2.1: tbody sets family only — no stretch, no
     weight — so descendants inherit the CSS defaults 100% / 400). */
  font-family: 'Archivo', sans-serif;
}

/* The four-step gutter scale (spec §2.2): 47 sites, four values, no others.
   Modelled as four classes rather than an arbitrary integer so a fifth value
   is a deliberate act rather than a typo. Set on the viewport layer; the
   custom property inherits down to the bar and the content region.
   ⚠️ These four class names are pinned to Gutter::px() by a unit test — the
   number in the name IS the number in the padding. */
.rd-shell--gutter-0  { --rd-shell-gutter: 0px; }
.rd-shell--gutter-16 { --rd-shell-gutter: 16px; }
.rd-shell--gutter-18 { --rd-shell-gutter: 18px; }
.rd-shell--gutter-20 { --rd-shell-gutter: 20px; }

/* ---------------------------------------------------------------------------
   2. Frame — the width-capped column. Transparent: the ground is above.
   --------------------------------------------------------------------------- */
.rd-shell__frame {
  flex: 1 1 auto;                /* grow toward the viewport width... */
  max-width: var(--rd-shell-max-w);  /* ...and stop here; the leftover free
                                        space is what justify-content centres */
  min-width: 0;                  /* a flex item defaults to min-width:auto and
                                    refuses to shrink below its content */
  height: 100%;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  overflow: hidden;

  /* No chamfer anywhere in this component (spec §4): tcham is not called in
     rd-train-kit.jsx:57-73. The shell is a full-bleed rectangle; the chamfer is
     carried by the controls inside it. */
}

/* ---------------------------------------------------------------------------
   3. Slot 1 — top inset. Air, not a bar.
   --------------------------------------------------------------------------- */
.rd-shell__inset {
  flex: none;
  height: var(--rd-shell-inset-top);
  /* No background of its own — the root's ground shows through, which is what
     makes this air rather than a status bar (spec §4). */
}

/* THE INSET BELONGS TO THE OUTERMOST SHELL, AND ONLY TO IT.
   `--rd-shell-inset-top` is `12px + env(safe-area-inset-top)`: air below the
   browser chrome and under the notch. Both are properties of THE VIEWPORT, and a
   viewport has exactly one top edge — so a shell rendered inside another shell's
   content region has no notch to clear and must contribute no air.

   It contributed it anyway. `ShellLayout` wraps every shooter route in an
   `RdShell` (gutter Flush, tab bar on), and a migrated page nests a second one to
   get its own gutter and back row — four ship today: home, squad_board,
   org_search, squad_score_entry. Measured on the shipped stylesheet at 390x844,
   content began at 24px against a single shell's 12px. In an installed PWA on a
   notched phone the whole token doubles, not just the 12.

   A STRUCTURAL RULE, NOT A PROP, and that is the point: the DOM already knows it
   is nested, so nothing has to be remembered at a call site. A prop would be an
   opt-out that each of the four — and every future migration — must not forget,
   which is the same shape as the class that resolved to nothing and shipped a
   page white. This fixes all four with no call-site edit.

   Three deep rather than two: the outer `.rd-shell` and the inner `.rd-shell` are
   both matched by the descendant pair, so `.rd-shell .rd-shell` already means
   "a nested shell". The third term is the slot itself. Specificity (0,3,0) beats
   the (0,1,0) above it, so this wins wherever it sits in the cascade. */
.rd-shell .rd-shell .rd-shell__inset { height: 0; }

/* THE OFFLINE STRIP BELONGS TO THE OUTERMOST SHELL FOR THE SAME REASON, AND
   THIS RULE IS ITS TWIN. Ruled 2026-09-01 (James, option 2): the app-wide
   offline banner moved out of `ConnectivityShell` and INTO the shell frames, so
   `RdShell` mounts `OfflineBanner` in slot 1b. Shells nest — the same four
   pages the rule above names — and a nested mount draws the strip a second
   time, one under the other, saying the same sentence twice.

   Structural, not a prop, for the reason stated at length above: the DOM knows
   it is nested and no call site has to remember. `display: none` rather than
   `height: 0` because this box has copy in it and a zero-height clip would
   still paint its border. Specificity (0,3,0) beats the strip's own (0,1,0) in
   `_legacy-base.css` wherever the two land in the cascade. */
.rd-shell .rd-shell .offline-banner { display: none; }

/* ---------------------------------------------------------------------------
   4. Slot 2 — banner. Full-bleed, normal flow, ABOVE the bar and the content.
   --------------------------------------------------------------------------- */
.rd-shell__banner {
  /* ⚠️ TRAP 2. `flex: none` is MANDATORY and the kit omits it:
     rd-train-kit.jsx:62 renders {banner} as a BARE flex child, which defaults
     to `flex: 0 1 auto` and is therefore shrinkable. Squeezed against a
     `flex: 1` content region it compresses and clips its own text. The sibling
     shell RDShell:47 gets this right; TShell does not. We port RDShell's
     treatment, not TShell's (spec §7b.2, §11.2). */
  flex: none;

  /* Full-bleed comes from the frame carrying no horizontal padding — the
     gutter lives on the bar and the content region only, so a banner spans
     edge to edge of the capped column regardless of the gutter (spec §1). */

  /* ⚠️ ROUTED THEN RESOLVED, AND NOT HERE. This slot still declares no air of
     its own — that half is unchanged, and the reasons stand: RDShell:48 has a
     fixed 14px, TShell has none, the kit is internally inconsistent (spec
     §10.2) and the shell's rule is "no vertical padding, the screen supplies
     its own" (§4).

     What resolved it is that the STRIP now supplies the air, at every mount
     rather than at this one: `.rd-banner` carries `--rd-banner-gap` (`p1.css`,
     THE SLOT RULING 2026-08-28), so a banner in this slot gets the same shelf a
     banner mid-pane gets, with nothing declared here and no `.rd-shell__banner`
     variant. That is what the ruling asked for — one slot at both device
     classes and on both sides of the product.

     This does NOT reach `.offline-banner`, which is a legacy class and not
     `.rd-banner`; it still carries its own `border-bottom` and its own air.
     ⚠️ Corrected 2026-09-01: nothing measures that strip any more. It used to
     be sized by `--rd-offline-banner-h` so the shells could subtract it; the
     token is deleted with the arrangement that needed it, and the strip is a
     `min-height: 63px` box in slot 1b of this very column. */
}

/* ---------------------------------------------------------------------------
   5. Slot 3 — back / title bar.
   --------------------------------------------------------------------------- */
.rd-shell__bar {
  flex: none;
  display: flex;
  align-items: center;
  height: 42px;                  /* fixed — see the overflow treatment below */
  padding: 0 var(--rd-shell-gutter);
  box-sizing: border-box;
}

.rd-shell__back {
  /* One control spanning chevron + gap + label, not a chevron with a caption
     beside it: a 9x15 glyph is far below a usable target outdoors with gloves
     on (spec §6). It takes the full 42px bar height as its hit area. */
  display: inline-flex;
  align-items: center;
  gap: 9px;
  height: 100%;

  /* Shrinkable, and min-width:0 is what lets the shrink actually reach the
     label so the ellipsis below can fire. */
  flex: 0 1 auto;
  min-width: 0;

  /* Leading bleed: the hit area reaches the screen edge so a thumb coming from
     the bezel lands on it, while the negative margin and the equal padding
     cancel out so the chevron does NOT move — the label stays on the same left
     edge as the first line of content. */
  margin-inline-start: calc(-1 * var(--rd-shell-gutter));
  padding-inline-start: var(--rd-shell-gutter);
  padding-inline-end: 0;

  /* It is a real <button>; strip the UA chrome without stripping the
     semantics. */
  background: none;
  border: 0;
  cursor: pointer;
  font: inherit;
  color: inherit;
  text-align: start;
  -webkit-appearance: none;
  appearance: none;
}

/* PROVISIONAL pending Claude Design (spec §5c). The kit is drawn in iOS frames
   and iOS has no hover, so there is no drawn source. Proposal: raise the label
   one step up the grey ladder, fg2 -> fg; the chevron does not change (it is
   already at full accent strength and has nowhere to go). NO background wash —
   the ground is a photograph and a translucent plate on it reads as a
   rendering artefact. Endorsed by the designer's own nav comment
   (rd-train-kit.jsx:36-40): outdoors, white beats the accent.
   (hover: hover) so a touch device does not get a stuck hover after a tap. */
@media (hover: hover) {
  .rd-shell__back:hover .rd-shell__label { color: var(--rd-fg); }
}

/* PROVISIONAL pending Claude Design (spec §5d) — the accessibility-critical
   one: the back control is frequently the only focusable element in the shell
   chrome, and an unstyled UA outline on a dark photographic ground is close to
   invisible. Square-cornered because border-radius is banned system-wide
   (substrate §1.5) and the chamfer is a bottom-right cut on CONTROLS, which
   this row is not. :focus-visible only, never :focus, so a pointer press
   leaves no ring.
   An outline is safe HERE specifically because the shell has no clip-path
   anywhere (§4) — on a chamfered element the clip would eat it. */
.rd-shell__back:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
  border-radius: 0;
}

.rd-shell__chevron {
  flex: none;                    /* the chevron never shrinks (spec §5e) */
  display: block;
  width: 9px;
  height: 15px;
  /* ⚠️ --rd-acc, NEVER --rd-volt. This is a STROKE, and per substrate §1.1 a
     stroke takes acc, which goes a deep warm brown (#5C5030) in light theme.
     Writing volt here yields an unreadable tan chevron on the light ground —
     the exact failure the acc/volt split exists to prevent. `stroke` is an
     inherited SVG property, so this reaches the path. (Was olive #3E4E10 under
     lime volt; export 28 moved both values and neither the rule nor the failure
     mode moved with them.) */
  stroke: var(--rd-acc);
}

.rd-shell__label {
  /* Shell-title type role (substrate §2.3): tdisp(700, 78, .16em) at 10px,
     colour fg2. tdisp is always uppercase — the transform is in the helper. */
  font-weight: 700;
  font-stretch: 78%;
  font-size: 10px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--rd-fg2);

  /* A DEFECT FIXED, NOT PORTED (spec §5e): rd-train-kit.jsx:66 declares no
     overflow, no text-overflow and no white-space while :64 fixes the bar at
     42px, so a wrapping label overflows a fixed-height box. The kit never
     exercises it because its longest label is "Modified Bill Drill"; WE do,
     because our labels are user-authored drill and course names of unbounded
     length. Single line, ellipsis, chevron unaffected. */
  min-width: 0;
  flex: 0 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   6. Slot 4 — content region. The only thing that scrolls.
   --------------------------------------------------------------------------- */
.rd-shell__content {
  flex: 1;

  /* ⚠️ TRAP 1, and the single most important declaration in this file. A flex
     child defaults to `min-height: auto`, which refuses to shrink below its
     content — so as soon as the content exceeds the fold the column grows and
     the tab bar walks off the bottom of the viewport. It looks correct on
     every short screen, which is exactly what makes it dangerous (spec §4,
     §11.1). This one line is what keeps the tab bar planted. */
  min-height: 0;

  /* Ours to supply, not the kit's (spec §7e). The kit sets overflow:hidden here
     because the device FRAME supplies the scroll (ios-frame.jsx:227). We have
     no frame. Scrolling the REGION rather than the document is what keeps the
     banner and the tab bar still while content moves under them. */
  overflow-y: auto;

  /* ⚠️ `auto`, NOT `hidden` — a deliberate deviation from spec §7e, measured.
     §7e says "keeping `overflow-x: hidden`", written when the only consumers
     were screens drawn to fit. The Shell migration puts 41 pages in here, most
     of them unmigrated and laid out before the 448 cap existed, and `hidden`
     does not prevent their overflow — it CONCEALS it. Driven and measured on
     `/training`: its header row is 470px inside a 416px box, and 54px of it was
     silently unreachable.

     Worse, `hidden` is a REGRESSION against what shipped: the old layout's
     `main` carried `overflow: auto`, so that row has always been scrollable on
     a phone, where 470px overflows regardless of any cap.

     And the rule's own purpose argues for `auto`. What §7e wants is that a
     migrated screen never overflows horizontally — a property to ENFORCE, which
     `hidden` cannot do and actively hides. `auto` makes the same defect visible
     the moment it appears, on a migrated screen or an unmigrated one.

     Nothing changes for a screen that fits: no overflow, no scrollbar. */
  overflow-x: auto;

  /* Horizontal only — NO top or bottom padding. Screens that want breathing
     room add their own (spec §4); baking it in here takes the choice away from
     all 47 of them. */
  padding: 0 var(--rd-shell-gutter);
  box-sizing: border-box;

  display: flex;
  flex-direction: column;
}

/* ---------------------------------------------------------------------------
   7. Slot 5 — tab bar. The shell reserves the slot; RdTabBar draws it.
   --------------------------------------------------------------------------- */
.rd-shell__nav {
  /* The shell's contract is that it reserves this slot and NEVER lets it
     shrink (spec §1, §11.2). The kit supplies flex:none for the tab bar itself
     (rd-train-kit.jsx:43) and RdTabBar is expected to as well — this is the
     shell honouring its own half of the contract regardless, so the invariant
     does not depend on a sibling component's internals.

     Full-bleed within the capped column: no gutter, by construction.
     Everything inside — glyphs, labels, the active rule, the nav background
     --rd-nav-bg — belongs to RdTabBar and is not styled here. */
  flex: none;
}

/* ---------------------------------------------------------------------------
   7a. Slot 5, the OTHER branch — the home-indicator clearance.

   `rd-train-kit.jsx:164` is a ternary, not an `&&`: a shell either ends with
   the tab bar or it ends with this spacer, and there is no third outcome. Air,
   like slot 1 — no ground of its own, no content, nothing tappable — so the
   last control on a nav-less screen stops 28px short of the swipe-away gesture.

   A FLOW SIBLING of the content region, exactly as the bar is, and for the same
   reason `04-tnav`'s bar gives: growing it shrinks the scroll region by
   construction, so there is no reserved-space constant anywhere that has to be
   edited to match. It also means a sheet absolutely positioned inside
   `.rd-shell__content` inherits the clearance without naming it — see §0.
   --------------------------------------------------------------------------- */
.rd-shell__home-clear {
  flex: none;
  height: var(--rd-home-clear);
}

/* THE CLEARANCE BELONGS TO THE OUTERMOST SHELL, AND ONLY TO IT — the same
   structural rule §3 states for the top inset, for the same reason. A viewport
   has one bottom edge and one home indicator. `ShellLayout` wraps every shooter
   route in an `RdShell`, and four pages nest a second one inside it (home,
   squad_board, org_search, squad_score_entry); without this, a nested nav-less
   shell would stack its 28px on top of whatever the outer shell already ends
   with — 28 + the outer bar's 18, or 56 where both are nav-less.

   Structural and not a prop, because the DOM already knows it is nested: a prop
   would be an opt-out every future migration must remember not to forget, which
   is the failure this rule exists to prevent in the first place. Three terms
   deep and (0,3,0) specificity, so it wins wherever it sits in the cascade. */
.rd-shell .rd-shell .rd-shell__home-clear { height: 0; }

/* ---------------------------------------------------------------------------
   8. THE SCROLLBAR — ONE TREATMENT, EVERY SCROLLER, BY CONSTRUCTION.

   RULED BY JAMES, 2026-08-30 (rehearsal, feature-batch-30), and the ruling
   REPLACES two earlier ones this block used to carry:

     - 2026-08-13 §3 "UA-default is the answer on the phone. The console wants
       a spec" — the shooter root declared `scrollbar-color: auto` HERE and the
       console styled `html[data-app="pro"]` in adm0.css, later re-stated on
       `.rd-pro-page` (adm0-chrome.css) when the document stopped scrolling.
     - the 11px WebKit width, pinned 2026-08-14.

   THE DEFECT THOSE TWO RULES PRODUCED. The standard pair (`scrollbar-color`,
   `scrollbar-width`) INHERITS, but the `::-webkit-scrollbar` pseudo-elements
   are matched PER ELEMENT and do not — and they were spelled on exactly two
   elements, `html[data-app="pro"]` and `.rd-pro-page`. Every other scroller
   in the console (a pushed record's own column, a pane, a picker list) had
   no pseudo-element rule, so on Chrome and Safari it painted the platform's
   default bar next to a themed one on the same screen — James saw it on the
   Pro shooter progress record (2026-08-30). Per-element re-statement is the
   mechanism that cannot scale: every new scroller is a new place to forget.

   THE RULE. The standard pair sits on `*` — `scrollbar-color` inherits but
   `scrollbar-width` does NOT (measured 2026-08-30: a nested scroller under a
   `:root { scrollbar-width: thin }` computes `auto`), so `:root` alone would
   theme the colour and leave the width platform-default on every inner
   scroller. `*` reaches every scroller in the document, both apps, with the
   two colours read from theme tokens — and it is gated to engines WITHOUT
   `::-webkit-scrollbar` (Firefox). Chrome 121+ supports both and PREFERS the
   standard pair wherever it is set, so an ungated `*` pair would have made the
   trio unreachable on the console's primary browser and shipped a platform
   thumb (rounded, platform metrics) instead of the drawn square one. Under the
   gate Chrome and Safari both take the trio; Firefox takes the pair. Chrome on
   touch gets neither and keeps its overlay bars
   (`--rd-scroll-thumb` / `--rd-scroll-track`, 0b above, dark AND light). The
   WebKit trio is UNSELECTED — a bare pseudo-element matches every element —
   so Chrome (< 121, and wherever it still prefers the legacy path) and Safari
   get the same bar without any element having to ask for it. 8px, transparent
   track, square thumb (this kit's corners are chamfered or square, never
   pill), `fg3` on hover, no buttons, no corner fill.

   ⚠️ TOUCH IS LEFT ALONE. The WebKit trio is gated on a fine pointer that can
   hover: on iOS and Android the bars are the platform's overlay bars, drawn
   over the content on demand, and a `::-webkit-scrollbar` width there would
   turn them into permanent gutters. The standard pair still inherits on touch
   (Chrome honours `thin`, WebKit ignores it) — that is the platform's call.

   ⚠️ NOTHING ELSE IN THE POOL MAY SPELL THESE PROPERTIES. A per-scroller
   `scrollbar-color` is the per-element mechanism coming back one class at a
   time; `pages::tests::the_scrollbar_is_one_rule_for_every_scroller` reads
   the assembled sheet and refuses a second spelling anywhere.
   --------------------------------------------------------------------------- */
@supports not selector(::-webkit-scrollbar) {
  * {
    scrollbar-width: thin;
    scrollbar-color: var(--rd-scroll-thumb) var(--rd-scroll-track);
  }
}
@media (hover: hover) and (pointer: fine) {
  ::-webkit-scrollbar { width: 8px; height: 8px; }
  ::-webkit-scrollbar-track { background: var(--rd-scroll-track); border: none; }
  ::-webkit-scrollbar-thumb {
    background: var(--rd-scroll-thumb);
    border: none;
    border-radius: 0;
  }
  ::-webkit-scrollbar-thumb:hover { background: var(--rd-fg3); }
  ::-webkit-scrollbar-button { display: none; width: 0; height: 0; }
  ::-webkit-scrollbar-corner { background: var(--rd-scroll-track); }
}
/* ==== _legacy-kit-components-and-screens · 9 kit components + 2 screens · owner: NOBODY ==== */
/* UNOWNED · DELETE-ONLY, on the same terms as `_legacy-kit-chamfer-button.css`,
   the other fragment of the same cut. No lane owns it, so no lane may add to it;
   any lane MAY delete what its own change stranded.

   The name is what was MEASURED here, not a glance: nine kit components —
   04-tab-bar, 05-label, 06-heading, 07-text, 08-chip, 09-stat, 10-numeric-field,
   11-theme-swatch, 12-rdicon — AND two screen sections, AU1-gate-screens and
   S1-home. It is not a tabs file; naming it one would have put a false name on
   an artifact that gets reviewed.

   "components" rather than "primitives" is deliberate. The pool's retired name
   must not survive as a substring of a longer filename either, or a grep for it
   keeps matching and the retirement cannot be demonstrated. See the banner of
   `_legacy-kit-chamfer-button.css`, which carries the one deliberate mention of
   the old name so that someone who greps it from an old commit lands somewhere
   that explains where it went.

   Each `/* ---- NN-name.css ---- *` marker below names the kit-css source file
   the block came from, which is what made the re-partition's seams measurable
   rather than judged. */

/* ---- 04-tab-bar.css ---- */
/* RdTabBar — the bottom tab bar + the NAVI icon set.
   Spec: docs/design/kit/04-tnav.md. Tokens: 00-substrate.md §1.1 (acc/volt),
   §1.3 (the table), §2.3 (the TNav label role).

   NO CHAMFER ANYWHERE IN THIS COMPONENT. rd-train-kit.jsx:43-49 calls tcham
   zero times (§1): the app's one piece of ownable geometry is deliberately
   absent from the chrome, which is what lets it mean something on content.
   That is also why the focus ring below may legally be an `outline` — nothing
   here is clipped, unlike on RdButton where clip-path eats both outline and
   box-shadow.

   Every colour is a token role. Read §1.1 before touching one: the rule and the
   glyph are STROKES, so they are --rd-acc and never --rd-volt. In dark theme
   the two are the same value and the error is invisible; in light theme
   --rd-acc is a deep warm brown and --rd-volt is tan, and a tan 2px rule on a
   white bar is unreadable. (The Training document's own prose says "a volt top
   rule and a volt glyph" — that is the slip §1.1 pre-empts; the kit code is
   right.) */

/* -- Bar ------------------------------------------------------------------ */

/* A FLOW SIBLING of the scroll region inside the shell's full-height flex
   column (`flex: none`), never `position: fixed` with a magic content padding.
   Today's shipped bar is fixed (style.css:124) against `main { pb-20 }` = 80px
   (style.css:41, shell.rs:62) for a bar that is neither 80px now nor 58px
   after — a constant that has already drifted once. A flow sibling shrinks the
   content region to fit, so there is no constant to maintain (§13 trap 5). */
.rd-tabbar {
  flex: none;
  display: grid;
  /* 1fr columns, NOT flex: equal by construction and equal regardless of label
     length, which is what stops "COMMUNITY" stealing width from "HOME".
     The 5 is the tab count, which is a module constant rather than a prop
     (§12); `tab_bar.rs` pins TABS.len() == 5 against this literal. */
  grid-template-columns: repeat(5, 1fr);
  border-top: 1px solid var(--rd-line);
  background: var(--rd-nav-bg);
  /* The kit's flat `padding-bottom: 6` is an iOS-frame value — the home
     indicator occupies it. A browser supplies no such furniture, so on web this
     must grow to the real inset or the bottom of a 52px touch target sits under
     the iOS Safari gesture bar (§8, trap 1). Our shipped bar already does this
     (style.css:132); dropping back to a flat 6px is a silent regression.

     ⚠️ The FLOOR is 18, not 6 (designer ruling, export 17 item 2): 6px left the
     bottom of the tab row inside the iOS home-indicator gesture zone, so a tap
     near the bottom edge of a cell was eaten by the system swipe. 18px is the
     gesture-clearance floor for the case where the browser reports NO inset —
     desktop, Android, iOS Safari in a tab. Where the platform does report one
     (iOS standalone/PWA), the real inset is larger and wins, which is why this
     stays a `max()` and never becomes a constant. `viewport-fit=cover` in
     `index.html` is what makes the `env()` half resolve at all; without it the
     inset reads 0 everywhere and only the floor is doing any work.

     The bar is a FLOW SIBLING (`flex: none`, see above), so growing it shrinks
     the scroll region by construction — there is no reserved-space constant
     anywhere that has to be edited to match. That is the whole point of the
     flow-sibling choice, and it is why this ruling is a one-line change. */
  padding-bottom: max(18px, env(safe-area-inset-bottom));
}

/* -- Cell ----------------------------------------------------------------- */

/* The whole cell is the link (§8, trap 2). A Link wrapping only the text would
   take a 52 x 80.4 target down to roughly 10 x 46. 52 x 80.4 CSS px clears iOS
   HIG 44, Material 48 and WCAG 2.5.5 on both axes — the 8px label is a
   legibility question, never a reachability one. */
.rd-tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 9px 0 7px;
  min-height: 52px;
  /* 52 is a BORDER-BOX total, not 52-plus-padding: every preview document sets
     `*{box-sizing:border-box}` (Range Day - Training.html:6), so the whole
     content stack is budgeted inside it — 2 border + 9 pad + 19 glyph + 5 gap +
     10 label line + 7 pad = 52 (§4.3). Restated locally rather than leaning on
     the global reset, because changing the glyph size or the gap changes the
     bar height and the arithmetic should be visible where it is spent. */
  box-sizing: border-box;
  /* The 2px is ALWAYS reserved, transparent when unselected, so selecting a tab
     changes only colour and never layout. "No border when unselected" shifts
     the whole bar 2px on every navigation. */
  border-top: 2px solid transparent;
  /* ⚠️ Pulls the cell up by exactly the bar's 1px hairline so the selected 2px
     rule sits ON the hairline rather than below it. Drop this and the selected
     tab gains a 1px grey line above its rule — a 3px double-stripe that reads
     as a rendering fault. No test catches it; it is the single most likely
     silent geometry loss in the component (§4.2, trap 1). */
  margin-top: -1px;
  text-decoration: none;
  /* Pressed is "unchanged": no ripple, no scale. Navigation completes in a
     frame and the arriving screen is the acknowledgement; a highlight flash
     would be a second, slower answer to a question already answered (§7). */
  -webkit-tap-highlight-color: transparent;
  /* THE GLYPH'S INK. Set on the cell so it inherits into the SVG as
     `currentColor` — see the Glyph note below for why it cannot be a rule on
     the paths. The label sets its own colour and is unaffected. */
  color: var(--rd-fg3);
  transition: color 120ms;
}

.rd-tab--on {
  border-top-color: var(--rd-acc);
  color: var(--rd-acc);
}

/* `.rd-tab--unrouted` USED TO BE HERE, and it did what it said it would:
   "Deletes itself when the Home screen ships." It shipped, and this is the
   deletion. It styled the non-link `span` a cell rendered when its tab had no
   route — one tab, Home, before `/home` existed. `00 (Mobile) Navigation
   Map.html:49`: "The tab bar is five, and all five now go somewhere … no node
   under any tab is amber now." `TabDef.route` is a plain `Route` now, so a
   non-link cell no longer has a way to exist. */

/* -- Glyph ---------------------------------------------------------------- */

/* The glyph is `RdIcon` (12-rdicon.md) at 19x19 from a 24-unit viewBox —
   `rd-train-kit.jsx:138` renders `<RDIcon … size={19}>`. Geometry is set as SVG
   attributes by the component, since it is geometry rather than colour.

   ⚠️ COLOUR IS SET ON THE CELL, NOT ON THE GLYPH, and that is what the icon
   rewrite bought. Every element in the set paints `currentColor`, so one
   inherited declaration colours a twelve-element mark; the previous rules
   selected `.rd-tab__glyph path { fill: none; stroke: … }`, which is only
   correct for an icon that is exactly one stroked path. The new set has solid
   fills, mixed weights and a ground-coloured knockout, and a blanket
   `fill: none` erases the solid half of every one of them.

   ⚠️ THE SELECTED `home` FILL IS GONE, deliberately. The rule was
   `fill={k[2] === 'home' && on ? c : 'none'}` (old rd-train-kit.jsx:48); the
   corpus no longer contains it, nor the second NAVI map it belonged to — `TNav`
   passes no fill flag and paint is a property of the asset. Do not reintroduce
   it as a CSS selector: it would now hit whole multi-element icons.

   The two declarations live in `.rd-tab` and `.rd-tab--on` above — one rule per
   selector, so there is no second block here to drift from the first. */

/* -- Label ---------------------------------------------------------------- */

/* tdisp(700, 78, .08em) at 8.5px (00-substrate §2.3, rd-train-kit.jsx:84).
   ✅ P3 ADJUDICATED: this was 8px and provisional. The kit now draws 8.5 —
   "Labels at 8.5pt with the icon, always" (Range Day - Training.html:150) — and
   substrate §P3 rules that every 7.5px and 8px site goes to 8.5 wherever it
   appears. The declaration below has shipped 8.5 since the token layer landed;
   only this comment lagged. It is still the joint-smallest type bound to any
   primitive AND the app's primary wayfinding text, so the two rules below stand.

   Two rules ride with it: an 8.5px label may never be set in a grey weaker than
   --rd-fg3, and text scaling must not be suppressed (no
   -webkit-text-size-adjust: none, no user-scalable=no) — at this size the
   browser's own scaling is the only remedy a user has. */
.rd-tab__label {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 8.5px;
  /* Budgeted at ~10px in the 52px stack (§4.3). */
  line-height: 10px;
  color: var(--rd-fg3);
  /* Not in the kit and required here: with no wrapping rule, a two-word label
     wraps at a narrow viewport, the cell exceeds 52px and the grid row grows
     the whole bar. No ellipsis either — a truncated wayfinding label is worse
     than a shorter word. If a label does not fit, the WORD changes, not the
     layout (§4.4). */
  white-space: nowrap;
  transition: color 120ms;
}

/* ⚠️⚠️ THE SELECTED LABEL IS --rd-fg, NOT --rd-acc. This is the single decision
   this component's designer comment exists to prevent, and it is the one that
   looks correct while being wrong (§13 trap 3). Verbatim, rd-train-kit.jsx:38-40:

     "The selected label is WHITE, not volt. Outdoor photos of the shipped app
      show lime losing contrast before white does, so volt carries the rule and
      the glyph — emphasis — while the word that tells you where you are stays
      white."

   Colouring it --rd-acc is INVISIBLE in dark theme, where --rd-acc and --rd-fg
   are both bright. It is caught only in light theme, or outdoors, which is the
   one place this app is actually used. */
.rd-tab--on .rd-tab__label {
  color: var(--rd-fg);
}

/* -- Hover: PROVISIONAL, pending Claude Design ---------------------------- */

/* iOS has no hover, so the kit specifies none (constraints-for-design.md §8.1).
   The step is BORROWED, not invented: the designer states the visible hierarchy
   comes from the step between fg2 and fg3 (rd-train-kit.jsx:6-10), so hover
   reuses the system's existing one-notch move instead of adding a sixth grey.
   Hover must NEVER reach --rd-acc or draw the rule — acc-plus-rule is the
   entire selected vocabulary, and a hover that borrows it makes "where am I"
   ambiguous, the precise failure the mandatory labels exist to prevent. */
@media (hover: hover) {
  .rd-tab:hover {
    color: var(--rd-fg2);
  }
  .rd-tab:hover .rd-tab__label {
    color: var(--rd-fg2);
  }
  /* The selected tab does not change on hover: it is already at maximum
     emphasis and any further move implies a state the system does not have.
     These two rules are not redundant — `.rd-tab:hover .rd-tab__label` and
     `.rd-tab--on .rd-tab__label` have EQUAL specificity (0,2,0), so the hover
     rule above would win on source order and repaint the selected label grey.
     The glyph pair below has the same shape for the same reason: `.rd-tab:hover`
     and `.rd-tab--on` are both (0,2,0). */
  .rd-tab--on:hover {
    color: var(--rd-acc);
  }
  .rd-tab--on:hover .rd-tab__label {
    color: var(--rd-fg);
  }
}

/* -- :focus-visible: PROVISIONAL, pending Claude Design ------------------- */

/* Full perimeter, not a top edge: the selected tab already carries an --rd-acc
   top rule, so a treatment that only thickens the top edge is indistinguishable
   from selection on the one tab a keyboard user is most likely to land on.
   `:focus-visible`, never `:focus` — a tap must not leave a ring. Square
   corners: §1.5 bans border-radius and this component has no chamfer.
   And it is never acceptable to remove the default outline without shipping
   this: an invisible focus ring on the app's primary navigation is a total
   keyboard loss, not a cosmetic one (§7). */
.rd-tab:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* -- States with no rule, stated rather than omitted ---------------------- */
/* Disabled: DOES NOT EXIST. A tab is always reachable.
   Loading / empty / error: DO NOT EXIST. The bar is a constant and never
   reflects data state.
   No tab selected (§6.4): not a class. It is the absence of .rd-tab--on on all
   five cells, which is already the default above — all rules transparent, all
   glyphs and labels --rd-fg3, no aria-current anywhere. The bar still renders;
   the user must keep their way out. */

/* ---- 05-label.css ---- */
/* RdLabel — the all-caps micro-label. Spec: docs/design/kit/05-tlab.md
   Component: apps/web/src/components/kit/label.rs

   MERGE NOTE — this block LAYERS ON the PR A type roles already in style.css:
   `.rd-lab` (:979) and `.rd-lab-acc` (:980). It deliberately does NOT redefine
   them: the type recipe (Archivo / uppercase / 700 / stretch 78% / .2em /
   --rd-fg3) stays there and stays the single definition of the role. Everything
   below is either an ADDITION the role class does not carry, or — where marked
   OVERRIDE — a deliberate replacement written as a COMPOUND selector, so it
   wins on specificity and merge order cannot decide it. */

/* ⚠️ PROVISIONAL P3 (substrate §0a). 8.5px is below Foundations' own floor —
   "Nothing on a phone screen goes below 13px" (Range Day - Foundations.html:116)
   — which the rendered kit breaks in 295 of 362 size declarations, with 239 in
   the 8-11.5px band. Shipping as drawn under substrate §0 (rendered kit practice
   beats Foundations prose) and routed loudly, not absorbed: the designer
   compensated with CONTRAST rather than size (rd-train-kit.jsx:6-10, "almost all
   secondary copy in this app is 8-11px, so the tertiary grey has to clear
   roughly 5:1"), so the size and the lifted --rd-fg3 are ONE decision. A surface
   that keeps 8.5px and reverts the grey gets the worst of both.

   The size is a token and not a literal for exactly one reason: this role has 40
   call sites, two of them inside the kit (so every numeric field caption and
   every stat key in the app), and there is no partial adoption available. If
   Claude Design rules for the floor, the fix must be one line here. */
:root { --rd-lab-size: 8.5px; }

.rd-label {
  /* The kit leaves line-height UNSET (05-tlab.md §3.2) — in the preview harness
     it inherits from an ancestor, which is fine in a preview and unsafe here,
     where the ancestor is not fixed. Every one of the 40 corpus strings is one
     line at phone width (the longest is 26 characters), so the corpus offers NO
     evidence for what a wrapped label should do. 1.2 keeps a two-line accident
     readable; the VALUE is an assumption, unverified, and wrap-vs-truncate is
     routed to the designer. Leaving it unset is the one option that guarantees a
     surprise later. */
  line-height: 1.2;
  /* Owns no vertical rhythm: 7 of 40 sites wrap it in a spacing container and
     the other 33 sit flush against a neighbour that carries the gap. Explicit
     because the element is the caller's choice (§7) and h2-h6, <p> and <dt> all
     arrive with a UA margin. */
  margin: 0;
}

/* OVERRIDE: swaps .rd-lab's literal 8.5px for the token above. Identical pixel
   today; the point is the flip point. Compound selector so this holds wherever
   the merge lands it. */
.rd-label.rd-lab { font-size: var(--rd-lab-size); }

/* .rd-label must NEVER declare `color`. Emphasis is carried by .rd-lab-acc
   (style.css:980, --rd-acc), which beats .rd-lab's --rd-fg3 by source order in
   the existing file; a `color` here would sit later still and silently win over
   both. The two-value emphasis set is enforced in Rust (`Emphasis`), and it is
   --rd-acc and never --rd-volt: volt is the accent as a FILL, acc is the accent
   as text or stroke (substrate §1.1). Volt here reads fine in dark and is
   unreadable in light (#A89968 on #F1F1EC). */

/* States (05-tlab.md §4). The kit draws exactly two — default and accent — and
   both are above. There is no disabled, loading, error or empty state: absence
   IS the empty state and it lives at the call site (`{label && <TLab …>}`,
   rd-train-kit.jsx:102), not in the component.

   hover / :focus-visible — NOT APPLICABLE, stated rather than omitted so a
   reviewer knows it was considered. RdLabel is non-interactive text: no hover,
   no focus ring, no tab stop, and it is never itself the interactive element.
   The corpus cannot rule on this — onClick, cursor:, role=, tabIndex and href=
   are ZERO across all seventeen .jsx files, so nothing there is interactive,
   including its buttons. PROVISIONAL pending Claude Design, for the two cases we
   will hit: when the label names a form control the CONTROL shows focus (the
   ring belongs to the field, see 10-tnum.md §4.2); when the label sits inside a
   tappable card the CARD takes hover and focus as one unit. The label never
   lights up independently and never becomes its own tab stop. If a design ever
   needs a tappable micro-label, that is a button wearing the label voice — a
   different primitive, not a prop added here. No rules are emitted for these
   deliberately: an absent selector is how the next author learns this was a
   decision rather than an omission. */

/* Two hazards worth carrying, neither of which is a rule to add here:

   1. letter-spacing: .2em applies AFTER the last character too, so a
      right-aligned or flex-end label sits ~1.7px short of its box edge. No
      corpus site right-aligns one, so it does not bite yet. Compensate with a
      negative margin at THAT call site; never by trimming the token.
   2. font-stretch: 78% needs the variable font's `wdth` axis. The shipped subset
      is 62-100 (substrate §2.2, PR #468), so 78 is comfortably inside it — but
      if the subset is ever narrowed, this role breaks SILENTLY by snapping to
      the nearest available width rather than erroring. */

/* ---- 06-heading.css ---- */
/* RdHeading — the display heading. Spec: docs/design/kit/06-th1.md
   Component: apps/web/src/components/kit/heading.rs

   MERGE NOTE — this block LAYERS ON `.rd-h1` (style.css:977, PR A), which stays
   the single definition of the type role: Archivo / uppercase / 900 /
   stretch 62% / letter-spacing 0 / line-height .92 / --rd-fg. Everything below
   is an ADDITION, except the one marked OVERRIDE, which is written as a COMPOUND
   selector so it wins on specificity and merge order cannot decide it.

   Two declarations in .rd-h1 are load-bearing and must stay there:

     line-height: .92   Sub-1 on purpose and part of the ROLE, not a tunable
                        (substrate §2.3). At weight 900 / width 62% the caps are
                        tall and flat-topped, so the default `normal` opens a
                        visible gutter between the two lines of a two-line
                        heading; .92 closes it so a stacked heading reads as one
                        mass. The sibling system reached .9 for the same job at a
                        larger default size (rd-auth-screens.jsx:88).
     letter-spacing: 0  Written EXPLICITLY, not omitted. Omitting it lets an
                        ancestor's tracking inherit — which is exactly the bug
                        .rd-lab's .2em would cause on a nested heading. */

.rd-heading {
  /* Owns no vertical rhythm: 4 of 33 sites wrap it in a spacing container (all
     four a marginTop separating the heading from an eyebrow above it) and the
     other 29 sit flush, with the FOLLOWING element carrying the gap. Explicit
     because the element is the caller's choice (05-tlab.md §7) and every
     h1-h6 arrives with a UA margin. */
  margin: 0;
}

/* OVERRIDE, deliberate: .rd-h1 hard-codes font-size: 30px, and that 30 is the
   kit's DEAD default — `size` is passed at 33 of 33 corpus sites
   (grep -ho '<TH1[^>]*>' *.jsx | grep -vc 'size=' -> 0), and even the two sites
   that render at 30 pass it explicitly. A default nobody has taken is a default
   nobody has checked, so the Rust prop is required and every RdHeading sets
   --rd-h1-size inline.

   No var() fallback, deliberately: the only candidate is that same dead 30. If
   the property is ever missing the declaration becomes invalid-at-computed-value
   -time and the size inherits, which is visibly wrong — the failure that gets
   noticed, rather than 30px everywhere, which is the failure that does not.

   Twelve distinct sizes over 33 sites (17-30, all integers) is why this is a
   custom property and not twelve modifier classes. It is a fit control, not a
   hierarchy control: size falls as string length rises, and one screen carries a
   23px title and a 22px empty-state heading a full outline level apart
   (rd-train-shell.jsx:83-90). Never derive the element from it. */
.rd-heading.rd-h1 { font-size: var(--rd-h1-size); }

/* One line per block. 10 of 33 corpus headings are broken by hand, and none of
   the ten breaks where the text would naturally wrap — `Pick your look` at 30px
   fits one line and is broken anyway (rd-train-theme.jsx:35) to make a two-line
   block whose .92 leading reads as one mass. The break is a typographic decision
   made per string; auto-wrapping reproduces none of them.

   The component emits these spans from a sequence of line strings the caller
   passes as DATA. The caller never authors markup: this role renders
   user-authored drill and course names at 8 of 33 sites ({BILL.name},
   rd-train-run.jsx:76), so accepting "<br />" in a string would put a raw-HTML
   sink on the heading — not a trade worth two lines of copy.

   <span>, not <div>: a heading's content model is phrasing content. */
.rd-heading__line { display: block; }

/* ⚠️ NEVER put `overflow: hidden` on .rd-heading, or on any box sized to one.
   At size=30 the .92 line box is 27.6px — SHORTER THAN THE TYPE — so descenders
   and accented capitals overflow it by construction, and clipping eats them.
   This is not theoretical: the corpus's one ellipsised display heading had to
   LEAVE THE ROLE to do it. The site that established this was rd-profile.jsx:95 [gone],
   an ad-hoc tdisp(900, 66, '.01em') relaxed to lineHeight: .98 — the leading was
   relaxed specifically in order to truncate. ⚠️ That line no longer exists: the
   08-04 refresh split rd-profile.jsx (now 68 lines) and collapsed 66 to 62. The
   surviving ellipsised display heading is rd-profile-collapsed.jsx:25, at
   tdisp(900, 62, '.01em') / 18px / lineHeight 1 — so the ESCAPE still exists and
   the reasoning holds, but .rd-h1-alt's own size and leading are now unmoored
   from any live site and are queued for the re-diff batch. That escape lives as
   `.rd-h1-alt` (:978) and is deliberately NOT a prop on this component: if you
   need to ellipsise, you are not in this role. Long user-authored headings are
   an open question routed to the designer (06-th1.md §10 item 1). */

/* States (06-th1.md §4). The kit draws exactly one: --rd-fg at the size passed.
   No disabled, no loading, no error, no empty — and the absence of a state
   colour is DELIBERATE, checked against two screens that had one in hand and
   spent it elsewhere. ScreenGuard (rd-train-run.jsx:341-357) has two variants
   carrying t.warn and t.danger, puts both on a 42px marker block ABOVE the
   heading, and leaves the heading on --rd-fg in both. State never recolours the
   heading; the furniture around it carries the state. There is no colour
   modifier here and no colour prop in Rust — a screen that needs a red heading
   is a finding to route, not a class to add.

   One near-state that is NOT a heading state: rd-authoring-create.jsx:344 dims a
   whole screen with opacity: .3 behind an open sheet. That belongs to whatever
   draws the sheet.

   hover / :focus-visible — NOT APPLICABLE, stated rather than omitted. RdHeading
   is non-interactive text: no hover, no focus ring, no tab stop. The corpus
   cannot rule on it (onClick / cursor: / role= / tabIndex / href= are zero
   across all seventeen .jsx files, so nothing there is interactive, including
   its buttons). PROVISIONAL pending Claude Design: when a heading sits inside a
   control — a navigable list row, a tappable card — the CONTROL takes hover and
   focus as one unit. The heading never lights up independently and never becomes
   its own tab stop. If a heading itself must be actionable, that is a control
   wearing the display voice, which is a different primitive. */

/* font-stretch: 62% is the LOWEST shipped value of the variable font's `wdth`
   axis (subset 62-100, substrate §2.2, PR #468). If the subset is ever narrowed,
   this role breaks first and it breaks SILENTLY — the browser snaps to the
   nearest available width rather than erroring. Worth a look in the first
   release build, and note the standing hazard that Dioxus rsx! differs debug vs
   release, so verify under `trunk --release`. */

/* ---- 07-text.css ---- */
/* RdText — the body paragraph. Spec: docs/design/kit/07-tp.md
   Component: apps/web/src/components/kit/text.rs

   MERGE NOTE — this block LAYERS ON `.rd-p` (style.css:983, PR A), which stays
   the single definition of the type role: Archivo / 13px / line-height 1.5 /
   --rd-fg2 / text-wrap: pretty / margin: 0. Everything below is an ADDITION,
   except the one marked OVERRIDE, which is written as a COMPOUND selector so it
   wins on specificity and merge order cannot decide it.

   Three declarations already in .rd-p are load-bearing:

     margin: 0        The highest-value line in this role. See below.
     line-height: 1.5 Hard-coded and part of the ROLE (substrate §2.3), not a
                      tunable and not exposed as a prop. It is the exact inverse
                      of the heading's .92: the display voice closes up, the
                      reading voice opens out, and that contrast does as much
                      work as the size difference.
     text-wrap: pretty  Progressive enhancement — see the note at the foot. */

/* ⚠️ PROVISIONAL P3 (substrate §0a), and this role is where the floor broke.
   Foundations says "Nothing on a phone screen goes below 13px"
   (Range Day - Foundations.html:116). TP's default of 13 is the ONLY value in
   this whole wave that satisfies that sentence — and 13 of 18 call sites (72%)
   go below it. The role was specified at the floor and then walked under it on
   nearly every real screen.

   Tokenised so a ruling is one line. Worth recording: RdText is the CHEAPEST
   place to honour a partial reinstatement — the whole range is 11.5-13, so
   clamping to 13 costs at most 1.5px of reflow per site, against RdLabel's
   8.5 -> 13. If the floor comes back partially, start here. */
:root { --rd-body-size: 13px; }

.rd-text {
  /* `tbody` is font-family and NOTHING else (rd-train-kit.jsx:18) — no stretch,
     no transform, no tracking, and no weight. In the preview harness the weight
     inherits from <body>; in our stylesheet the ancestor is not fixed, and this
     role's most likely neighbours are weight 700-900 display elements, so
     inheritance will find them. 400 is measured, not assumed: Foundations.html:100
     is the Archivo specimen for reading copy and declares font-weight:400 with
     colour fg2 — this role's colour at a body size. That the value belongs in
     the class rather than being left to inherit is OUR inference, labelled. */
  font-weight: 400;
  /* Restated even though .rd-p already carries it, because it is the single
     highest-value declaration in this component and it must not depend on
     another rule keeping it. ALL 18 corpus sites wrap this paragraph in an
     explicit spacing container — 11 on the same line, the other 7 on the line
     above, checked individually because a same-line grep undercounts multi-line
     JSX. There is no counterexample. The corpus runs under a preview-harness
     reset (Range Day - Training.html:6, `*{…margin:0;padding:0}`) that our
     hand-curated stylesheet does not provide, so a paragraph that inherits the
     UA <p> margin is wrong on all 18 surfaces at once — and wrong in a way that
     looks plausible. */
  margin: 0;
}

/* OVERRIDE: swaps .rd-p's literal 13px for the token, and lets a call site step
   DOWN via --rd-p-size. Two names on purpose: --rd-body-size is the role's own
   size (the P3 flip point, set once at :root), --rd-p-size is the per-instance
   concession (set inline, four values over 18 sites — a custom property rather
   than four modifier classes, and a number, never a colour).

   The component emits --rd-p-size ONLY for a genuine override, and normalises an
   explicit 13 back to "no override", so the default always reaches the render
   through --rd-body-size. Inlining 13px at the five default sites would leave
   the token inert and a P3 ruling silently ineffective. */
.rd-text.rd-p { font-size: var(--rd-p-size, var(--rd-body-size)); }

/* The default is the MAXIMUM, not a midpoint. Four values are passed
   (11.5 x1, 12 x6, 12.5 x6, 13 x5) and every explicit one goes DOWN; there is no
   site above 13. So the size prop is a density concession, not a size in the
   ordinary sense. The five sites that take the default are not scattered: all
   five are the paragraph of a full-screen, vertically-centred outcome or empty
   state, each verified individually inside a justifyContent:'center' container
   (rd-train-shell.jsx:91,154; rd-train-run.jsx:350; rd-train-stress.jsx:166;
   rd-authoring-create.jsx:390). 5 of 5, no exception:

     When the paragraph IS the screen, it is 13. When it competes for space with
     controls, lists or a diagram, it steps down. */

/* text-wrap: pretty is a PROGRESSIVE ENHANCEMENT, and it stays in .rd-p
   unguarded — no @supports, no fallback declaration. The mechanism, not a guess:
   a declaration whose value is not valid for its property is invalid and dropped
   AT PARSE TIME. It does not throw, does not invalidate its neighbours, and does
   not invalidate the rule; the element then takes the initial values (`wrap`,
   `auto`), i.e. ordinary greedy line-breaking. So the failure mode is: the
   paragraph still wraps, at the same width, in the same box, with the same
   leading — some paragraphs just get a shorter last line. Nothing reflows out of
   its container and nothing is hidden. Verified safe for us specifically: no
   corpus site puts this role in a height-constrained box, no ancestor sets
   overflow: hidden, and none has a fixed pixel height.

     Rule: nothing in any layout may depend on it, and a differing line count
     between engines is not a defect.

   NOT claimed here: which engines and versions support it. That matrix moves,
   and stating versions from memory is exactly the unlabelled assumption this
   spec set forbids — check it against our real browser-support floor when it
   matters (07-tp.md §10 item 4). The ruling above holds either way, which is the
   point of specifying it as an enhancement.

   Do NOT substitute `balance`: it is a different value for a different job
   (short blocks — headings, pull quotes) and browsers cap the line count it
   applies to. The designer wrote `pretty` three times across two systems. And if
   a fallback is ever wanted it is not CSS — manual <wbr> or non-breaking spaces
   would put copy decisions in markup, which the element-mapping rule and the
   heading's line-break rule both forbid. The correct fallback is no fallback. */

/* States (07-tp.md §5). The kit draws exactly one: --rd-fg2 at the size passed.
   No disabled, no loading, no error, no empty — when there is nothing to say the
   call site renders no paragraph at all. There is no colour modifier and no
   colour prop: 18 of 18 sites are --rd-fg2, the only one of the three roles in
   this wave with no escape hatch at all. The three-step hierarchy heading ->
   paragraph -> meta maps exactly onto --rd-fg -> --rd-fg2 -> --rd-fg3, and this
   is the middle step that makes "the hierarchy comes from the step between them"
   (rd-train-kit.jsx:6-10) true.

   One near-state that is NOT a state of this role: rd-authoring-create.jsx:344 dims a
   whole screen with opacity: .3 behind an open sheet. That is the backdrop
   treatment on the container.

   hover / :focus-visible — NOT APPLICABLE, stated rather than omitted. RdText is
   non-interactive text: no hover, no focus ring, no tab stop. The case we will
   actually hit is not a tappable paragraph but A LINK INSIDE ONE — body copy is
   where inline links live, and two corpus strings are already reaching for one
   (rd-train-course.jsx:100, rd-authoring-create.jsx:357). PROVISIONAL pending Claude
   Design: this role gains no hover or focus styling ever; an inline link inside
   it is a separate element with its own hover and :focus-visible treatment, and
   it must be distinguishable from surrounding body copy by something other than
   colour alone. The link treatment itself is NOT specified here — the corpus
   contains no inline link (grep -c '<a ' *.jsx -> 0) and Foundations declares
   none. Note the constraint that puts on the API: the Rust prop is a plain
   String and so cannot contain a link. Deliberate, and routed. */

/* No measure cap, and that is an open question rather than a decision. The
   designer's own desktop body style caps at 70ch (Range Day - Training.html:12,
   `.lede`) and this role sets no max-width. At phone width the viewport IS the
   measure, so the corpus cannot tell us whether the omission is intent. It
   becomes real at the first tablet or desktop breakpoint (07-tp.md §10 item 6). */

/* ---- 08-chip.css ---- */
/* RdChipSelector / RdChipCommand / RdChipStatus — spec: docs/design/kit/08-tchip.md
   Three components share exactly ONE thing: the painted leaf below. The roles are
   not shared and are not a modifier — see chip.rs for why. */

/* --------------------------------------------------------------------------
   The painted leaf. Numbers from tmp/design/snapshot/rd-train-kit.jsx:123.

   SINGLE LAYER, deliberately. A chip has no stroke, so the two-layer chamfer
   pattern (substrate §3.3) does NOT apply and the inner-cut rule is not in play. With
   the filled TBtn it is one of only two primitives in the system drawn as a
   solid fill with no border — do not copy RdChamferBox in here.

   Chamfer 7, bottom-right only (substrate §3.1) — the smallest cut bound to
   anything in the kit. Written literally rather than as --rd-box-r because a
   chip's cut is fixed at every one of its 20 corpus call sites; there is no
   scale to parameterise.

   Type role, substrate §2.3: 700 / stretch 78 / 9.5px, uppercase from the
   display helper. The 9.5px sits inside contested provisional §P3 (Foundations:
   "nothing on a phone screen goes below 13px"); kit practice ships under
   substrate §0. Its compensation is CONTRAST, and the contrast MUST travel with
   the size — --rd-fg2 on --rd-chip measures 7.75:1 dark / 8.04:1 light, and a
   later screen that keeps 9.5px but reverts to a dimmer grey loses it. Do not
   soften the label colour here. */
.rd-chip {
  display: inline-block;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  font-size: 9.5px;
  /* A long label makes a long chip. It does not truncate and it does not wrap;
     the ROW wraps or scrolls instead (§4.3). rd-train-kit.jsx:123. */
  white-space: nowrap;
  /* --rd-chip-bg is the chip's own fill, restated as a variable so the focus
     ring's hole can read it without knowing which state the chip is in. */
  --rd-chip-bg: var(--rd-chip);
  background: var(--rd-chip-bg);
  color: var(--rd-fg2);
  /* The inset focus ring is positioned against this box. */
  position: relative;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%);
}

/* Vertical padding is IDENTICAL across both sizes. Billboard is wider and
   looser, never taller: 8→11px horizontal, .05→.1em tracking. */
.rd-chip--compact   { padding: 7px 8px;  letter-spacing: .05em; }
.rd-chip--billboard { padding: 7px 11px; letter-spacing: .1em; }

/* Active — a selector's lit chip, or a status chip's primary attribute.

   --rd-volt, NEVER --rd-acc. The active chip is a FILL, so it takes the fill
   token and is the same tan in both themes; --rd-acc is the accent as text or
   stroke and goes a deep warm brown in light. Writing --rd-acc here is correct
   in dark theme BY COINCIDENCE — the two tokens hold the same #A89968 there —
   and renders brown-on-tan in light. It is the single most likely token error
   in this component and no dark-theme screenshot can catch it.

   Label is --rd-on-volt (black in both themes), 18.05:1 on volt. Never white on
   volt (substrate §1.5).

   ⚠ KNOWN DEFECT, NOT COMPENSATED HERE. In light theme --rd-volt is 1.03:1
   against --rd-bg: the fill is invisible as a shape, the 7px chamfer — the
   system's one piece of ownable geometry — cannot be seen at all, and the whole
   state reads as "the label went black". That is designer question 12
   (08-tchip.md §3.2 / §9.4) and it is awaiting a ruling. Implement the tokens as
   measured; do NOT add a border or a shadow to rescue the silhouette. */
/* ADJUDICATED — designer answer #12. The selected state is a FILL FLIP, not a
   label-colour flip, so the chamfer reads and the contrast is structural.

   The defect: in light theme a volt-filled chip measured 1.03:1 against the page
   ground — the shape was invisible and "selected" survived only as a change of
   label colour. His fix, verbatim in intent: "an inverted chip is unmistakable
   at arm's length in daylight, which is the actual use case. Do not solve this
   by fixing the wash value — a wash cannot carry a selected state on paper."

   Carried as token VALUES per architect ruling 2, so the component is
   structurally identical in both themes. Dark is unchanged. */
.rd-chip--on {
  --rd-chip-bg: var(--rd-chip-on-fill);
  background: var(--rd-chip-bg);
  color: var(--rd-chip-on-label);
}

/* --------------------------------------------------------------------------
   The hit area — roles A and B only. Status chips get none; they are not
   tappable.

   ARCHITECT RULING (spec §4.1, reported not asked): interactive chips get a
   44×44 minimum hit area with the PAINTED BOX UNCHANGED. A compact chip
   measures ≈21×26 CSS px — less than a quarter of the 44pt minimum the frame
   itself declares (ios-frame.jsx:63, `height: 44, minWidth: 44`) — and this app
   is used outdoors, in gloves, on a live line. One of the smallest chips in the
   corpus is the control chosen specifically to replace typing.

   Expansion is on the PRESSABLE ELEMENT, not on padding: padding would change
   the drawn design, and the drawn design is what was approved. The chip is
   inset-painted at its natural size inside this box and stays 21×26 to the eye.

   ⚠ One consequence to look at on the kitchen sink: min-width lays the row out
   on a 44px pitch, so two SHORT chips ("1", "Fit") sit further apart than the
   mock draws them, while long labels ("Assessments") are unaffected because the
   min never fires. The alternative — an absolutely-positioned ::before that
   expands the hit area without touching layout — keeps the row rhythm exactly
   but makes adjacent 44px targets OVERLAP in a 27px-pitch row, so the later
   sibling wins the last ~5px of its neighbour's painted glyph. A mis-tap on a
   visible control is worse than a wider gap, hence this choice; it is a
   trade-off, not a free lunch. */
.rd-chip-hit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* PROVISIONAL — pending Claude Design (spec §8.1). The kit specifies no hover;
   the iOS frames have none by construction, so this is a pointer-only concern
   and ScreenShelfDesktop (rd-editor-shelf.jsx:299) is the one corpus screen
   where it applies.

   The LABEL lifts, not the fill: --rd-chip has no neighbouring step in the token
   set and its nearest candidate (--rd-surf2) is 1.16:1 away in dark theme — a
   hover nobody would see. The label already has a one-step ladder the designer
   built (fg2 → fg).

   An already-lit chip does not change: it is volt, there is no brighter state,
   and the volt-scarcity rule forbids inventing one.

   No pressed treatment, deliberately: selection is instantaneous and local, so
   the result IS the feedback (§8.1). */
@media (hover: hover) {
  .rd-chip-hit:hover .rd-chip:not(.rd-chip--on) { color: var(--rd-fg); }
}

/* PROVISIONAL, AND GENUINELY UNSOLVED — spec §8.2, needs a rendered look before
   merge.

   The problem: clip-path clips BOTH `outline` and `box-shadow`, so neither can
   draw a ring that follows the 7px cut, and a chip is a single-layer element so
   the two-layer trick that saves RdChamferBox is not available without changing
   its anatomy. border-radius is banned system-wide (substrate §1.5), so a
   rounded ring is not a fallback.

   ADJUDICATED — the designer's answer: "for single-layer chips, use an inset
   ring: a second clip-path'ed element inside the chip at chamfer `n`, drawn only
   on focus. It costs one node on a primitive that already has one."

   This replaces a `filter: drop-shadow()` candidate. That one DID trace the
   chamfered silhouette — the filter's input is the already-clipped chip, so its
   alpha channel is the cut — but drop-shadow has no spread, so two stacked
   passes read as a GLOW rather than the crisp stroke asked for. The rendered
   evidence decided against it, which is what the ruling asked for.

   Geometry, by the same rule as every other cut (substrate §3.3,
   n = r - w(2 - SQRT2)):
     chip cut          7.0
     ring outer cut    7.0 - 2.0(2-SQRT2) = 5.83   (inset 2px inside the chip)
     ring hole cut     5.83 - 1.5(2-SQRT2) = 4.95   (ring is 1.5px)
   The hole takes the chip's OWN fill via --rd-chip-bg, so the ring reads as a
   stroke on every chip state without knowing which state it is in.

   --rd-acc, not --rd-volt: it is a stroke (substrate §1.1). That is also what
   keeps it legible on a SELECTED chip, where a volt ring on a volt fill would be
   invisible.

   :focus-visible only, never :focus — a plain :focus ring on a chip tapped with
   a thumb leaves a stuck outline on a touch screen. The UA outline is removed
   only inside this rule, and only because the rule replaces it: the UA draws a
   rectangle around the 44px hit box, which is both the wrong shape and the wrong
   size. */
.rd-chip-hit:focus-visible { outline: none; }

/* Present but invisible at rest, so focus costs no layout and no repaint of the
   chip itself — only this node's opacity changes. */
.rd-chip__ring {
  position: absolute;
  inset: 2px;
  background: var(--rd-acc);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 5.83px), calc(100% - 5.83px) 100%, 0 100%);
  opacity: 0;
  pointer-events: none;
}
.rd-chip__ring::after {
  content: "";
  position: absolute;
  inset: 1.5px;
  background: var(--rd-chip-bg);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 4.95px), calc(100% - 4.95px) 100%, 0 100%);
}
.rd-chip-hit:focus-visible .rd-chip__ring { opacity: 1; }
/* The ring is absolutely positioned and its hole is OPAQUE (it carries the
   chip's own fill so the ring reads as a stroke on any state). Without this the
   hole paints straight over the label — caught in the 10x render, not by any
   checker: every class resolved and every test passed with the text invisible.
   `position: relative` alone is NOT enough: positioned siblings paint in DOM
   order and the ring comes second, so the label needs an explicit z-index. */
.rd-chip__label { position: relative; z-index: 1; }

/* ADJUDICATED — designer follow-up ①. The ATTRIBUTE chip: the lead chip of a
   metadata row, a bare category literal. It used to take the selected fill —
   a hairline instead, because "a category literal was never a selection and
   should not have looked like one."

   ⚠️ REBUILT 2026-08-04. This was a single clipped span with
   `box-shadow: inset 0 0 0 1px`, justified by the claim that "an inset shadow is
   clipped WITH the shape, so it traces the chamfer". THAT CLAIM IS FALSE, and
   this stylesheet already said so 70 lines above, in the focus-ring block:
   "clip-path clips BOTH `outline` and `box-shadow`, so neither can draw a ring
   that follows the 7px cut."

   An inset shadow is painted against the BORDER BOX — a rectangle. `clip-path`
   then cuts the painted result, so along the diagonal there is no ring at all:
   the hairline was absent on the one edge it exists to draw, and present on the
   four that a plain rectangle would have given for free.

   Rendered proof, geometry scaled 8x so a 1px stroke reads as an 8px band:
   tmp/design/kit-renders/rediff-01-attr-chip-diagonal-comparison.png.

   It is now the two-layer construction every other stroked box in the kit uses
   (RdChamferBox, stroke Muted, fill Hollow, r=7, w=1) — "a chamfered hairline is
   always two layers, and the inner fill must be opaque." The face below is the
   INNER layer; the stroke is the outer layer's background.

   Process note worth keeping: the old block was labelled "PROVISIONAL, AND
   GENUINELY UNSOLVED ... needs a rendered look before merge", and it merged
   without one (#477). The label was accurate; it just did not stop anything. */
.rd-chip-attr-face {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  font-size: 9.5px;
  white-space: nowrap;
  color: var(--rd-fg);
}
/* The kit gives the attribute chip its own padding scale rather than reusing
   the plain chip's (rd-train-kit.jsx:182). The 1px stroke sits OUTSIDE this
   face, so the painted box lands 2px shy of a plain chip in each axis. */
.rd-chip-attr-face--compact   { padding: 5px 6px; letter-spacing: .05em; }
.rd-chip-attr-face--billboard { padding: 5px 9px; letter-spacing: .1em; }

/* --------------------------------------------------------------------------
   The selector row. Owned by RdChipSelector — callers do not pass a gap.

   Measured gaps run 5–8px across twelve rows with no single convention (§4.2);
   6 is the median and the value both header rows and the History filter use.
   The 8px command-row gap at rd-train-stage.jsx:249 is screen layout for the
   stage-viewer footer and is NOT shipped here — RdChipCommand does not own a
   row, so that value arrives with the Stage Editor migration.

   Wrap is the default; scroll is opt-in and only for fixed-height containers. */
.rd-chip-row {
  display: flex;
  gap: 6px;
  align-items: center;
}
.rd-chip-row--wrap   { flex-wrap: wrap; }
.rd-chip-row--scroll { flex-wrap: nowrap; overflow-x: auto; }

/* No disabled rule, deliberately. There is no disabled chip anywhere in the
   corpus, and TBtn's treatment (--rd-surf2 fill, --rd-fg3 label,
   rd-train-kit.jsx:81) would collide with --rd-chip, which is already a near
   neighbour of --rd-surf2. A page that needs one routes it rather than inventing
   it here (§5).

   No loading rule: a chip tap never waits on the network (§6).
   No error rule: N/A.
   The empty case is the absence of the whole strip, not a state on the chip
   (§1.2) — it is handled in Rust by rendering nothing. */

/* ---- 09-stat.css ---- */
/* RdStat — the key/value stat pair. Spec: docs/design/kit/09-tstat.md
   Numbers from tmp/design/snapshot/rd-train-kit.jsx:130-138.

   ⚠ ZERO DECORATION, DELIBERATELY. No background, no border, no clip-path, no
   padding, no margin. That is not an oversight and it is not a reset — it is the
   component (spec §3.1, rd-train-kit.jsx:119-122). The chamfered outlined box
   that surrounds stats on screen belongs to the ROW, an equal three-column grid
   that is identical in shape at 8 of 8 corpus groups (§3.2), and the row is the
   caller's. Adding a fill or a stroke here is the top implementation trap, and
   the designer wrote the sentence that rules it out (rd-train-detail.jsx:46-47):

     "Not a form. Values are set as values — no boxes, no field chrome, nothing
      that offers to be typed into. Editing is a button, once, at the bottom."

   So the whole component is two text elements and one gap. */
.rd-stat {
  display: flex;
  flex-direction: column;
  /* The one number RdStat owns. rd-train-kit.jsx:136. */
  gap: 3px;
}

/* The value. Face, weight, tabular figures and the default --rd-fg all come from
   the shared .rd-num role (style.css) — this rule adds ONLY the 16px the
   substrate binds to this primitive (§2.3; .rd-num's own 17px is the numeric
   FIELD's size).

   §P2 (system monospace stack vs Archivo-tabular) lived entirely inside .rd-num
   and RESOLVED 2026-08-04 to Archivo 800 / 75 / tabular. Nothing here ever
   depended on which way it went, which is why this rule needed no change.

   Qualified by the parent rather than written as a bare .rd-stat__value: it must
   beat .rd-num, and both are single-class selectors, so a bare form would depend
   on which file the orchestrator appends last. Two classes (0-2-0) wins
   regardless of merge order.

   No white-space, no text-overflow, no max-width — the kit sets none, so both
   lines wrap. Columns are ≈106px, the longest measured key is `Personal Bests`
   and every measured key fits; whether a two-line key is acceptable is not
   answerable from the corpus (§3.3). Do not invent an ellipsis. */
.rd-stat .rd-stat__value {
  font-size: 16px;
}

/* Accented — the comparative benchmark, 3 of 24 sites.

   --rd-acc, NEVER --rd-volt. A stat value is TEXT, and substrate §1.1 binds text
   and strokes to acc (#A89968 dark, #5C5030 light) while volt is the accent as a
   FILL and is the same tan in both themes. Writing volt here is correct in dark
   theme BY COINCIDENCE — the two tokens hold the same hex — and renders a
   benchmark number at almost no contrast on #F1F1EC in light. No dark-theme
   screenshot catches it (§9.1).

   Colour is the ONLY difference between the two states: size, weight, spacing
   and the key's own --rd-fg3 are all unchanged (§4.2). That is what makes the
   emphasis cheap enough to be scanned rather than read. */
.rd-stat .rd-stat__value--bench {
  color: var(--rd-acc);
}

/* Placeholder — a value that is not a number.

   The kit's `numc` resolves this BEFORE the accent, so these two classes are
   mutually exclusive by construction in stat.rs rather than by specificity
   here: an accented em dash is an accent on nothing. Same --rd-fg3 step the
   numeric field uses for an empty buffer, which is what closes the asymmetry
   between the two primitives (spec §4.3). */
.rd-stat .rd-stat__value--dim {
  color: var(--rd-fg3);
}

/* No :hover, :focus-visible, :active, disabled, loading or error rule — stated
   rather than omitted, per CONVENTIONS.md. A stat pair is read, not operated
   (§5): no cursor, no pointer-events change, no tabindex, no focus ring. Making
   it a focus stop would add 24 dead tab stops. If a page migration later needs a
   tappable stat row, that is the ROW's spec and goes to Claude Design; this
   text must not acquire a hover treatment either way.

   No empty/placeholder rule either. TStat does not dim its em dash the way the
   numeric field dims an empty buffer (rd-train-kit.jsx:136 colours only on acc),
   so the placeholder renders at full --rd-fg. That asymmetry between two
   primitives by the same hand is measured, and it is routed to Claude Design —
   do NOT "fix" it by copying the field's --rd-fg3 branch in here (§4.3, §9.4). */

/* ---- 10-numeric-field.css ---- */
/* RdNumericField — the digit-entry numeric field.
   Spec: docs/design/kit/10-tnum.md. Numbers from
   tmp/design/snapshot/rd-train-kit.jsx:231-246.

   The BOX is not here. It is RdChamferBox at chamfer 9 (01-chamfer-box.css), so
   the two-layer clip and the max(r - w(2 - √2), 2) inner rule are not re-derived — which
   is what makes the inner chamfer move 7 → 6 on focus for free, because the
   border widens 1 → 1.5. Holding it at 7 across both states is this primitive's
   top trap; numeric_field.rs pins it with a test.

   What lives here: the cell around the box, the value row inside it, the caret,
   the sub-caption, and the focus-ring layer.

   §P2 RESOLVED 2026-08-04 — the numeric face and its default colour are the
   shared .rd-num role in style.css (Archivo 800 / 75 / tabular, --rd-fg).
   Nothing below read which way it resolved; these rules contribute only the
   letter-spacing this field alone adds, plus the two sizes the substrate binds to
   this primitive (§2.3) and the measured --rd-fg3 branch, both of which hold
   under either answer. */

/* The cell. flex:1 at 8 of 8 call sites — every drawn instance shares a row with
   a sibling (a hit-factor readout, another field, or a read-only "Was" box), and
   the kit's non-flexed branch is never drawn. Harmless outside a flex row. */
.rd-numf {
  display: flex;
  flex-direction: column;
  flex: 1;
}

/* The kit puts margin-top:5 on the BOX when a label is present and 0 when it is
   not (rd-train-kit.jsx:103). Written as the label's margin-bottom instead: it
   renders identically, the no-label branch is 0 by construction rather than by a
   conditional, and the box's outer layer belongs to RdChamferBox, which exposes
   no class hook of its own. Face/size/colour come from the .rd-lab role. */
.rd-numf__label {
  margin-bottom: 5px;
}

/* --------------------------------------------------------------------------
   The focus-ring layer — 🟡 PROVISIONAL pending Claude Design (spec §4.2, Q4).

   ⚠ A ring on a chamfered control CANNOT be an `outline` or a `box-shadow`:
   clip-path cuts both. It needs its own layer outside the clip, and 02-tbtn.md
   §5.3 specifies an always-present wrapper so focus costs no layout shift. This
   is that wrapper.

   It is drawn with the same trick as the border itself — a padded, clipped box
   whose background IS the ring — so the ring follows the chamfer instead of
   boxing it. Chamfer 12 = 9 + 3, following TBtn §5.3's r+3 idiom (there: 16 =
   13 + 3) so the two provisional rings match each other rather than each being
   derived separately.

   Two consequences, both stated rather than smoothed over:
   - On the diagonal the ring measures |12 - 2*2 - 9| / √2 ≈ 0.7px, not 2px. The
     button's proposal has exactly the same property; collinear would be 13 and
     uniform 2px would be ~15.8. Flagged with Q4, not resolved here.
   - Inside a flex row the wrapper's 2px padding comes out of the field's width,
     so the drawn box is 4px narrower than the design measures. TBtn §5.3 accepts
     the same cost ("4px of extra layout box on every button").

   This is an ADDITIONAL channel, on top of — never instead of — the design-level
   focus treatment, which has already consumed the stroke (§4.1).

   :focus-visible only, never :focus — a plain :focus ring survives a thumb tap
   as a stuck outline. The UA outline is dropped only inside the rule that
   replaces it. The element is a Tab stop only when the caller opts in
   (`focusable`, default off, tabindex="-1"): the corpus binds no input control,
   and whether this field is keyboard-operable is UNDETERMINED (Q3). */
.rd-numf__ring {
  padding: 2px;
  box-sizing: border-box;
  background: transparent;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%);
}
.rd-numf__ring:focus-visible {
  outline: none;
  background: var(--rd-acc);
}

/* --------------------------------------------------------------------------
   The value row. This is RdChamferBox's inner layer (passed as inner_class), so
   it already carries the fill, the inner clip and box-sizing.

   Centred both axes with gap:1 between value and caret (rd-train-kit.jsx:104).
   min-height 44 is the iOS touch minimum and is met by the field itself.

   ⚠ The caret shifts the digits. Because value and caret are centred AS A GROUP
   with a 1px gap, the 2px caret moves the value's centre 1.5px left on focus.
   That is derived from the flex geometry, not stated by the designer, and
   whether it is intended or should be absorbed by reserving caret space is open
   (spec §3.3, Q6). Implemented as drawn — do not add a phantom caret to
   "fix" it before the ruling. */
.rd-numf__box {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1px;
  min-height: 44px;
}

/* Size, weight, tabular figures and the default --rd-fg are the .rd-num role.
   17px is already .rd-num's own size, so the standard field needs no rule here;
   only Large overrides it.

   letter-spacing is the ONE thing this field adds to the shared role. The kit
   applies -.01em at the TNum call site and nowhere else — TStat's value is
   untracked — so it belongs here, not on .rd-num. It used to ride .rd-num,
   which silently tracked every stat value in the app.

   No white-space, no text-overflow, no max-width: the kit sets no cap on buffer
   length and the box has no fixed width, so what should happen past a maximum —
   ignore the keypress, scroll, shrink the face — is UNDETERMINED (§5.5, Q3).
   Do not invent truncation. */
.rd-numf__value { letter-spacing: -.01em; }
.rd-numf--lg .rd-numf__box  { min-height: 56px; }
.rd-numf--lg .rd-numf__value { font-size: 26px; }
.rd-numf--lg .rd-numf__caret { height: 26px; }

/* Empty buffer, and the provisional disabled state.

   The colour step --rd-fg3 → --rd-fg is the ENTIRE filled/empty signal: no
   placeholder text, no border change, no icon (§4.1). The em dash itself is
   U+2014 and is keyed on buffer EMPTINESS, never on a truthy value — see
   numeric_field.rs, where a legitimately zero score renders as a value.

   Qualified by the parent so it beats .rd-num regardless of which file the
   orchestrator appends last: both are otherwise single-class selectors. */
.rd-numf .rd-numf__value--dim {
  color: var(--rd-fg3);
}

/* The caret bar — 2 × 18px (26 on large), --rd-acc, pinned to the RIGHT of the
   value. It does not travel: the buffer only ever grows at its right-hand end,
   so there is no insertion point for a cursor to sit at. That is the digit-entry
   model showing through the drawing (§5.2).

   --rd-acc, never --rd-volt: this is the focus signal, the same stroke language
   as the border, and in light theme it is the deep warm brown. */
.rd-numf__caret {
  flex: none;
  width: 2px;
  height: 18px;
  background: var(--rd-acc);
}

/* The sub-caption. Body face at 9.5px — NOT the .rd-p role, which is 13px in
   --rd-fg2. rd-train-kit.jsx:108.

   ⚠ 9.5px sits inside contested provisional P3 ("nothing on a phone screen goes
   below 13px", Foundations:116); kit practice ships under substrate §0, and its
   compensation is CONTRAST — the lifted --rd-fg3 must travel with the size. A
   later screen that keeps 9.5px and reverts to a dimmer grey gets the worst of
   both.

   It carries two different jobs (§5.4): the entry hint on the five time fields
   (`Type 451` — the entry SEQUENCE, not the value, which is what makes an
   unfamiliar shooter's first entry succeed) and a unit or scope caption on the
   three integer ones. Both are copy and both arrive as a prop; no string here. */
.rd-numf__sub {
  margin-top: 4px;
  text-align: center;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* Error — 🟡 PROVISIONAL (§4.2, Q4). Proposed by analogy, not invented: TBtn
   kind `d` establishes danger-as-stroke at width 1.5 (rd-train-kit.jsx:88), so
   the error state reuses the FOCUS GEOMETRY with the DANGER colour and one
   stroke language covers both. The message replaces the sub-caption; the caller
   passes it. */
.rd-numf__sub--error {
  color: var(--rd-danger);
}

/* States with no rule here, stated rather than omitted per CONVENTIONS.md:

   hover — 🟡 PROVISIONAL: no stroke change and no fill change, deliberately. The
   design's only stroke change is the focus signal, and an outdoor one-handed app
   has no hover; inventing one here would collide with it. `cursor: text` is
   proposed in the spec but not written, because the box binds no input control
   and the field is not a Tab stop by default — a text cursor over something that
   cannot be clicked into would be a lie. Routed with Q4.

   disabled — the fill (--rd-surf2 via Fill::Recessed), the stroke and the dimmed
   value are all resolved in numeric_field.rs, so there is no extra rule to write.

   loading — N/A. The field holds a locally-typed buffer and waits on nothing. */

/* ---- 11-theme-swatch.css ---- */
/* RdThemeSwatch — one option of the theme radio group.
   Spec: docs/design/kit/11-theme-swatch.md. Substrate: 00-substrate.md §1.4.

   THE STRUCTURAL FACT, and the only thing in this file worth reading twice:
   a swatch renders a miniature of the theme it is OFFERING, not of the theme
   currently in force. Two colour systems are on screen at once.

     - INSIDE the tile  -> the PREVIEWED theme. `.rd-swatch__mini` carries
       data-theme, so every --rd-* below it rebinds. This is why the token
       blocks widened from `:root[data-theme=…]` to `[data-theme=…]` and why
       each block defines the COMPLETE set: a partial block would leak half its
       tokens in from the active theme and the miniature would be a lie.
     - OUTSIDE the tile -> the ACTIVE theme. Radio ring, dot, option label and
       the UNSELECTED tile stroke are chrome; chrome belongs to the theme the
       user is standing in, not the one being offered.

   THE LOAD-BEARING PIXEL is `.rd-swatch__mini-score`. It is --rd-acc, which is
   tan in dark and a deep warm brown in light, while `.rd-swatch__mini-cta`
   below it is --rd-volt, the same #A89968 in both. Side by side, the two tiles ARE the
   acc/volt split drawn (substrate §1.1). **If the two tiles' 5.76 render the
   same colour, the build is wrong** — the previewed scope is not being applied
   and the control has stopped explaining the choice it is asking about.

   Not one hex literal here: every colour in the source is routed through a
   token, including the five frozen dark literals the source hard-codes
   (spec §5, R1-R5). They are the visible edge of a missing parameter — the
   source component takes no active-theme argument, so it could not express
   active-theme chrome and froze it at the one configuration ever rendered. */

/* The whole option is one hit target: <label> wrapping the input, the tile and
   the text. The ring is 13px and the label 10px — either alone fails the 44pt
   minimum the frame itself observes (substrate §P3). This element clears it in
   both axes by a wide margin (>=130 x ~200). */
.rd-swatch {
  flex: 1 1 0;
  min-width: 0;
  display: block;
  position: relative;                     /* containing block for the absolutely
                                             positioned native input */
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* Visually hidden but STILL FOCUSABLE and still hit-testable by the label.
   Never `display:none` / `visibility:hidden` — both remove it from the tab
   order and take the native radio-group keyboard semantics with them (single
   tab stop, arrow-key roving, wrap-around, "2 of 2" announcement). */
.rd-swatch__input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  opacity: 0;
  pointer-events: none;                   /* the <label> does the hit testing */
  appearance: none;
  -webkit-appearance: none;
}

/* Third, outermost clipped layer — ALWAYS present, transparent at rest, so
   focus costs no layout shift (02-tbtn §5.3 sets this idiom for the kit).
   A focus ring cannot be an `outline` or a `box-shadow` here: clip-path clips
   both, which is the same failure that forced the two-layer border.
   PROVISIONAL, pending Claude Design (spec §3.5, open question 3).

   Geometry: padding p = 2, tile chamfer n = 11, wrapper chamfer 13. The ring's
   thickness along the diagonal is (2p + n - 13)/sqrt(2) = 2/sqrt(2) ~ 1.41px
   against 2px on the straight edges. Validated against the known-positive pair
   in 01-chamfer-box.css: outer 13, w 1.5, inner 10 gives (3 + 10 - 13) = 0,
   i.e. the documented collinear/no-stroke case. Two live consequences:
     - 13 must stay BELOW 2p + n = 15, or the wrapper clips the tile's own
       diagonal instead of ringing it;
     - 12.17 (= 2p + n - p*sqrt(2)) would make the ring uniform 2px all round.
   Both routed with the ring itself rather than chosen here. */
.rd-swatch__focus {
  display: block;
  padding: 2px;
  box-sizing: border-box;                 /* inset, do not grow (see .rd-box) */
  background: transparent;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 13px), calc(100% - 13px) 100%, 0 100%);
}
.rd-swatch__input:focus-visible ~ .rd-swatch__focus {
  background: var(--rd-acc);              /* focus in this system is an --rd-acc
                                             STROKE (TNum draws it that way) —
                                             acc, never volt: substrate §1.1 */
}

/* The chamfer box's inner. It carries only the fixed height; the previewed
   subtree inside carries the padding and paints the ground edge to edge, so
   the tile's own fill role is structurally invisible. `min-height` rather than
   `height` so it does not fight `.rd-box__inner { height: 100% }` and does not
   depend on which of the two rules the merged stylesheet lists last. */
.rd-swatch__tile {
  display: flex;
  min-height: 148px;
  padding: 0;
}

/* ---- everything below here resolves in the PREVIEWED theme ---------------- */

/* The ground. --rd-swatch-ground, not --rd-app-bg: app-bg carries the
   photographic texture and different scrim alphas, and a background photograph
   inside a 148px miniature is not what the source draws (spec §3.3). The `tex`
   branch is pre-resolved per theme, so nothing here tests a flag
   (substrate §1.2). */
.rd-swatch__mini {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 11px;
  box-sizing: border-box;
  background: var(--rd-swatch-ground);
}

/* The four miniature items are a PICTURE, not a composition. Each is drawn at
   miniature-specific numbers that differ from the primitive it resembles, so
   none of them may be built from RdHeading / RdStat / RdButton: title is TH1's
   axes at 15px (TH1 is 30), score is mono at weight 700 (TNum is 600), CTA is
   7.5px / .1em (RdButton's label is 12.5px / .14em). Using the real primitives
   puts a 30px heading and a 52px button inside a 148px box (spec §7.2).
   They pair with `.rd-disp`, which supplies family + the uppercase transform. */
.rd-swatch__mini-title {
  font-weight: 900;
  font-stretch: 62%;
  letter-spacing: 0;
  font-size: 15px;
  line-height: .95;
  color: var(--rd-fg);
}

/* ⚠ THE LOAD-BEARING PIXEL — see the header. --rd-acc (tan dark / brown light)
   against the CTA's theme-invariant --rd-volt directly below it.

   §P2 RESOLVED 2026-08-04, and this was the one site the conversion missed:
   `.rd-num` went to Archivo 800 / width 75 / tabular while this stayed on the
   system monospace stack at weight 700. The kit draws the swatch score with the
   same shared `mono` role as every other number — "not a system mono. Numbers
   are the loudest thing on a scoring surface, so they speak in the display
   voice." Colour stays --rd-acc: this is text, and text takes acc, not volt. */
.rd-swatch__mini-score {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 21px;
  line-height: 1;
  color: var(--rd-acc);
}

/* Three rules standing in for body copy. Widths are fixed picture geometry, so
   they are modifier classes rather than inline styles — inline style is for
   genuinely dynamic numbers only. */
.rd-swatch__mini-bars {
  display: grid;
  gap: 4px;
  margin-top: 2px;
}
.rd-swatch__mini-bar {
  height: 3px;
  background: var(--rd-line);
}
.rd-swatch__mini-bar--full { width: 100%; }
.rd-swatch__mini-bar--mid  { width: 74%; }
.rd-swatch__mini-bar--short { width: 50%; }

/* Pinned to the bottom of the tile by `margin-top: auto`. --rd-volt as a FILL
   with --rd-on-volt (black in both themes) on it: never white text on volt. */
.rd-swatch__mini-cta {
  margin-top: auto;
  height: 22px;
  background: var(--rd-volt);
  color: var(--rd-on-volt);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 7.5px;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%);
}

/* ---- back to the ACTIVE theme -------------------------------------------- */

.rd-swatch__opt {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
}


/* --rd-fg2, NOT Foundations' superseded #9A9A9A (spec §5 R4). The kit's pairing
   is preserved: the ring takes the tertiary grey, the label the secondary one —
   the hierarchy comes from the step between them. */
.rd-swatch__name {
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 10px;
  color: var(--rd-fg2);
}

/* ---- selected ------------------------------------------------------------ */

/* The tile stroke. Reaching into `.rd-box` from here is deliberate and is the
   one place this component overrides the primitive it composes: RdChamferBox's
   `Stroke` enum has no volt variant BY DESIGN (substrate §1.1 — a stroke is
   acc, never volt), and the selected swatch is the corpus's one volt stroke,
   which the spec is explicit must not be "fixed" to acc: volt is a fill here
   and is #A89968 in both themes, so both tiles get a tan selection band
   whichever theme they preview. Confined to this component rather than widening
   the shared enum, which would legalise a volt stroke everywhere. Routed.
   Unselected needs no rule: `Stroke::Line` already resolves --rd-line in the
   ACTIVE scope, which is what the frozen #2A2A2A should have been (§5 R5). */
.rd-swatch--sel .rd-box { background: var(--rd-volt); }
.rd-swatch--sel .rd-swatch__name { color: var(--rd-fg); }   /* not '#fff' (§5 R3) */

/* ---- hover / pressed: PROVISIONAL, pending Claude Design ------------------ */

/* The kit specifies neither — the frames are iOS and have no hover. Proposal:
   on an UNSELECTED option lift the stroke --rd-line -> --rd-fg3 and the label
   --rd-fg2 -> --rd-fg, leaving the border WIDTH at 1 so the tile does not
   shift; selected shows no hover change. Nothing inside the tile reacts: the
   miniature is a picture of another theme and must not answer the pointer —
   which is why no rule below crosses into `.rd-swatch__mini`. */
.rd-swatch:not(.rd-swatch--sel):hover .rd-box { background: var(--rd-fg3); }
.rd-swatch:not(.rd-swatch--sel):hover .rd-swatch__name { color: var(--rd-fg); }
/* Pressed borrows the hover treatment: on touch there is no hover at all, so
   :active is the only pointer feedback this control ever gets. Fill changes
   only — no transform, no scale: a scale on a chamfered box makes the diagonal
   crawl against the pixel grid, and the system has no motion vocabulary (zero
   `transition` in the corpus). */
.rd-swatch:not(.rd-swatch--sel):active .rd-box { background: var(--rd-fg3); }

/* Disabled: N/A by design — both themes are always available, the control has
   no disabled state, and one must not be invented. Loading / error / empty:
   N/A — the choice is local and synchronous, there is nothing to await. */

/* ---- 12-rdicon.css ---- */
/* RdIcon — the redrawn legacy icon set. Spec: docs/design/kit/12-rdicon.md
   Component: apps/web/src/components/kit/icon.rs
   Source of truth: tmp/design/snapshot/rd-icons.jsx

   THREE RULES, AND THAT IS THE POINT. Everything geometric — viewBox, size,
   coordinates, stroke widths, joins, caps — is an SVG attribute set by the
   component, because it is transcribed asset data and belongs next to the
   coordinates it belongs to. CSS carries only what CSS has to: the two colours,
   which are token roles.

   INK is `currentColor` on every element, so a consumer colours a
   twelve-element mark with one inherited `color` declaration. This is not a
   style choice — `var()` is NOT substituted inside an SVG presentation
   attribute, so `stroke="var(--rd-acc)"` silently draws nothing, and a
   per-element CSS rule cannot work either because the elements differ in
   whether they take a fill or a stroke. `currentColor` is the one mechanism
   that survives both constraints.

   ⚠️ THE COST OF THAT, AND IT BIT ON THE FIRST RENDER: `color` inherits a
   COMPUTED value. A consumer that re-scopes `data-theme` BELOW the element
   where `color` was set does not re-resolve it — the icon keeps the outer
   theme's ink. So a consumer must set `color` at or inside its own theme scope
   rather than relying on an ancestor's. `.rd-tab` does (`var(--rd-fg3)`); the
   /kit panels did not, and their icons came out white-on-white in the light
   panel while every test passed. */
.rd-icon { display: block; flex: none; --rd-icon-ground: var(--rd-app-bg); }

/* THE GROUND, for the knockout — and the only reason `bg` is threaded through
   the component at all. rd-icons.jsx:115-118: "`bg` is the ground the icon sits
   on — only the checklist needs it, for its knockout, but it must be passed
   wherever the ground is not app ink or the check will fuse."

   The default above is the app ground, which is correct for the app ground and
   WRONG anywhere else — a knockout in the wrong colour is not a subtle miss, it
   is a stripe of the wrong shade painted across the mark. */
.rd-icon--on-surf { --rd-icon-ground: var(--rd-surf); }

/* ⚠️ ONE ICON USES THIS and it is load-bearing (rd-icons.jsx:40-42): "The check
   breaks past the right edge of its bar, as in the source; where it crosses the
   bar it needs a knockout in the ground colour, or volt-on-volt fuses into a
   blob." The checklist draws its check TWICE — once here at 3.1x weight in the
   ground colour, then again at 1.5x in ink. Deleting either pass is a visible
   defect at tab size, not a subtle one. Same family as the substrate's
   opaque-inner-fill rule: a mark composited against the wrong ground. */
.rd-icon__knockout { stroke: var(--rd-icon-ground); }

/* ---- AU1-gate-screens.css ---- */
/* ScreenVerifying + ScreenGate. Spec: docs/design/kit/AU1-auth-boot-gate.md §2, §3
   Component: apps/web/src/pages/gate_screens.rs

   THE FIRST KIT-CONSUMING SCREENS. What this block does and does not do is the
   pattern for every screen migration after it:

   — It carries LAYOUT ONLY. Every colour, type size and chamfer on these two
     screens comes from a primitive; nothing here sets a colour, and the one
     exception (the fault mark's size) is geometry the primitive does not expose.
   — It owns the CENTRING, because AU1 §0.3 rules the centring belongs to the
     screen and not to the shell. `RdShell` gives a gutter and a content region;
     a shell that centred its children would make every ordinary page fight it. */

/* Both screens: one centred column inside the shell's content region. The
   forced screens have no scroll and no back row, so the column takes the full
   height it is given and centres in it. */
.rd-gate {
  min-height: 100%;
  display: flex;
  flex-direction: column;
  /* VERTICAL centring only. `RDShell` carries `center = true` and applies it as
     the content region's `justify-content` (`:45`, `:50`), and all four screens
     ALSO wrap their content in a `flex: 1` + `justify-content: center` box — so
     the drawing expresses it twice and AU1 §0.3 rules it belongs to the screen. */
  justify-content: center;
}

/* ⚠️ HORIZONTAL CENTRING IS NOT SHARED, AND THE TWO SCREENS DISAGREE ON PURPOSE.
   `ScreenVerifying` (`:140`) and `ScreenSplash` (`:124`) set `alignItems:
   'center'`; `ScreenGate` (`:162`) sets NO `alignItems` at all, so its fault
   block, title and body are LEFT-ALIGNED.

   Measured, not eyeballed — and the corpus double-confirms it: `ScreenGate`'s
   `Change server` link wraps itself in `textAlign: 'center'` (`:168`), which is
   only necessary because the content around it is not centred.

   This was wrong in the first implementation, which centred both because the
   PREVIOUS shipped screen was centred (`text-center` on the old div). The kit
   migration transcribes the drawing, not the thing it replaces. */
.rd-gate--brand {
  align-items: center;
  text-align: center;
}

/* ⚠️ THE WORDMARK IS THE ASSET NOW — AU1 §6.12 CLOSED (2026-08-25).

   What stood here was the argument for text: "nobody ships a wordmark and
   apps/web does not have one. It is text here because the pre-boot splash in
   index.html renders it as text and MUST (it draws before any asset can
   load)." Export 28 ships `uploads/RD_Assets/LOGOs`, so both halves moved in
   one commit — which is the condition the old note set.

   §2.1's requirement is unchanged and is still what governs this rule: "same
   composition as the splash so the transition is silent." The splash and these
   two screens draw the SAME artwork (`brand.rs`, which compares their path
   data), so the only thing left for this rule to get wrong is the size.

   IT IS THE SQUARE LOCKUP. `rd-auth-screens.jsx:9` sets `RD.logo` to the
   square powered-by-T.REX artwork and `RDLogo` (`:85`) is the only brand
   element these screens draw. The horizontal Type wordmark is drawn at exactly
   one site in the corpus — the tablet rail head, as type — and stays there.

   THE WIDTH IS 155px, WHICH IS THE DRAWN SLOT THROUGH THE DRAWN COMPONENT.
   `rd-auth-screens.jsx:141` writes `RDLogo w={182}`, and `RDLogo` renders
   `Math.round(w * 0.85)`. 182 is the slot; 155 is the mark. Taking 182 to CSS
   would be transcribing the argument to the function instead of its result.

   ⚠️ 155 LOOKS SMALL BESIDE THE OLD 20px TEXT HEAD, AND THAT IS THE POINT.
   01 Foundations:73, on this exact swap: "The square lockup is near 1:1 where
   the old horizontal PNG was wide, so widths step down to keep the same visual
   weight." The Foundations plate itself went 340px -> 240px. A square at the
   strip's width would be four times the ink.

   `max-width` under it because the drawing is a 390px phone frame and this app
   renders down to 320; at a fixed width the mark would touch both gutters on
   the narrowest device the shell supports. 62vw rather than 100%: this is a
   near-1:1 block, so the constraint that bites first on a small screen is
   HEIGHT, and a mark allowed to fill the width would push the caption under
   the fold.

   `height: auto` is load-bearing rather than a default: the artwork carries a
   viewBox and no intrinsic height, and a bare width on such an image is what
   makes a boot screen reflow the moment the SVG parses.

   /login takes the same rule one size up — see `.rd-gate__mark--auth` in
   s02-auth.css, which is a MODIFIER on this rule and not a second declaration
   of it, for the reason that file states at length. */
.rd-gate__mark {
  display: block;
  width: 155px;
  max-width: 62vw;
  height: auto;
  margin: 0 auto;
}

/* 34px under the mark on Verifying (`rd-auth-screens.jsx:154`), against the
   splash's 30 (`:143`).

   ⚠️ THE 4px IS UNEXPLAINED AND IS DELIBERATELY NOT NORMALISED. Two screens
   built to be indistinguishable differ by exactly this and by one caption size.
   AU1 §2.1 routes it: either a deliberate optical correction for a longer
   string, or a transcription artefact. "Do not silently normalise it in either
   direction." Transcribed as drawn, flagged here, answered in §6. */
.rd-gate__status { margin-top: 34px; }

/* The fault mark: 46x46, cham(12), solid status colour, replacing the wordmark.
   Size only — the chamfer comes from RdChamferBox's prop and the colour from
   Fill::Danger / Fill::Warn, so neither is expressible here by accident.
   26px under the block (`:163`). */
.rd-gate__fault { margin-bottom: 26px; }
/* 14px under the title. The type roles carry NO spacing by design (`.rd-p` is
   `margin: 0`), so the gap is the screen's to supply — and the drawing puts it
   on the body role itself: `RDBody`'s `margin: '14px 0 28px'` (`:82`). The 28px
   below it belonged to the Retry button this platform does not render. */
.rd-gate__body { margin-top: 14px; }
.rd-gate__fault-inner { width: 46px; height: 46px; }

/* ⚠️ ONE OF THE THREE SCREENS HAS A SIBLING BELOW IT, AND `min-height: 100%`
   CANNOT SHARE. `.rd-gate` fills the shell's content region on its own, so a
   foot beside it starts exactly at the fold and the shooter has to scroll a
   terminal screen to read the one instruction on it. This hands the height back
   to the flex parent — the column takes what is left, the foot keeps the bottom
   — and it is a modifier rather than an edit to the base rule because Verifying,
   Restoring and the boot splash still have nothing below them, and a rule three
   screens rely on is not the place to fix one screen's problem.

   `flex: 1` + `min-height: 0` is the same pair `.rd-auth` takes for the same
   arrangement, one drawing over (`s02-auth.css`). */
.rd-gate--footed { min-height: 0; flex: 1; }

/* The web gate's footer — 17 Aug reply §2a, `rd-auth-screens.jsx:192`.

   ⚠️ DECLARED HERE, NOT BORROWED FROM `.rd-auth__foot`, though the values are
   the same. The wordmark is shared across the two parts because `/login` renders
   IMMEDIATELY after `ScreenVerifying` and two spellings of it make that
   transition flicker — an adjacency argument, and this footer has no such
   neighbour: the blocked gate is terminal by construction and `/login` is
   precisely the screen it never reaches. `s02-auth.css` also scopes itself to
   the ROUTED auth screens, and these three render before the router exists.
   A shared value with nothing sharing it is a dependency, not a saving. */
.rd-gate__foot {
  flex: none;
  text-align: center;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  line-height: 1.5;
  color: var(--rd-fg3);
  padding: 20px 0 26px;
}

/* ---- S1-home.css ---- */
/* Home, the launcher. Spec: docs/design/kit/S1-home.md
   Component: apps/web/src/pages/home.rs
   Source: rd-train-shell.jsx ScreenHome :49-83

   LAYOUT AND RHYTHM ONLY. Every colour, chamfer and type size on this screen
   comes from a primitive; the two colours below are the avatar's and the
   chevron's, neither of which is a primitive's job. */

.rd-home {
  display: flex;
  flex-direction: column;
  min-height: 0;
  /* :53 — `paddingTop: 6` inside the content region. */
  padding-top: 6px;
}

/* :56 — the lockup row, `gap: 12`, `marginBottom: 12`. */
.rd-home__head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
}
.rd-home__lockup { flex: 1; min-width: 0; }

/* :59 — `Powered by` + `T.REX ARMS` on one line, `marginTop: 4`.
   TWO LABELS, not one with a coloured span: RdLabel carries one emphasis, and
   the two the role already has are exactly these two. A `colour` prop on the
   label is what 05-tlab.md §2.1 forbids by name. The gap is a word space. */
.rd-home__poweredby { display: flex; gap: .32em; margin-top: 4px; }

/* :62 — 34x34, --rd-chip, 1.5px --rd-acc, circular.
   ⚠️ NOT a border-radius violation. Substrate §1.5: "Circles and pills are
   permitted where the form is inherently round: radio, AVATAR, status dot,
   NUMERIC BADGE. The ban is on softened corners on rectangular things, which is
   the whole point of having a chamfer." */
.rd-home__avatar {
  flex: none;
  width: 34px;
  height: 34px;
  box-sizing: border-box;
  background: var(--rd-chip);
  border: 1.5px solid var(--rd-acc);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.rd-home__initials {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  font-size: 12px;
  color: var(--rd-fg2);
}
/* The avatar is a control, so it gets the same visible focus the kit gives every
   other one. Square-cornered rings are the system's rule; this element is the
   exception the substrate names, so the ring follows the shape. */
.rd-home__avatar:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* :67 — the grid, `gap: 7`. */
.rd-home__tiles { display: grid; gap: 7px; }

/* The whole row is the link (the same reachability rule as the tab bar's cell):
   a Link wrapping only the word would take a 54px target down to the height of
   14px type. */
.rd-home__tile-link { display: block; text-decoration: none; -webkit-tap-highlight-color: transparent; }
.rd-home__tile-link:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* :69-70 — `padding: '11px 15px'`, `gap: 12`, `minHeight: 54`. */
.rd-home__tile {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 54px;
  padding: 11px 15px;
  box-sizing: border-box;
}
.rd-home__tile-copy { flex: 1; min-width: 0; }
/* :73 — the description sits 4px under the name. The type roles carry no
   spacing by design (`.rd-p` is `margin: 0`), so the screen supplies it. */
.rd-home__tile-copy .rd-p { margin-top: 4px; }

/* :74 — 7x12, --rd-fg3, stroke-width 1.6, square cap. Sized here because the
   component sets no width/height attributes; `currentColor` takes the value. */
.rd-home__chevron { flex: none; width: 7px; height: 12px; color: var(--rd-fg3); }

/* RdAvatar — a person, reduced to two letters.
   Spec: docs/design/kit/13-rdav.md · Source: rd-people.jsx `Av` :27-29
   ------------------------------------------------------------------------- */

/* ⚠️ RENAMED `.rd-av` → `.rd-avmark`, 2026-08-18 — a NAMESPACE COLLISION, not a
   flex-geometry defect. James's red-line item 6: the avatar rendered as a wide
   oval (~1.6:1) on Profile and Edit Profile — width inflated, height held.

   The mark's own rule already carried `flex: none` and an explicit
   width/height. The deformation was never this rule failing to declare armor;
   it was `s04-author.css:719`'s UNRELATED `.rd-av` — "ROW 09 — ScreenVariant,
   same drill, new distance", the course-authoring distance-variant screen's
   root container — declaring `flex: 1; flex-direction: column; min-height: 0;
   padding-top: 4px;` under the IDENTICAL selector. Two components, one class
   name, equal specificity (one class each): `styles/order.txt` loads
   `s04-author.css` (line 363) after `_legacy-kit-components-and-screens.css`
   (line 100), so the later rule's `flex: 1` won the cascade for every element
   bearing `.rd-av`, this mark included — CSS cascades per DECLARED PROPERTY,
   not per rule, so `flex: none` here was silently replaced while `width`,
   `height` and everything else s04 does not touch survived untouched.

   `flex: 1` expands to `flex: 1 1 0%` — flex-basis 0% overrides the explicit
   `width` for main-axis sizing, so wherever the mark sits as a DIRECT flex
   item of a row with a growable sibling (the ident/name column beside it is
   `flex: 1` at every one of the mark's ~13 mounts: `.rd-prof__ident`,
   `.rd-thub__head`'s identity block, `.rd-avup`'s bare second child, `.rd-
   lb__who`, `.rd-af__ident`, `.rd-board__who`, …), the mark grows to share the
   row's leftover width with that sibling. Height stays pinned — `align-items:
   center` on every one of those rows, never `stretch` — which is exactly
   "width inflated, height held" and exactly why the deformation reads as
   horizontal. A mount wrapped in a non-flex intermediate cell (`.rd-
   prof__face` in the friends facepile) was never exposed: flex properties are
   inert on an element that is not itself a flex/grid item.

   The fix is the rename, not new armor: eliminating the shared selector
   removes the leak at its root, for every current and future mount, rather
   than out-specificitying or `!important`-ing one property while the other
   three leaked declarations (`flex-direction`, `min-height`, `padding-top`)
   remain live under the next unrelated `.rd-av` some other screen writes.
   `s04-author.css`'s rule is untouched — it is a correct, tested, unrelated
   component and this file has no reason to rename it.

   THE RATIO LIVES HERE AND NOWHERE ELSE. The drawing sets `fontSize: s * .34`
   at all nine sites, so the size prop drives the numeral through one custom
   property rather than through a size→font table in Rust. A table would be four
   rows today and five the first time a screen wants 44px — and the fifth row is
   where the wrong number gets typed.

   border-box is a STATED CORRECTION to the drawing (spec §6.6): React's default
   is content-box, which would make a 30px avatar measure larger than stated by
   its stroke and break the 46px row rhythm SQPick sets. (Applies unchanged to
   the 12 Aug state-weighted strokes: border-box absorbs 1px and 2px alike.) */
.rd-avmark {
  /* THE ARMOR. `flex: none` is what stops the mark absorbing a flex row's
     leftover main-axis space — it was correct here from the start; the 2026-
     08-18 defect was another rule's `flex: 1` reaching this class from OUTSIDE
     this file, under the same name. Pinned by
     avatar.rs::tests::the_armor_survives_removal, whose own doc is the
     red-then-green receipt: deleting this declaration is what proves the test
     is watching it. */
  flex: none;
  box-sizing: border-box;
  width: var(--rd-av-size);
  height: var(--rd-av-size);
  border-radius: 50%;
  background: var(--rd-chip);
  /* Weight is a function of STATE, not size (rd-profile.jsx "ONE AVATAR,
     ruled 12 Aug"): 1px at rest, 2px marked — the marked one is heavier
     because that is the signal. Widths live on the state classes below. */
  border-style: solid;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .04em;
  font-size: calc(var(--rd-av-size) * .34);
  color: var(--rd-fg2);
}
/* The acc/ctrl pair, which is the substrate's control-boundary rule and not a
   local choice — the same one the squad's order checkbox takes one element
   away (S2-squad.md §2.4). */
.rd-avmark--lit  { border-color: var(--rd-acc); border-width: 2px; }
.rd-avmark--rest { border-color: var(--rd-ctrl); border-width: 1px; }

/* The squad SETUP and squad BOARD sections used to sit here — S2a and S2b,
   spec docs/design/kit/S2-squad.md §2 and §3. They moved out of this pool
   verbatim on 2026-08-21 and now live, with an owner, in
   apps/web/styles/s09-board.css. Nothing changed in the move.

   ⚠️ THE POINTER NAMES THE FILE AND NOT THE SELECTORS, deliberately, and for
   the reason _legacy-pre-kit.css states at its own moved rule: the class
   sweeps in the page sources search the ASSEMBLED sheet for their selector, so
   spelling one in a comment lets a comment satisfy a rule-body assertion. The
   file name is enough to find them and cannot be mistaken for a declaration. */
/* ==== ADM0-chrome · ADM0 Pro chrome — rail, page head · owner: 1-track3 ==== */

/* ---------------------------------------------------------------------------
   CUSTODY MOVE, 2026-08-24. This file WAS `_legacy-pro-chrome.css`, and it was
   UNOWNED · DELETE-ONLY: *"No lane owns this file, so no lane may add to it …
   Any lane MAY delete what its own change stranded. The pool only shrinks."*

   ⚠️ NOTHING MOVED AND NOTHING WAS REORDERED. This is a RENAME, in place, at
   the same slot in `order.txt` — every rule below sits exactly where it sat, in
   the same sequence, at the same position in the assembled sheet. That is the
   whole reason it is a rename rather than an extraction into a new part: an
   extraction lands its rules at a different point in the concatenation and can
   silently flip which declaration wins a specificity tie, and this block IS
   contended — `adm0-mobile.css` deliberately sits after it to win four
   descendant overrides (`order.txt`'s note on that entry). A rename cannot move
   that pair; a new part could.

   WHY IT NEEDED AN OWNER, which is the same argument `s09-board.css` made on
   2026-08-21. `48 Kit Rules.html`'s **Shell Geometry** section (James, 21 Aug)
   rules the Pro rail and page head to the shooter shell's numbers — rail width
   208, rows 56 min with a 3px active edge, header bars 76 min bottom-aligned —
   and every rule those numbers live in is in THIS file. In the pool no lane
   could legally change them: delete-only admits a deletion and a ruled value
   change is not one. The drawing was unbuildable until the block had someone to
   answer for it. So: custody first, in this same change, then the ruled values.

   The pool still only shrinks. It is one file smaller, not one file different.

   THE OLD NAME'S TWO NOTES, KEPT because they are still true of the content:

   ⚠️ THE FILENAME NAMED THE CONTENT, NOT THE POSITION — and the two disagreed.
   These are ADM0 rules, and in the single stylesheet they sat under the ADM1-PR3
   banner (lines 4456-4754 at `9e5b26b`). The split kept them exactly where they
   were: it re-files by POSITION. The name is now honest; the POSITION is still
   ADM1-PR3's, and that is what #662 is about.

   Moving them under adm0.css is #662, and it is NOT a pure move: it changes CSS
   source order, which decides specificity ties. It needs verification of its
   own — do not treat it as the same class of safe move the split or this rename
   were.
   ===========================================================================
   ADM0 — the Pro chrome. Spec: docs/design/kit/ADM0-chrome.md
   Drawn: tmp/design/snapshot/rd-pro-kit.jsx (rd-pro-shell.jsx lands it).

   Desktop. The shooter shell's 448px cap and its gutter tokens do not apply and
   are not referenced here: --rd-shell-max-w stays exactly one screen's decision.

   ⚠️ 1280x800 is the DRAWING CANVAS, not a cap ("1280 is the width a 13"
   laptop actually gives you once browser chrome is gone", rd-pro-kit.jsx:3).
   The console's old max-w-7xl letterboxed anything wider; the rail is fixed and
   the main column takes the rest.

   ⚠️ 1600 IS THE CAP, AND IT IS A DIFFERENT NUMBER FROM THE CANVAS. This block
   read "NO max-width on the frame" until 2026-08-26, and that sentence was
   written before the growth ruling and never revisited. `31 (Desktop) Pro
   Shell`, "The desktop growth question, ruled 18 Aug", names this console
   explicitly: *"the tablet layout is the desktop layout, and the shell has a
   hard upper end. Nothing new appears with width. Shooter routes grow from the
   1194 the tablet documents draw, console routes from the 1280 drawn here, and
   both stop at 1600 - content centres on ink past that, and the growth between
   floor and cap goes into pane measure and whitespace, never into new columns
   or new density. The rail does not grow at all."*

   The two ⚠️s agree rather than fight: 1280 is the FLOOR the drawing is drawn
   at and must not become a cap, 1600 is the CAP the ruling gives, and between
   them the main column simply grows. The shooter half of the same sentence has
   shipped since the 18 Aug export (kit-tablet-shell.css §3); the console half
   is this rule. See `.rd-pro__main` below for the mechanism and for why the cap
   is on the column here and on a frame element there.
   --------------------------------------------------------------------------- */

.rd-pro {
  /* RAILW, `rd-pro-kit.jsx:27` ("const RAILW = 208").
     ⚠️ 232 -> 208, ruled 21 Aug (`48 Kit Rules.html`, Shell Geometry):
     "Shells share their geometry across the two apps; rooms differ by colour
     and structure, never by dimensions … the door swaps between the apps in
     place, and chrome that changes size across the swap reads as the page
     lurching. The shared numbers: rail width 208 (shooter tablet RAIL_W, Pro
     RAILW, the Platform rail)". The shooter rail already ships 208
     (`kit-tablet-rail.css`); Pro gives up its 24px and the content panes gain
     them. Consumed here at `.rd-pro-rail` and by `adm0-picker.css`, which sizes
     the open org menu to the rail. */
  --rd-pro-rail-w: 208px;

  /* ⚠️ A DEFINITE HEIGHT AND A CLIP — THE FRAME IS A BOX, NOT A COLUMN THAT
     GROWS. This was `min-height: 100vh` and that one word was the whole of the
     rail-scrolls defect (James, NTOA rehearsal, on `/admin/drills/:id/edit`):
     a floor is not a height, so a page taller than the fold grew THIS element,
     the document gained the overflow, and the RAIL — a flex child stretched to
     the frame — scrolled away with it. The console has no fixed chrome at all
     under a floor.

     THE DRAWING RULES A BOX, TWICE, IN TWO FILES. `ProFrame`
     (`rd-pro-kit.jsx:319`) mounts the console as
     `width: w, height: h, overflow: 'hidden'` and `ProShell`
     (`rd-pro-kit.jsx:326`) fills exactly that box with
     `width: '100%', height: '100%'`. `51 Shell Lab`'s `LabTab`
     (`rd-shell-lab.jsx:36`) restates it for the shared shell geometry:
     `width: 1194, height: 834, overflow: 'hidden'`. The drawn shell is
     BOUNDED and CLIPPED; only the content region inside it is allowed to
     exceed its box. Our build read `height: '100%'` as "fill the page" and
     shipped a floor; it means "fill the frame", and the frame is one screen.

     `100dvh` OVER `100vh`, WITH `100vh` LEFT ABOVE IT AS THE FALLBACK — the
     same pair `.rd-prom` (adm0-mobile.css) and `.rd-rday-app` (adm-rday.css)
     already ship, for the reason `.rd-tshell` states in kit-tablet-shell.css:
     on a mobile browser `100vh` is the URL-bar-collapsed height, so a `100vh`
     shell is taller than the visible viewport. The console's Tablet arm is
     this same element (`layout.rs`: only `DeviceClass::Phone` returns early),
     so a tablet browser really does reach this rule.

     `overflow: hidden` IS NOT DECORATION — it is what makes "the document
     never scrolls" true rather than merely usual. With a definite height and
     no clip, anything taller than the frame still extends the document's
     scrollable overflow and the defect returns for that case.

     ⚠️ WHAT THAT COSTS, MEASURED RATHER THAN REASONED (headless Chrome against
     the assembled sheet, 1280 wide, 2026-08-24; the full eight-row rail — the
     T.REX staff maximum in `layout.rs`: the org's five, plus `Platform` and
     `PlatformOrgs`). The rail's rows need 458px and the head 67. At an inner
     viewport height of 613 the list box is 465 and everything fits; at 553 the
     list box is 405 and the LAST ROW clips by about 53px. So the threshold is
     an inner height near 600 — a window of roughly 690 with browser chrome.

     THE SWITCH DOOR IS NEVER WHAT CLIPS, which was the thing worth checking:
     `door bottom` measured equal to the viewport height at 800, 700, 640 and
     600. `.rd-pro-rail__list` is `flex: 1; min-height: 0` and the door is
     `flex: none`, so the LIST absorbs every pixel of the shortfall — the exact
     shape `ruling_the_foot_is_a_fixture_and_the_list_keeps_the_height`
     (shell.rs) already pins. The way out of the console stays reachable at
     every height; what a very short window loses is the tail of the row list.

     Recorded rather than fixed, and it follows `.rd-tshell__rail`'s precedent
     (`overflow: hidden`, "membership is closed at seven rows"): a rail
     scroller is a second scrolling region and the drawing has exactly one.
     That the eight-row rail can clip a row below ~600 is a designer question,
     filed with this change. */
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  display: flex;
  align-items: stretch;

  /* ⚠️ **THE TOP INSET, ONCE, AT THE SHELL ROOT — RULED 2026-08-28 (James,
     option (c) on the rehearsal finding).** The shooter tablet shell carried a
     fixed 24px status reserve inside its content column, which insetted its page
     head and not the rail beside it; the ruling deletes that reserve and puts
     one safe-area inset on each shell ROOT instead:

       "The top inset becomes `env(safe-area-inset-top, 0px)` applied ONCE at
        the shell level — the tablet shell root, both rail and page column, and
        the Pro shell root equally — so the shooter rail head, the shooter
        tablet page head and the Pro top all start at the same y: 0 in a browser
        tab, the real inset in the installed PWA."

     THE CONSOLE IS IN IT BECAUSE THE CONSOLE IS A TABLET FRAME TOO. `layout.rs`
     returns early only for `DeviceClass::Phone`, so an installed iPad PWA
     crossing the door reaches THIS element — and a room that does not take the
     inset would be exactly the 24px jump across the crossing that the shooter
     side's own removal is fixing. On a desktop browser, where the console
     usually is, there is no safe area and this is 0.

     `env(safe-area-inset-top, 0px)` and not a number: `index.html` ships
     `viewport-fit=cover`, which is the precondition for `env()` resolving to
     anything, so this is the platform's own measurement.

     IT COMES OUT OF THE 100dvh, NOT OFF THE BOTTOM: `_legacy-base.css` sets
     `box-sizing: border-box` on `*`, so the padding is inside the definite
     height this rule's own note argues for at length, and the frame still ends
     at the fold with `overflow: hidden` still meaning what it says.

     ⚠️ NO PER-HEAD OFFSET ANYWHERE. `.rd-pro-top`, `.rd-pro-rail__head` and the
     shooter's two heads all declare no top margin, padding or offset of their
     own — one inset, one place per root, or the reserve comes back one room at
     a time. Pinned by `ruling_the_three_heads_share_one_top_offset` and
     `ruling_no_fixed_status_reserve_the_inset_is_the_shell_roots`
     (`components/kit/tablet_shell.rs`, which reads this file). */
  padding-top: env(safe-area-inset-top, 0px);
  /* ⚠️ THE CONSOLE TAKES THE SAME DARK GROUND AS THE TWO SHOOTER SHELLS, and
     this is the element that paints it — the viewport-sized frame, never the
     region that moves. A background is painted on its element's own padding box
     and is fixed to it (`background-attachment: scroll` means fixed with regard
     to the ELEMENT, not scrolling with its contents), so `center / cover` here
     holds the texture still with no `background-attachment`. What the frame's
     `100dvh` + `overflow: hidden` buy is a box that does not GROW with the
     page, which is the other half — see `.rd-pro-page`.

     Two children of this frame are therefore transparent rather than
     re-painting: `.rd-pro-page`, the console's one scroller, and
     `.rd-pro__closed`, the closed-org banner (`adm0-picker.css`). The value,
     the token split and the attachment argument are in `kit-shell.css` §0b,
     where `--rd-app-ground` is declared. */
  background: var(--rd-app-ground);
}

/* min-width:0 is load-bearing: a flex child defaults to min-width:auto and
   refuses to shrink below its content, so one wide table would push the rail
   off-screen instead of scrolling itself.

   `min-height: 0` IS THE SAME DECLARATION ON THE OTHER AXIS, and it is what
   carries the frame's now-definite height DOWN to the page. This column is a
   flex ITEM of `.rd-pro` (a row) and a flex CONTAINER for the page (a column);
   its own children take `min-height: auto` in the block axis and refuse to
   shrink below their content, so without this line a long page grows the
   column past the frame it was just bounded to and only the CLIP above stops
   it — the page would be cut off instead of scrolling. Same line, same reason,
   as `.rd-tshell__col` in kit-tablet-shell.css.

   ⚠️ AND NO `overflow` HERE, deliberately. `.rd-tshell__col` carries
   `overflow: hidden`; this column must not, because the closed-org banner and
   the page are siblings.

   ⚠️ **CORRECTED 2026-08-25.** This used to give the console's confirm
   modals as the reason: "the console's confirm modals are `position:
   absolute` scrims that resolve against whichever page region declared
   `position: relative` (`.rd-adm-lib`, `.rd-mem-body`, `.rd-plat` — each says
   so in its own part). A clip here would be a second answer to a question
   those parts already answer." `.rd-confirm` is `position: fixed` now
   (adm1-5-1.css), and export 27 moved `position: relative` off every Pro
   content pane and onto `ProShell` itself (`rd-pro-kit.jsx:326`) — so a
   `position: fixed` scrim is no longer clipped by an ancestor's `overflow`
   at all (fixed escapes ordinary overflow clipping; only `transform`,
   `filter`, `contain`, `will-change` or `perspective` on an ancestor would
   change that, and none is declared here — see
   `ruling_no_console_shell_ancestor_captures_the_fixed_scrim` in
   `confirm_modal.rs`). The three page regions named above still carry
   `position: relative` as a kept reversal path, not as a live containing
   block for any confirm scrim. The frame clips; the page scrolls; nothing in
   between does either — that conclusion is unchanged, only its stated reason
   was retired. */
.rd-pro__main {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;

  /* ⚠️ THE 1600 CAP — `31 (Desktop) Pro Shell`, "The desktop growth question,
     ruled 18 Aug", quoted in full at the head of this file. The rail plus this
     column stop at 1600; past that the surplus is ground.

     THE RAIL IS PINNED LEFT, and that half is not an inference — it is James's
     own correction on staging, 2026-08-19, after the shooter shell shipped the
     centred reading and he caught the rail floating ~165px inboard of the
     window edge on a ~1930px viewport. "Content centres on ink past that"
     means the CONTENT PANE'S INK — the measure of the text and cards inside
     the column — never the chrome. Both drawings pin the rail flush and
     neither centres anything: `TabShell` (`rd-tablet-kit.jsx:319`-`:327`) and
     its console twin `ProShell` (`rd-pro-kit.jsx:324`-`:331`) are
     `width: '100%', height: '100%', display: 'flex'` with the rail as the
     first child — no `maxWidth`, no `margin`, no `justifyContent`. A floating
     rail is drawn nowhere in the corpus.

       [ rail 208, fixed | main column, up to 1392 ][ ground ]
        \________ capped pair, flush left _________/

     ⚠️ THE CAP IS ON THE COLUMN HERE AND ON A FRAME ELEMENT IN THE SHOOTER
     SHELL, and the difference is markup, not geometry. `.rd-tshell__frame`
     exists because that shell caps rail + column as one block and would
     otherwise carry the rail's width a second time as a literal
     (kit-tablet-shell.css §3a: "no second literal to keep in sync with the
     rail's"). Here the rail's width is already a custom property on `.rd-pro`,
     so `calc()` reads the ONE declaration of it — the drift the frame element
     was introduced to prevent cannot happen through a token. Changing
     `--rd-pro-rail-w` moves both edges together, and `ProShell`'s markup is
     unchanged: the drawing has two children under the shell, and so do we.

     ⚠️ NO `justify-content` ON `.rd-pro`, AND IT MUST NOT ACQUIRE ONE. The
     free space past the cap lands on the end edge because a flex row's default
     `flex-start` puts it there, which is what keeps the rail on the shell's
     start edge at every width. `justify-content: center` is UNSAFE alignment
     per the CSS box-alignment spec — an overflowing item can be pushed past
     the container's start edge and become unreachable — and it would also be
     the overturned centred reading coming back.

     Inert below 1600 (a bare `max-width` names no floor), so the 1280 canvas
     and every narrower tablet width are untouched. Pinned by
     `ruling_the_console_caps_at_sixteen_hundred_with_the_rail_pinned_left`
     (components/pro/shell.rs). */
  max-width: calc(1600px - var(--rd-pro-rail-w));
}

/* ---------------------------------------------------------------------------
   The rail — 208px of words. No glyphs, by ruling (rd-pro-kit.jsx:13-18): the
   icon set has no calendar, gear or library mark, and a desktop rail is a list
   of words with room to be read rather than a tab bar.
   --------------------------------------------------------------------------- */

.rd-pro-rail {
  width: var(--rd-pro-rail-w);
  flex: none;
  background: var(--rd-surf);
  border-right: 1px solid var(--rd-line);
  display: flex;
  flex-direction: column;
}

/* ⚠️ HARD 76, NOT A FLOOR — ruled 27 Aug, upgraded from the 21 Aug min-height
   ruling via a relay note, not an edit to `48 Kit Rules.html` itself (see the
   doc comment above `.rd-pro-top` below for the full history). `rd-pro-kit.jsx:118-121`:
   "PINNED TO THE HEADER BAR (James, 27 Aug): a hard 76, bottom-aligned — the
   same metrics as ProTop — so the rail's org block and every page heading
   share one baseline and their bottom borders draw one continuous line
   across the shell, on every Pro view." Content that doesn't fit gets
   redesigned, never a taller bar — this rule asserts the ABSENCE of
   `min-height` as well as the presence of `height`, same as `.rd-pro-top`,
   because a floor left beside a cap at the same number is the cap written
   twice. Pinned by
   `ruling_the_rail_head_pins_a_hard_seventy_six_with_the_page_head`
   (components/pro/shell.rs), which supersedes
   `ruling_the_rail_head_is_pinned_to_the_page_head`. */
.rd-pro-rail__head {
  flex: none;
  height: 76px;
  box-sizing: border-box;
  padding: 10px 16px 16px;
  border-bottom: 1px solid var(--rd-line);
  display: flex;
  align-items: flex-end;
}

/* ⚠️ TWO ROWS, NOT ONE — the drawing's own structure, restored 28 Aug 2026.
   `rd-pro-kit.jsx` OrgHead is a bar (`height: 76`, `alignItems: 'flex-end'`)
   holding ONE content block (`{ flex: 1, minWidth: 0, display: 'flex',
   alignItems: 'center', gap: 11 }`), and the mark, the name column and the
   chevron are centred against EACH OTHER inside that block. The build had
   collapsed the two into one row, so `flex-end` bottom-aligned every child
   individually: the mark and the chevron sank to the org role's last line
   instead of centring on the name column.

   Invisible until C-44.1 (27 Aug) let the org name wrap. On one line the column
   is ~33px against the mark's 34 and bottom-aligned is centred to the pixel; at
   two lines it is ~48.5px and the mark sits ~7px low — the rehearsal screenshot
   James filed against DICKSON COUNTY SHERIFF'S OFFICE.

   The bottom alignment of the BLOCK is untouched, and it is the half the hard-76
   ruling is about: the block still sits on the bar's bottom padding edge, so the
   rail's border and the page head's still draw one line. */
.rd-pro-rail__head-body {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 11px;
}

.rd-pro-rail__org { flex: 1; min-width: 0; }

/* ⚠️ **TWO LINES, WRAPPED — NEVER ELLIPSISED.** This rule used to read *"One
   line, ellipsised. An org name is user-supplied and the rail is 208px."* The
   208px rail is still the reason the name is at risk; it is no longer the reason
   to cut it. C-44.1 (ruled 2026-08-27) settles the case this rule was answering:
   `44 The Doors.html:50` — *"the name must survive the 208px rail untruncated,
   since naming the destination is the fixture's whole point — and where a name
   still overruns, it wraps to two lines; it is never cut."*

   The sentence is about the door's name line and it governs this one, because
   the door exists to preview this header: *"the org's initials in accent, the
   same mark the Pro rail header wears — so the door previews the header you will
   land under"* (`44 The Doors.html`, row 01, quoted in `switch_door.rs`). A door
   that wraps into a header that clips would preview a lie.

   The bound is `.rd-clamp-2` (kit-clamp.css) at the call site
   (`components/pro/shell.rs`), never a quartet re-declared here. adm0-chrome.css
   sits ABOVE kit-clamp.css in `order.txt` (134 vs 329), so the utility wins on
   source order — but this rule stays silent about `display` and `overflow`
   anyway, so the two cannot argue.

   `title` stays on the element: two lines still bound a pathological name.

   ⚠️ RE-CHECKED AGAINST THE HARD 76 (this rule's own ruling, above), NOT LEFT
   UNVERIFIED. Content box under `.rd-pro-rail__head` is the same 49px as
   `.rd-pro-top`'s (76 - 10 - 16 - 1 border). Two wrapped lines of the org name
   at 13.5px / line-height 1.15 run 2 * 15.525 = 31.05px — not the ~15.5px a
   single line took when this box was last measured. The org-role line under it
   (9px, no explicit `line-height`, so it inherits body's 1.5 = 13.5px) is
   unchanged, a fixed short string from `ROLE_LABEL`, not user text. Full
   column: 31.05 + 4 margin + 13.5 = 48.55px against the 49px budget — 0.45px
   of headroom, nothing like the ~15px this comment used to report for a single
   line. Unlike the page head's title+sub, this one still needs no tablet-shell
   borrow to fit, but the margin is now the same order as `.rd-pro-top`'s and a
   third wrapped line, or any growth of the 76 box, would not have room to
   spare. */
.rd-pro-rail__org-name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .03em;
  font-size: 13.5px;
  line-height: 1.15;
  color: var(--rd-fg);
  overflow-wrap: anywhere;
}

.rd-pro-rail__org-role {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9px;
  color: var(--rd-fg3);
  margin-top: 4px;
}

.rd-pro-rail__chev { flex: none; color: var(--rd-fg3); font-size: 11px; }

.rd-pro-rail__list {
  flex: 1;
  min-height: 0;
  padding: 10px 0 0;
  margin: 0;
  list-style: none;
}

/* The row, `rd-pro-kit.jsx:136` ("minHeight: 56").

   ⚠️ TWO LINES AND THE SHOOTER RAIL'S METRICS, ruled 21 Aug. The drawing states
   it at the component (`rd-pro-kit.jsx:132-135`): *"56 and a 3px edge — the
   shooter tablet rail's row metrics (rd-tablet-kit TabRail), shared under the
   same 21 Aug ruling as the width. Sub-lines are PRO_NAV_SUB descriptions,
   matching the shooter rail's two-line anatomy."* `48 Kit Rules.html`, Shell
   Geometry, states it once for both apps: *"rail rows 56 min with a 3px active
   edge"*.

   `min-height`, not `height`: the sub-line is a second line and a row that
   cannot grow clips it. The shooter rail's `.rd-trail__row` is the same shape.

   The type stays on the row and the sub-line overrides it below, which is what
   the drawing does — the label span carries no colour of its own and the sub
   names `t.fg3` explicitly. */
.rd-pro-rail__item {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  min-height: 56px;
  padding: 9px 16px;
  box-sizing: border-box;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  font-weight: 400;
  color: var(--rd-fg2);
  text-decoration: none;
  border-left: 3px solid transparent;
  background: transparent;
}

.rd-pro-rail__item:hover { color: var(--rd-fg); }

/* The row's second line — `rd-pro-kit.jsx:138`
   ("fontSize: 11, color: t.fg3"): `...pbody, fontSize: 11, color: t.fg3,
   lineHeight: 1.3`. `pbody` is font-family only, so no `font-stretch` is
   authored and 100 arrives as the CSS initial — the body role.

   The colour and the weight are declared rather than inherited ON PURPOSE: the
   lit row sets `color: var(--rd-fg)` and `font-weight: 600` on the row, and a
   sub-line that inherited them would go full-strength and semibold on exactly
   the row where it is meant to stay quiet. The drawing names `t.fg3` on the sub
   for the same reason. */
.rd-pro-rail__sub {
  font-size: 11px;
  line-height: 1.3;
  font-weight: 400;
  color: var(--rd-fg3);
}

/* The lit row. Ground + a 3px accent edge + weight, so the state survives
   greyscale and does not rest on colour alone.

   ⚠️ 2px -> 3px, and the transparent edge on the base row above is what stops
   lighting a row from shifting its own words 3px right. Same ruling as the
   height: `48 Kit Rules.html`, Shell Geometry, *"rail rows 56 min with a 3px
   active edge"*; the shooter rail already ships 3px (`kit-tablet-rail.css`). */
.rd-pro-rail__item--on {
  background: var(--rd-surf2);
  border-left-color: var(--rd-acc);
  color: var(--rd-fg);
  font-weight: 600;
}

.rd-pro-rail__item:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* The rail's foot was four rules here — the padded strip, the row, its hover and
   its arrow. All four are DELETED (2026-08-20, `44 The Doors.html`): the foot is
   now `RdSwitchDoor`, whose part is `kit-switch-door.css`, and the arrow the
   fourth one coloured is the thing that document retires. Deleted rather than
   left behind, which is the only edit this unowned pool accepts. */

/* ---------------------------------------------------------------------------
   The org mark — the org's uploaded logo, or its initials in a chamfered square.

   This banner used to read "No logo slot: no such column exists in the schema
   (rd-pro-kit.jsx:105-107)", and that was true when it was written. The
   2026-08-28 ruling (James) carved the column — Org.logo_url on the wire, a
   sixth media target, the media/public/orgs/ scope behind it — so the refusal's
   own premise is gone and the slot is ruled IN. Corrected rather than deleted:
   a reader who found the img rule below sitting next to the old refusal would
   correctly delete the rule as an unbacked promise.

   ⚠️ A LOGO REPLACES THE CHAMFERED BOX (James, same day, second ruling): "an
   image should replace the chamfered box, not fit within it", and "whatever the
   user uploads is what we'll use". So there are TWO boxes here, not one box with
   two contents. .rd-pro-mark__face is the chamfer's inner layer and belongs to
   the INITIALS branch alone; the logo branch draws only the img, in the same
   --rd-pro-mark-size box carrying no ground, no border and no clip. object-fit
   is contain and not cover, which is the "whatever the user uploads" half —
   a non-square asset letterboxes rather than losing its edges.

   A logo with a white ground therefore draws a white tile in dark mode. That is
   no longer a flagged gap: it is the ruling. No inset line, no knock-out, no
   ground of our own — the upload is the mark.
   --------------------------------------------------------------------------- */

/* ⚠️ ONE GEOMETRY, BOTH BRANCHES — AND THE ONE CHILD FILLS IT.
   The size box was already the same element either way; what was NOT the same
   was what the child did inside it, and that is the 28 Aug rehearsal bug James
   reported: with a logo uploaded, the rail's mark sat at the BOTTOM of the head
   block while the abbreviation appeared centred.

   The image was never the fault. `.rd-pro-mark` was a BLOCK, so:
   - the logo branch's img (`display: block; height: 100%`) filled all 34px, and
   - the initials branch's `.rd-box` — a plain block with no width or height of
     its own — collapsed to the initials' line box (~21px at the 34px size) and
     sat at the TOP of it, because `.rd-pro-mark__face`'s `height: 100%`
     resolves to `auto` against an auto-height parent.
   A 21px square top-aligned inside a bottom-aligned 34px box lands within a
   pixel of the text column's centre by arithmetic accident, which is the whole
   reason this read as a logo-only fault. Both marks were mis-aligned; only one
   of them looked it.

   `display: grid` and not `display: flex`: the one child has to fill the box on
   BOTH axes, and a flex item is stretched only across the cross axis — a flex
   row would leave `.rd-box` at its text's width and a flex column at its
   height. A single implicit cell stretches it in both, so the chamfer draws the
   drawn square (`rd-pro-kit.jsx:109`, a `size` x `size` bordered box) and the
   img keeps the box it already filled. The stretch is stated rather than left
   to the initial value because it is load-bearing, and because
   `.rd-pro-mark__face`'s `height: 100%` has nothing to resolve against without
   it. The centring of the CONTENT is `.rd-pro-mark__face`'s, below, and the
   centring of the whole mark against the name is the row's — see
   `.rd-pro-rail__head-body`. */
.rd-pro-mark {
  flex: none;
  width: var(--rd-pro-mark-size);
  height: var(--rd-pro-mark-size);
  display: grid;
  align-items: stretch;
  justify-items: stretch;
}

/* Stated, not inherited. `.rd-pro-mark` carries no ground today, and if it ever
   grows one the initials box wants it and the logo box must not have it — a
   ground behind the image is the chamfered box coming back by another name. */
.rd-pro-mark--logo { background: none; border: 0; }

/* The chamfer's inner layer — the INITIALS branch only. */
.rd-pro-mark__face {
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
}

.rd-pro-mark__img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

.rd-pro-mark__text {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .04em;
  font-size: var(--rd-pro-mark-type);
  color: var(--rd-fg2);
}

.rd-pro-mark__text--on { color: var(--rd-acc); }

/* ---------------------------------------------------------------------------
   The page head. Its action is VOLT, not ACC — in light mode acc is the deep warm
   brown reserved for accent TEXT, and using it as a fill puts black on 2.3:1
   (rd-pro-kit.jsx:229-230). The events status chip uses acc as text+border and
   is also correct; the two are different tans on purpose.
   --------------------------------------------------------------------------- */

/* ⚠️ THE BAR IS A HARD 76, BOTTOM-ALIGNED — ruled 27 Aug, upgraded from the
   21 Aug min-height ruling. The 21 Aug text stood here for three days: *"76
   minimum, bottom-aligned — the shooter tablet header's metrics (TabShell),
   shared 21 Aug. The sub grows the bar below 76 exactly as it does there."*
   `48 Kit Rules.html`, Shell Geometry, carried the same word — *"header bars
   76 min, bottom-aligned (shooter TabShell, Pro ProTop)"* — and on that
   reading the two builds disagreed: the shooter's `.rd-tshell__head` pinned a
   fixed `height: 76px` while Pro built the literal minimum, and the designer
   confirm at the time was that the shooter's fixed height was the one off the
   corpus text.

   James reversed that 27 Aug — a relay note, not an edit to `48 Kit
   Rules.html` itself, whose Shell Geometry paragraph still reads "76 min"
   (`design-reply-2026-08-27.md`, "48 — Kit Rules", Shell Lab: "Header bar:
   HARD 76 — upgraded from 76-minimum in `rd-pro-kit` (ProTop, OrgHead) and
   `rd-tablet-kit` (TabShell). Content that doesn't fit gets redesigned, never
   a taller bar"). `rd-pro-kit.jsx:228-230` now draws `height: 76` with the
   same comment. So Pro now matches the shooter's `.rd-tshell__head`, which
   was right all along — a lane that reads this as "tidying a unit back to
   height" is not wrong this time, but it is reading a REVERSAL, not the
   original ruling; see `ruling_the_page_head_is_a_hard_seventy_six_bottom_aligned`
   (components/pro/shell.rs), which supersedes
   `ruling_the_page_head_is_seventy_six_minimum_bottom_aligned` and asserts the
   ABSENCE of `min-height` as well as the presence of `height` — a floor left
   beside a cap at the same number is the cap written twice.

   `align-items: flex-end` still matters with a hard height: it is what
   bottom-anchors the title (and the now-required sub, per #1276/#1277) to the
   bar's baseline rather than centering or top-aligning it. `26px 32px 20px`
   -> `10px 32px 16px` (`rd-pro-kit.jsx:231`, unchanged by this ruling).

   ⚠️ FITTED, BORROWING THE TABLET SHELL'S TREATMENT — FLAGGED AS A RULING
   ITEM, NOT INVENTED. This box did not fit as shipped: `.rd-pro-top__title`
   carried no `white-space: nowrap` (unlike the shooter's `.rd-tshell__title`,
   which does), and `.rd-pro-top__sub` carried no explicit `line-height`
   (unlike `.rd-tshell__sub`'s 1.25) plus a `margin-top: 9px` that was drift
   from the drawing's `marginTop: 6` (`rd-pro-kit.jsx:234`). Both are now
   fixed by mirroring `.rd-tshell__title` / `.rd-tshell__sub`'s already-ruled
   numbers (see the comments on `.rd-pro-top__title` and `.rd-pro-top__sub`
   below) rather than by guessing new ones — the designer has not named a
   value for this specific pair, only for its tablet sibling, so "borrowed"
   is the honest word for it, not "ruled". Arithmetic: 26 (title, line-height
   1) + 6 (margin, restored to the drawn value) + 15.625 (sub, 12.5px at
   line-height 1.25) = 47.625, against this rule's 49px content box
   (76 - 10 - 16 - 1 border) — 1.375px of headroom. Pinned by
   `ruling_the_page_head_title_and_sub_fit_the_hard_seventy_six`
   (components/pro/shell.rs). */
.rd-pro-top {
  flex: none;
  height: 76px;
  box-sizing: border-box;
  padding: 10px 32px 16px;
  border-bottom: 1px solid var(--rd-line);
  display: flex;
  align-items: flex-end;
  gap: 20px;
}

.rd-pro-top__lead { flex: 1; min-width: 0; }

.rd-pro-top__title {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 72%;
  letter-spacing: .02em;
  /* 27 -> 26, `rd-pro-kit.jsx:226` ("fontSize: 26"), the same 21 Aug redraw
     that bottom-aligned the bar. The PHONE frame still draws 27
     (`rd-pro-mobile.jsx:53`, `TH1 size={27}`) and keeps it by an override in
     `adm0-mobile.css` — one number changed, one frame, deliberately. */
  font-size: 26px;
  line-height: 1;
  color: var(--rd-fg);
  margin: 0;
  /* ⚠️ SINGLE-LINE, BORROWED FROM THE TABLET SHELL PENDING THE DESIGNER'S
     OWN NUMBER — flagged in the PR as a ruling item, not invented. A Pro
     page title is a screen name of unbounded length (event/org/member
     names), and nothing here constrained it before this line — the hard
     `height: 76px` bar above has no room for a second title line the way a
     `min-height` bar did. `.rd-tshell__title` (`kit-tablet-shell.css:496-510`)
     already carries exactly this treatment for the same reason ("the drawing
     never exercises it ... so this is a defect fixed, not ported"); mirrored
     here rather than left as a fresh unanswered question. Pinned by
     `ruling_the_page_head_title_and_sub_fit_the_hard_seventy_six`
     (components/pro/shell.rs). */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rd-pro-top__sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg3);
  /* ⚠️ 9px -> 6px: DRIFT FROM THE DRAWING, FOUND WHILE FITTING THE HARD 76.
     `rd-pro-kit.jsx:234` draws `marginTop: 6`; this rule shipped `9` with no
     comment explaining the difference. Restored to the drawn value because
     the hard cap has no headroom to spare: at 9px this box does not fit (see
     the line-height note below) and at 6px — the drawn number — it does,
     with margin left over. Not a designer call, a corpus-fidelity fix. */
  margin-top: 6px;
  /* ⚠️ LINE-HEIGHT BORROWED FROM THE TABLET SHELL, PENDING THE DESIGNER'S OWN
     NUMBER — flagged in the PR as a ruling item, not invented. Without an
     explicit `line-height` this inherits `body { line-height: 1.5 }`
     (`_legacy-base.css`), putting the 12.5px sub in an 18.75px line box:
     26 (title) + 6 (margin) + 18.75 = 50.75, over the 49px content box
     (76 - 10 - 16 - 1 border) even after the margin fix above. `1.25` is
     `.rd-tshell__sub`'s own value for the identical reason
     (`kit-tablet-shell.css:524-546`, pinned by
     `the_header_subtitle_declares_a_line_height`) — mirrored here rather than
     guessed, and it fits: 26 + 6 + 15.625 = 47.625, 1.375px of headroom.
     Pinned by `ruling_the_page_head_title_and_sub_fit_the_hard_seventy_six`
     (components/pro/shell.rs). */
  line-height: 1.25;
}

/* The sub-line's IN-FLIGHT stand-in, and the reason it carries a height.

   James, 2026-08-27: no page header without a sub-line, ever. A head whose line
   is still loading holds the slot with the kit's line skeleton
   (`43 Loading States.html:40` — loading TEXT is not a treatment), and the box
   is floored at the line box the 12.5px type stands in so the head is exactly
   as tall before the load as after it. `em`, not a pixel figure, because
   `adm0-mobile.css` re-sizes this rule's type to 11px under `.rd-prom` and the
   floor has to follow it there rather than being restated.

   ⚠️ 1.2em -> 1.25em: the settled rule below now declares `line-height: 1.25`
   (borrowed from `.rd-tshell__sub`, see that rule's comment) and this floor
   has to track it exactly, the same way `.rd-tshell__sub--wait`'s does for
   its own settled line — `1.2em` predates that line-height existing at all,
   back when the settled line had none of its own and this was only ever an
   approximation. At `1.2em` the floor was 15px against a 15.625px settled
   line (12.5 * 1.25) — 0.625px of exactly the bounce this box exists to
   prevent. At `1.25em` it is 15.625px, matching to the pixel. */
.rd-pro-top__sub--pending {
  display: flex;
  align-items: center;
  min-height: 1.25em;
  /* The BOX is sized here, never the kit's mark: `.rd-skel--line` is 82% of
     whatever holds it, and the lead is `flex: 1`, so an unconstrained slot
     would draw a placeholder the width of the console for a line that reads
     `T-Rex Arms`. 220px is a sub-line's rough measure at 12.5px; the mark stays
     the kit's 82% of it. `max-width` so a narrow frame shrinks it rather than
     overflowing. */
  width: 220px;
  max-width: 100%;
}

.rd-pro-top__action { flex: none; }

/* ---------------------------------------------------------------------------
   THE FORM FOOT ACT ROW — `ProFormActs` (`components/pro/form_acts.rs`).

   James, staging `/admin/drills/new`, 2026-08-27: the primary act lived only in
   the bar above and `Cancel` lived at the foot, so on a long form saving meant
   scrolling back up. The commit now sits BESIDE the back-out, at the foot, in
   the part of the page that scrolls with the fields.

   ⚠️ IT LIVES HERE, IN THE PAGE-HEAD PART, ON PURPOSE. This row is the other
   half of `.rd-pro-top__action` — the same one primary act, relocated — and the
   `48 Kit Rules.html` Action Tiers rule that governs it ("one volt-filled role
   per screen") is a statement about the two of them TOGETHER. Splitting them
   across two files would put the two halves of one rule where neither reader
   sees the other. It is also why this is not a new part: a form's foot is page
   chrome, and page chrome already has a file with an owner.

   ORDER: `Cancel` left, primary right. `48 Kit Rules.html`, *Games (55 Band)*:
   *"The committing act (SHOOT) sits RIGHT on the trigger side … the restraint
   (HOLD) sits LEFT"* — the corpus's one ruling on where a commit sits beside a
   back-out. The Pro MODAL foot draws the reverse and keeps it; see the module
   header for why the two are not in conflict.

   NO PAINT. Every control in this row keeps the face its page already gave it —
   `.rd-pro-top__btn` for the primary, the page's own quiet outlined button for
   the rest. This rule set is geometry and nothing else, so a page adopting the
   row cannot pick up a second spelling of a button it already has.

   `space-between` puts the destructive act at the far edge, away from the pair:
   the same separation `.rd-drillf__actions` carried before it, which was the
   one thing about that row worth keeping.
   --------------------------------------------------------------------------- */
.rd-pro-acts {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-top: 4px;
}

/* The pair is its own box so `space-between` cannot get between its two
   members — a flat row would fling the primary to the middle and rebuild the
   defect sideways. `flex-wrap` so a narrow console pane stacks the two rather
   than overflowing; they keep their order either way. */
.rd-pro-acts__pair {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
}

.rd-pro-acts__aside {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* ---------------------------------------------------------------------------
   THE PUSHED SCREEN'S BACK ROW, AT CONSOLE WIDTH — UNDER THE HEAD, IN THE
   HEAD'S GUTTER, AT THE KIT'S RHYTHM. NOTHING ELSE.

   ⚠️ **RULING — JAMES, REHEARSAL 2026-08-29: THE HEADER COMES FIRST, AT ITS
   HARD 76, AND THE BACK ROW SITS UNDER IT.** `OrgHead` and `ProTop` are both
   a hard 76 so that the rail's bottom border and the page head's draw ONE
   continuous line across the shell (§2.1). Until the 29th `ProPushedScreen`
   (`components/pro/pushed.rs`) put the row ABOVE the head — doc 49's phone
   order — which pushed the head's hairline down by the row's height on every
   pushed console screen (`/admin/progress/:id?from=…`), and the two lines no
   longer met. That was the open question this block carried (#1347,
   `ADM0-chrome.md` §3.1, relay item 68). James answered it: head first, back
   row under it, content after — on every frame, and the fixture builds that
   order once. The hairlines meet again.

   ⚠️ **THE ROW IS NOT IN THE BAR ABOVE, AND THIS RULE DOES NOT PUT IT THERE.**
   `ProTop` has no back slot (`components/pro/top.rs`: *"There is no back slot,
   and that is drawn rather than omitted"*). The row is a `Link` that is a
   SIBLING of `.rd-pro-top`, never a child — and the hard 76 above stays a hard
   76 whether the row is there or not (`.rd-pro-top` is `flex: none` with a
   definite `height`, so a sibling cannot grow it).

   THE GUTTER — 32px IS `.rd-pro-top`'s OWN, read off the rule above rather
   than chosen. James, staging, 2026-08-28: on `/admin/progress/:id?from=members`
   the back control *"renders top-left inside the page header, above the
   member's name"*. Structurally it never was inside it; it READ that way
   because `.rd-prom__back` declares no horizontal inset (`adm-evd-mob.css` §6
   — both phone roots pad 18px, `.rd-prom` and `.rd-prom-bare`) and
   `.rd-pro-page` is a bare flex column (no padding: the head carries its own
   32px and every page region carries its own), so the row landed at x=0 while
   the `<h1>` started at 32px. The back label and the title now stand on one
   left edge.

   THE RHYTHM — 18px IS THE KIT'S HEAD-TO-FIRST-REGION DISTANCE, not a new
   number: `rd-pro-kit.jsx:300` draws `ProTabs` as `'18px 32px 0'`, the first
   thing under a `ProTop` on every tabbed Pro page, and `adm-lib.css`'s
   `.rd-adm-lib__search` carries the same `18px 32px 0` for the same reason.
   Now that the row sits under the head's hairline it is the head's first
   region, so it takes that 18 from the hairline and gives the same 18 to the
   content under it. The whole `padding` is restated here rather than the two
   horizontal edges, because the phone's vertical 12 (the phone frame's own
   head-to-first-row distance) is the wrong number on this frame.

   No height, no hairline, no ground: the row is otherwise UNDRAWN at console
   width — `37 (Desktop) Pro Shooter Progress` draws `ProShell` + `ProTop` and
   no back row; its type and chevron are the phone's 9.5px carried up, and the
   designer question that remains is to draw the console row's own (relay
   item 68).

   ⚠️ SELECTOR: `.rd-pro__main` is the DESKTOP arm's content column, mounted
   only by `ProShell` (`components/pro/shell.rs`, `admin/layout.rs`'s desktop
   branch). No `@media`: the breakpoint is asked once, in Rust
   (`device::use_device_class`), the same call every part in this pool makes.
   Scoping on `.rd-prom` instead would have been wrong in a way that is easy to
   miss — the RO Range Day screens mount `ProPushedScreen` under
   `.rd-prom-bare`, which is a PHONE root that is not `.rd-prom`, so a
   `.rd-prom`-scoped phone rule with a console default would have handed those
   three screens the console's numbers on a phone. */
.rd-pro__main .rd-prom__back {
  padding: 18px 32px;
}

/* ---------------------------------------------------------------------------
   ⚠️ **THE ACCOUNT STRIP IS GONE — `.rd-pro__account`, `.rd-pro__who` and
   `.rd-pro__signout` WERE HERE AND DO NOT COME BACK.**

   It was undrawn: the signed-in name beside a sign-out button, invented above
   the page head because the drawings assume a session and never draw a way out. James
   ruled it out (27 Aug). The corpus puts identity in the rail head
   (`rd-pro-kit.jsx`'s `OrgHead` — org name over role) and makes the RailFoot
   "Switch to → Range Day" fixture the console's only exit
   (`rd-pro-kit.jsx:159-183`); signing out is a shooter-side act and lives on
   Edit Account's Sign Out row.

   Its removal also puts `.rd-pro-top` back at the top of `.rd-pro__main`, which
   is what lets the rail head's bottom border and the page head's draw one
   continuous line across the shell — the reason `OrgHead` and `ProTop` share
   the 76 minimum in the first place (`48 Kit Rules.html`, Shell Geometry). A
   row reintroduced here would break that line again.
   --------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
   The page ground.

   ⚠️ **`.rd-pro__legacy` IS GONE — do not put it back.** ADM0 wrapped the
   console's `Outlet` in a fixed light island (#f9fafb / #111827) because the
   old top bar had been `min-h-screen bg-gray-50` and every admin page's greys
   were picked against it; inside a themed frame those pages' near-black
   headings would otherwise land on --rd-app-bg and vanish in the DARK theme,
   which is the default. That rule carried a stated removal trigger — "the last
   admin page stops needing a light ground" — and the trigger has fired. Every
   routed admin page now either carries the class below or paints its own
   full-viewport ground (`pages/stage_editor.rs`), so the island and its div in
   `admin/layout.rs` were deleted together.

   What is left is this one rule, and it is no longer an "opt-out" of anything:
   it is simply how an admin page claims the frame's main column. It keeps the
   flex box the island used to supply — `.rd-pro__main` is a flex column, and a
   page that does not grow sizes to its content and leaves a band of frame
   below it, which is exactly what the island's first render showed.

   ⚠️ It must keep RESTORING THE INK rather than declaring nothing. The pages
   are kit-styled and theme-aware; --rd-fg is what they are drawn in, and
   inheriting whatever the frame happens to hold is how the white-on-white
   defect arrived the first time. It is named here on purpose.

   ⚠️ AND THE GROUND HALF IS NOW `transparent`, WHICH IS A CHANGE OF VALUE AND
   NOT A CHANGE OF POSITION. This used to say `background: var(--rd-app-bg)`,
   from when that token was the console's whole ground. Since the dark theme
   gained the concrete texture (`--rd-app-ground`, `kit-shell.css` §0b), the
   ground is painted by `.rd-pro` — the 100dvh, `overflow: hidden` frame — and
   THIS element is the console's one SCROLLER. Repainting the ground here would
   do one of two wrong things: an opaque flat colour hides the photograph on
   every console screen, and a second copy of `--rd-app-ground` would size
   `cover` to a box that grows with the page's content and scrolls with it, so
   the texture would stretch and slide. Transparent is what lets the frame's
   still ground show through the region that moves.

   `transparent` is safe in BOTH of this class's mountings, which is why it is
   unconditional: on the desktop console the box behind it is `.rd-pro`, and on
   the phone it is `.rd-prom` (`adm0-mobile.css`), which paints the flat
   `--rd-app-bg`. Neither is "whatever the frame happens to hold" — both are the
   app ground the pages are drawn against, which is the thing the paragraph
   above actually asks for.
   --------------------------------------------------------------------------- */
/* ⚠️ **THIS IS THE CONSOLE'S ONE SCROLLING REGION.** `overflow: auto` here is
   the other half of the frame's `height` + `overflow: hidden` above, and the
   two are a single change: bounding the frame without giving the page a
   scroller CLIPS every long page, and giving the page a scroller without
   bounding the frame does nothing at all (an `auto` box whose height is its
   own content never overflows — which is exactly why the eight page regions
   that already declare `flex: 1; min-height: 0; overflow: auto` inside this
   one had no effect before today: `.rd-adm-lib`, `.rd-loc`, `.rd-loc-rec-area`,
   `.rd-rday`, `.rd-ev-form-area`, `.rd-ev-roster`/`.rd-ev-scores`/
   `.rd-ev-overview`. They were written against a bounded frame that did not
   exist. They work now, and on those pages the page head stays put because
   THEIR region is what scrolls, not this one).

   IT IS THE PAGE ROOT AND THERE IS EXACTLY ONE, WHICH IS WHY ONE RULE REACHES
   EVERY CONSOLE SCREEN: every routed `/admin/*` page renders this class as its
   root — pinned page-side by ruling tests in `progress.rs` and `members.rs`
   ("every return path renders inside `.rd-pro-page`") and shared by the pushed
   phone screens through `ProPushedScreen` (`components/pro/pushed.rs`). The
   one exception is `pages/stage_editor.rs`'s `.rd-se`, which is already
   `flex: 1; min-height: 0` with its own internal panes (s05-editor.css) — it
   asks for a bounded parent and, until this change, was never given one.

   ⚠️ **NARROWED 2026-08-29.** That exception is now the admin stage editor's
   PHONE arm only. At tablet width the console mount renders this class as its
   root like every other console screen, with `ProTop` above the drawn three
   columns — see `stage_editor::tablet_frame_for`. `.rd-se-tab` carries its own
   `min-height: 0` for the flex-column host this element is.

   `overflow: auto` AND NOT `overflow-y: auto`: a computed `overflow-y` of
   `auto` forces `overflow-x` to `auto` anyway, so writing the longhand would
   only hide that the horizontal axis is also being decided. Deciding it as
   `auto` is the same argument `min-width: 0` makes on `.rd-pro__main` — a
   table wider than the column scrolls itself rather than pushing the rail off
   the screen.

   THE HEAD SCROLLS WITH THE PAGE ON THE PAGES THAT HAVE NO REGION OF THEIR
   OWN, and that is unchanged behaviour, not a new decision: `ProTop` is
   mounted by each page INSIDE this element, so before today it scrolled with
   the document and now it scrolls with the page. The drawing has the head as a
   `flex: 'none'` sibling (`rd-shell-lab.jsx:40`) of a content region that is
   `flex: 1, minHeight: 0` (`rd-shell-lab.jsx:57`), i.e. outside the scroller
   and above it — reaching that
   means moving `ProTop` out of ~29 pages, which is a change of its own and is
   filed as an open question rather than smuggled in here. */
.rd-pro-page {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: auto;
  background: transparent;
  color: var(--rd-fg);
}

/* ---------------------------------------------------------------------------
   The console scrollbar on `.rd-pro-page` — RETIRED 2026-08-30.

   This part restated the WebKit pseudo-element half of adm0.css's console
   scrollbar on `.rd-pro-page` when the document stopped scrolling on console
   routes, because those pseudo-elements do not inherit. That per-element
   restatement is exactly what left every OTHER console scroller on the
   platform's default bar (James, 2026-08-30, the shooter progress record).
   The one rule for every scroller is kit-shell.css §8; nothing here selects a
   scrollbar any more.
   --------------------------------------------------------------------------- */

/* ==== ADM1-PR4 · docs/design/kit/ADM1-events.md §7 · owner: spine ==== */

/* The scores table — and it is NOT a `<table>`. §7: the same flex-row idiom as
   every other Pro list, with a header strip. Five columns, gap 18, a --rd-line
   rule under the header and under each row. */
.rd-ev-scores { flex: 1; min-height: 0; overflow: auto; padding: 18px 32px 32px; position: relative; }

.rd-ev-scores__filters { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-bottom: 18px; }

.rd-ev-scores__filter {
  border: 1px solid var(--rd-ctrl);
  background: var(--rd-field);
  color: var(--rd-fg);
  padding: 7px 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
}

.rd-ev-scores__toggle {
  display: flex; align-items: center; gap: 7px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg3);
  cursor: pointer;
}

.rd-ev-scores__head {
  display: flex;
  gap: 18px;
  padding: 0 0 10px;
  border-bottom: 1px solid var(--rd-line);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .11em;
  font-size: 9px;
  color: var(--rd-fg3);
}

.rd-ev-scores__row {
  display: flex;
  gap: 18px;
  align-items: center;
  padding: 13px 0;
  border-bottom: 1px solid var(--rd-line);
}

.rd-ev-scores__c-shooter {
  flex: 1; min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
}

/* ⚠️ **#467's second cluster, closed.** `line-through` was used by the pre-kit
   table and was UNDEFINED in this stylesheet, so a voided score was never
   actually struck through — on the one row in the product where the strike
   carries meaning rather than decoration. */
.rd-ev-scores__c-shooter--void { color: var(--rd-fg3); text-decoration: line-through; }

.rd-ev-scores__c-stage {
  flex: 1; min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg3);
}

/* Numbers are the kit's mono role: Archivo 800 / 75% with TABULAR figures, so a
   column of digits lines up instead of shimmering row to row. */
.rd-ev-scores__c-num {
  width: 90px; flex: none;
  text-align: right;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800; font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  color: var(--rd-fg);
}
.rd-ev-scores__c-num--void { color: var(--rd-fg3); }

.rd-ev-scores__c-act {
  width: 100px; flex: none;
  text-align: right;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
}

/* A voided row states its state in --rd-warn; a live row offers the verb in the
   quiet colour. The drawing puts both in the same cell so the column reads as
   one thing: what happened, or what you may do. */
.rd-ev-scores__voided { color: var(--rd-warn); }
.rd-ev-scores__act-none { color: var(--rd-fg3); }

.rd-ev-scores__void,
.rd-ev-scores__void-yes,
.rd-ev-scores__void-no {
  background: none;
  border: 0;
  padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  cursor: pointer;
}
.rd-ev-scores__void { color: var(--rd-fg3); }
.rd-ev-scores__void:hover { color: var(--rd-danger); }
.rd-ev-scores__void-yes { color: var(--rd-danger); margin-right: 10px; }
/* `Keep` draws PLAIN foreground, not muted — the drawing's call (32 Pro Events,
   ScreenEventScores: Confirm in danger, Keep in the full fg tone). It was fg3
   here while the confirm was undrawn; the 13 Aug redraw settled it. */
.rd-ev-scores__void-no { color: var(--rd-fg); }
.rd-ev-scores__void:focus-visible,
.rd-ev-scores__void-yes:focus-visible,
.rd-ev-scores__void-no:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

.rd-ev-scores__more { margin-top: 18px; text-align: center; }

.rd-ev-scores__more-btn {
  background: none;
  border: 1.5px solid var(--rd-ctrl);
  color: var(--rd-fg);
  padding: 9px 16px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 10.5px;
  cursor: pointer;
}
.rd-ev-scores__more-btn:hover { border-color: var(--rd-acc); }
.rd-ev-scores__more-btn:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

.rd-ev-scores__foot {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
  margin-top: 18px;
  line-height: 1.55;
  max-width: 66ch;
}

/* Not drawn, and kept anyway: the export writes the rows currently loaded, so on
   a paginated event it silently produces an incomplete file. Same shape as the
   roster picker's hundred-member wall. */
.rd-ev-scores__partial {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-warn);
  margin-top: 10px;
  line-height: 1.5;
  max-width: 66ch;
}

/* ==== KIT-P0 · docs/design/kit/ (per-block) · owner: spine ==== */

/* Phase 0 primitive signatures. These blocks are the MINIMUM each placeholder
   body needs to render something inert and legible — deliberately not the
   finished visuals, which land with the bodies. Every class is a whole literal
   in a `class:` string at the call site, because the CSS gate cannot see a class
   assembled in a helper (#467).

   Section convention: this file is multi-lane now. Open a block with
   `==== <SLICE-ID> · <spec-file> · owner: <lane> ====`, append only inside your
   own section, and keep removal triggers beside temporary blocks. */

/* -- RdLoading · fetching it -----------------------------------------------
   Visible-type rule for `.rd-loading` DELETED (`43 Loading States.html:40`,
   19 Aug): the honest sentence is retired to `rd-sr-only` (`kit-loading.css`,
   `.rd-loading` markup in `loading.rs`), so this rule was live only because
   `kit-p0.css` sits earlier in `order.txt` than `kit-loading.css` and lost the
   single-class tie on `.rd-sr-only` — a reorder would have made the retired
   sentence render visibly at 13px again. The rule goes with the position it
   encoded. */

/* ==== C-01.1 · docs/design/kit/00-substrate.md §3.2 · owner: spine ====

   THE RULED CHAMFER PAIR. `01 Foundations` §03, re-ruled 26 Aug 2026: the cut is
   bottom-right only (the absolute half of the rule) at 13px on a control and
   14px on a container, superseding that section's original 14/20 — 20 was never
   drawn at a single site, here or in the corpus.

   These two names exist for the hand-written clip-paths that cannot go through
   `RdChamferBox` (the skeleton, the chip, the keypad, the swatch each write the
   five-point polygon out with a literal). Everything that DOES go through the
   component takes the value as a required prop and always will — a default is
   the one mechanism that could re-cut a screen nobody opened.

   Mirrored by CONTROL_CHAMFER_PX / CONTAINER_CHAMFER_PX in
   apps/web/src/components/kit/chamfer_box.rs, which is the source of record for
   the pair, and by --rd-btn-r (_legacy-kit-chamfer-button.css) for the filled
   button's single-layer clip. All of them move together.

   ⚠️ ADDITIVE ONLY. Nothing below re-points an existing declaration, and
   --rd-btn-r keeps its literal: ZERO RENDERED PIXELS MOVE. Values drawn at other
   sizes are drift to converge as screens are touched, per the ruling — not a
   re-audit event, so no call site was re-cut.

   ⚠️ DECLARED HERE, ON `:root`, AND NOT IN `_legacy-tokens.css`. That part is
   UNOWNED · DELETE-ONLY and no new NAME may be added there — the same wall
   `kit-ok.css` hit with `--rd-ok` and `kit-shell.css` with `--rd-home-clear`,
   answered the same way. The chamfer's own part
   (`_legacy-kit-chamfer-button.css`) is under that same DELETE-ONLY rule, so an
   owned part has to hold the names; this is the kit-primitive part and the
   chamfer box is a kit primitive. No part was added — the part count is a
   hand-updated pin in `cli/src/css.rs` and an extra file would move it.

   Theme-invariant on purpose: geometry, not colour. One block, no
   `[data-theme]` sibling, because the cut does not change with the theme. */
:root {
  --rd-chamfer-control: 13px;
  --rd-chamfer-container: 14px;
}

/* ==== kit-error-list · RdErrorList, the validation-error summary · owner: spine ==== */
/* No spec and no drawing — see the component's own doc,
   apps/web/src/components/kit/error_list.rs. The values below were not
   designed here: they are the byte-for-byte-identical declarations that
   `admin/event_form.rs` (`.rd-ev-form__errors`, formerly in adm1-4.css,
   owner: spine) and `admin/drill_form.rs` (`.rd-drillf__errors`, formerly in
   adm-drillf.css, owner: lane4) each carried on their own. `RdErrorList`
   wired the two into one primitive first and left the values behind on
   purpose (a change in a sheet two lanes own is a separate diff from the
   wiring one); this is that diff. Both call sites now pass this class
   verbatim instead of their old page-local name.

   The one difference found between the two twins was `margin-bottom: 20px`
   (adm1-4.css) vs. `margin: 0 0 20px` (adm-drillf.css) — not visual: every
   element is reset to `margin: 0` globally (`_legacy-base.css`), so the two
   forms agree on every other side already and the shorthand adds nothing.
   Unified on the shorter form below. */
.rd-error-list {
  margin-bottom: 20px;
  border: 1px solid var(--rd-danger);
  padding: 11px 13px;
  list-style: disc;
  padding-left: 30px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-danger);
}
.rd-error-list li { margin: 2px 0; }
/* ==== kit-segmented · RdSegmented, the segmented single-choice control · owner: spine ==== */

/* Drawn: `PSeg` (rd-profile-kit.jsx:87), rendered by `ScreenSettings`'s Display
   group. Component: apps/web/src/components/kit/segmented.rs, which carries the
   argument for the markup shape (native radios in labels) and for the two cell
   states. Only the values live here.

   ⚠️ NO GEOMETRY IS RE-DERIVED IN THIS FILE. The cell's chamfer and its inner
   cut arrive as `--rd-box-r` / `--rd-box-ri` from `RdChamferBox`, which is the
   kit's substrate and the only place the inner-chamfer formula exists. What is
   authored below is layout, type and the focus layer — and the focus layer is a
   clip-path only because it cannot be anything else (see its own note). */

/* The row. Flex at default `align-items: stretch`, which is load-bearing rather
   than incidental: the unselected cells carry a 1px hairline and the selected
   one does not, so in isolation they differ in height by 2px. Stretch makes
   every cell take the tallest. A fixed height here would put the drawn 40px
   minimum in two places. */
.rd-seg {
  display: flex;
  gap: 6px;
}

/* One option. `flex: 1 1 0` rather than `flex: 1`, so three cells are three
   EQUAL thirds regardless of how long their words are — `Auto` and `Light`
   would otherwise take different widths and the control would look ragged.
   `position: relative` is the containing block for the absolutely positioned
   native input. */
.rd-seg__opt {
  flex: 1 1 0;
  min-width: 0;
  display: block;
  position: relative;
  cursor: pointer;
}

/* Already chosen: pressing it again does nothing, so it does not offer. */
.rd-seg__opt--sel {
  cursor: default;
}

/* The native radio: visually hidden, never `display: none`. Hiding it that way
   takes the whole keyboard contract with it — one tab stop, arrow roving,
   Space. Same construction as `.rd-swatch__input`, for the same reason. */
.rd-seg__input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  opacity: 0;
  pointer-events: none;                   /* the <label> does the hit testing */
  appearance: none;
  -webkit-appearance: none;
}

/* Third, outermost clipped layer — ALWAYS present, transparent at rest, so
   focus costs no layout shift (the idiom `.rd-swatch__focus` sets for the kit).
   A focus ring cannot be an `outline` or a `box-shadow` here: clip-path clips
   both, which is the same failure that forced the two-layer border in the first
   place.

   Geometry: pad p = 2, cell chamfer n = 9, wrapper chamfer 11. Thickness along
   the diagonal is (2p + n - 11)/sqrt(2) = 2/sqrt(2) ~ 1.41px against 2px on the
   straight edges — the same ring the swatch draws. 11 must stay BELOW
   2p + n = 13, or the wrapper clips the cell's own diagonal instead of ringing
   it; `segmented.rs` pins that constraint in a test. */
.rd-seg__focus {
  display: block;
  height: 100%;
  padding: 2px;
  box-sizing: border-box;                 /* inset, do not grow (see .rd-box) */
  background: transparent;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 11px), calc(100% - 11px) 100%, 0 100%);
}

.rd-seg__input:focus-visible ~ .rd-seg__focus {
  background: var(--rd-acc);              /* focus in this system is an --rd-acc
                                             STROKE — acc, never volt:
                                             substrate §1.1 */
}

/* The chamfer box fills its option, so the three cells stay equal thirds. */
.rd-seg__opt .rd-box {
  height: 100%;
}

/* The cell's inner surface: the drawn 40px minimum, centred, in the label axis
   (`tdisp(800, 78, '.12em')` at 10.5px). `--rd-fg2` is the unselected label
   colour; the selected one inverts below. */
.rd-seg__cell {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: 0 6px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 10.5px;
  color: var(--rd-fg2);
  text-align: center;
}

/* On volt, and only on volt. `--rd-on-volt` is black in BOTH themes because
   `--rd-volt` is the same #A89968 in both — the fill half of the acc/volt
   split. Reaching for `--rd-fg` here would be white text on tan in dark.

   The tan rebrand (export 28) changed the value and NOT the ink: black on
   #A89968 is a shorter step than black was on #CEFF51, and the designer kept
   it (`rd-train-kit.jsx:19,20`, `onVolt: '#000'` in both theme objects). */
.rd-seg__opt--sel .rd-seg__cell {
  color: var(--rd-on-volt);
}
/* ==== kit-rep-stepper · RdRepStepper, the rep-count stepper · owner: spine ==== */

/* Drawn twice, identically, and that is the point: `ScreenPlanDay`
   rd-rangeday.jsx:68-76 ("flex: 'none'") on the phone and `ScreenTabPlanDay`
   rd-tablet-relay.jsx:221-225 ("flex: 'none'") on the tablet draw the same
   cluster with one difference — the value box's minimum width. Component:
   apps/web/src/components/kit/rep_stepper.rs, which carries the ruling, the
   floor argument and the open questions. Only the values live here.

   ⚠️ CITATIONS HERE POINT AT A CORPUS NEWER THAN tmp/design/snapshot. The
   17 Aug 15:40 designer export added this stepper; the checked-out snapshot is
   the 12:09 one and predates it, and `rd-tablet-relay.jsx` is not in the ledger
   at all. Re-run docs/design/corpus-resync.md before trusting the anchors gate
   on this file.

   ⚠️ NO CHAMFER IN THIS FILE, AND THAT IS DELIBERATE. The drawn buttons carry a
   plain 1px square border, not `tcham` rd-rangeday.jsx:73 ("&minus;"). The
   corpus's three other steppers are cut differently; a chamfer added here to
   "match the kit" would be a change to the drawing, not a fix to this file. */

/* The cluster: three flush boxes, no gaps, and it does not stretch. The drawing
   sets `flex: 'none'` on this wrapper rd-rangeday.jsx:68 ("flex: 'none'") so
   the row's name column absorbs the slack and the two 44px targets keep the
   right edge. The per-drill total and the row itself belong to the caller —
   the tablet keeps that total on the row and the phone gives it up
   rd-tablet-relay.jsx:226 ("The total stays on the row here"). */
.rd-repstep {
  display: flex;
  align-items: center;
  flex: none;
}

/* One button. 44 x 44 is the iOS touch minimum and is met by the control
   itself, not by padding around it — both drawn boxes carry
   `width: 44, height: 44`: rd-rangeday.jsx:73 ("&minus;") and
   rd-rangeday.jsx:75 (">+</div>"). Everything else here is a reset: a
   `<button>` arrives with a UA background, border and font, and each of those
   would otherwise paint over a drawn value.

   Type is the display voice at `tdisp(700, 78, 0)` — Archivo 700, 78% stretch,
   15px, NO tracking. `text-transform` is inert on `-` and `+` and is kept
   anyway, because it is what the role is and a glyph that later becomes a word
   should not silently change voice. */
.rd-repstep__btn {
  width: 44px;
  height: 44px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: transparent;                /* the drawing gives these no fill */
  border: 1px solid var(--rd-ctrl);
  color: var(--rd-fg2);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: 0;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}

/* Minus at the floor. **Recoloured, never removed** — the drawing branches only
   the two colours and holds the 44 x 44 box
   — rd-rangeday.jsx:73 ("&minus;") branches `reps > 1 ? t.ctrl : t.line` on the
   border and `reps > 1 ? t.fg2 : t.fg3` on the glyph, and nothing else. A
   control that vanishes at the boundary makes the boundary look like a bug:
   rd-rangeday.jsx:69 ("Minus goes quiet at one").

   It stays a live `<button>` and stays in the tab order: the click is a no-op
   in Rust, and whether it should instead be inert is an open design question
   recorded in `rep_stepper.rs`. `cursor` is left as `pointer` for the same
   reason — a `not-allowed` cursor would answer that question in CSS. */
.rd-repstep__btn--quiet {
  border-color: var(--rd-line);
  color: var(--rd-fg3);
}

/* No drawn focus state, so this is the house treatment: the kit's 2px
   `--rd-acc` ring, `:focus-visible` so a pointer press does not leave one
   behind. The offset is NEGATIVE — the three boxes are flush, so a +2px ring
   would paint over the value box and its neighbour. Same construction and same
   reason as `.rd-scorein__stepper-btn:focus-visible` in adm0-picker.css. */
.rd-repstep__btn:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* The value. Face, weight, 75% stretch and tabular figures ride the shared
   `.rd-num` role; only the drawn size and width are set here
   rd-rangeday.jsx:74 ("minWidth: 30") — `...mono, fontSize: 15`.

   `color` is restated even though the role already supplies it, because it is
   the one property in this control that must NEVER branch on state: the minus
   button beside it dims at the floor and the value does not. Same token as the
   role's default, so this is a statement of intent rather than a second value
   to maintain. */
.rd-repstep__value {
  flex: none;
  min-width: 30px;
  text-align: center;
  font-size: 15px;
  color: var(--rd-fg);
}

/* The tablet's wider box rd-tablet-relay.jsx:223 ("minWidth: 34"). The only
   difference between the two drawings of this cluster. */
.rd-repstep__value--wide {
  min-width: 34px;
}
/* ==== kit-centered-card · RdCenteredCard, the phone card on the tablet ground · owner: spine ==== */

/* Drawn once and hand-rolled twice more, which is the argument for lifting it:
   `CenteredCard` at rd-tablet-identity.jsx:240 ("function CenteredCard") serves
   Auth and Account Deletion, and `ScreenTabScanQR` repeats the same ground and
   card by hand at rd-tablet-relay.jsx:154 ("width: 400"). Component:
   apps/web/src/components/kit/centered_card.rs, which carries the refusal the
   three documents are making, the box-sizing reading and the open questions.
   Only the values live here.

   ⚠️ THIS PART DECLARES NO COLOUR ON THE GROUND, AND THE DRAWING DOES. The
   corpus paints `background: t.bg` on the outer div
   (rd-tablet-identity.jsx:242 ("padding: 40")) because a JSX preview has no app
   ground behind it. This app has one. `.rd-gate` — the other centred
   full-screen column in this stylesheet — states the same position for the same
   reason: "It carries LAYOUT ONLY ... nothing here sets a colour". A background
   added here would paint over whatever theme surface the shell put down and
   would look correct in dark theme by coincidence.

   ⚠️ THE WIDTHS ARE OUTER WIDTHS. The corpus is React inline styles at the
   browser default `content-box`, so its `width: 420` plus `padding: 26` is a
   474px card; this app sets `box-sizing: border-box` globally
   (_legacy-base.css:22), so 420px here is the card edge to edge. The tablet
   Auth document states the number as an outer one in prose — "420px card,
   centred, unchanged from the phone" — and that is the reading implemented.
   Open question in the component's doc; do not "fix" one side alone. */

/* The ground. `min-height: 100%` and the flex centring are `.rd-gate`'s, which
   solves the identical problem one drawing over: fill the shell's content
   region, centre one column in it, own no colour. */
.rd-ccard {
  width: 100%;
  min-height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  /* rd-tablet-identity.jsx:242 ("padding: 40"). It is what keeps the card off
     the bezel on the 1194px ground, and it is why the card may be 470 wide
     without ever touching an edge. */
  padding: 40px;
  overflow: hidden;
}

/* The card. 420px is Auth's, the default of the three
   (rd-tablet-identity.jsx:281 ("w={420}")); the other two are modifiers below.

   `max-width: 100%` is NOT redundant with the ground's centring: the ground
   clips (`overflow: hidden` above), so on a 390px phone browser a 470px card
   would have its right edge cut off with no scroll to reach it. Pinned by a
   test in the component. */
.rd-ccard__card {
  width: 420px;
  max-width: 100%;
  background: var(--rd-surf);
  border: 1px solid var(--rd-line);
  /* rd-tablet-identity.jsx:243 ("padding: 26"). Scan QR's card is drawn WITHOUT
     it — the viewfinder is flush to the card edge — and that is a modifier this
     part does not yet carry, because no screen exists to check it against. */
  padding: 26px;
}

/* Scan QR — rd-tablet-relay.jsx:154 ("width: 400"). */
.rd-ccard__card--narrow { width: 400px; }

/* ⚠️ The 470px "wide" modifier rule (Account Deletion) is DELETED 2026-08-19 —
   `CardWidth::Wide` retired per design-reply-2026-08-18-relay5.md §1: "Delete
   CardWidth::Wide." Its last consumer, pages/account_gone.rs's farewell route,
   now renders a bare 340px column with no card at all
   (.rd-gone__tab/.rd-gone__tab-col/.rd-gone__tab-act in s18-foot.css) and does
   not call RdCenteredCard. History: this rule retired from the drawn corpus a
   day earlier, 2026-08-18, when 22 (Tablet) Account Deletion v0.2 ruled the
   deletion CONFIRM a modal over the profile (TabModal at 380,
   rd-tablet-identity.jsx:260) — that left the farewell as this rule's only
   remaining consumer, reached directly by §1 the following day. */
/* ==== kit-theme-swatch · RdThemeSwatch radio · owner: spine ==== */

/* Same argument as kit-segmented.css and kit-rep-stepper.css above: a
   primitive sits with the kit parts, not beside the screen that first calls
   it. `.rd-swatch__radio` and `.rd-swatch__dot` used to live in
   `_legacy-kit-components-and-screens.css`, whose own banner is UNOWNED ·
   DELETE-ONLY — "any lane may delete what its own change stranded; no lane
   may add to them, ever". This lane's change strands both selectors: the
   radio's ring stopped being a plain bordered circle, so the two rules moved
   here rather than being edited in place. Everything else `.rd-swatch*`
   (the option row, the tile, the label) is untouched and stays where it was.

   Borrows `.rd-box` / `.rd-box__inner`, declared in
   `_legacy-kit-chamfer-button.css` above, the same way kit-segmented.css
   does — descended into for sizing and colour, never redeclared. */

/* The circular-radio "known, routed exception" (a 50% round corner) is
   RETIRED. 48 export 24 (21 Aug 2026) rules it out: this radio simulates a
   native control but is hand-drawn — "it is OURS, not the OS's" — so it
   takes the kit's own chamfer like every other stroked box, not the OS's
   circle. Status dots,
   step marks, the native date/time input face and the toggle track stay
   circular/pill-shaped; they are the OS's own shapes or read as marks rather
   than controls, and this ruling does not reach them.

   A stroke on a clip-path'd box loses its diagonal (`01-chamfer-box.css`'s
   own header), which is exactly why this now routes through `RdChamferBox`
   instead of a bare `clip-path` on `.rd-swatch__radio` — the ring IS a
   border, and a hand-rolled cut here would render it bare on every corner.

   Colour is UNCHANGED by this migration: `--rd-fg3` unselected / `--rd-acc`
   selected, both still the frozen literals `theme_swatch.rs`'s module doc
   tracks under R2/R5 (routed to Claude Design, unrelated to this shape fix).
   `RdChamferBox`'s own `Stroke`/`Fill` enums do not carry either literal
   exactly, so the ring reaches into `.rd-box` directly for its background —
   the same override shape `.rd-swatch--sel .rd-box { background:
   var(--rd-volt); }` already takes for the tile, just scoped one selector
   deeper so it does not also repaint the tile's own `.rd-box`. */
.rd-swatch__radio {
  flex: none;
}
.rd-swatch__radio .rd-box {
  width: 13px;
  height: 13px;
  background: var(--rd-fg3);
}
/* `RdChamferBox`'s inner layer, via its `inner_class` prop — the literal
   class the component passes (`rd-swatch__radio-face`) is what `rangeday
   check css` resolves against this rule. */
.rd-swatch__radio-face {
  background: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* Higher specificity than `.rd-swatch--sel .rd-box` and
   `.rd-swatch:not(.rd-swatch--sel):active .rd-box` in
   `_legacy-kit-components-and-screens.css` (three classes against two), so
   this wins regardless of the two files' relative order — the same
   "specificity, not order" guarantee `adm1-5-1.css` documents for its own
   in-flight override. */
.rd-swatch--sel .rd-swatch__radio .rd-box,
.rd-swatch:not(.rd-swatch--sel):active .rd-swatch__radio .rd-box {
  background: var(--rd-acc);
}

/* The 6px mark. A solid fill with no border, so — unlike the ring — a plain
   `clip-path` chamfer is correct here: there is no stroke to lose. 1.5px cut,
   the smallest whole step the 7–14 scale's own floor implies at this size and
   proportionally close to the ring's outer 6.5. */
.rd-swatch__dot {
  width: 6px;
  height: 6px;
  background: var(--rd-acc);              /* a 6px mark reads as a stroke, not
                                             an area -> acc, not volt (spec §5
                                             R1) */
  clip-path: polygon(1.5px 0, 100% 0, 100% calc(100% - 1.5px), calc(100% - 1.5px) 100%, 0 100%, 0 1.5px);
}
/* ==== kit-brand-mark · RdBrandMark's theme pair — which square lockup the theme draws · owner: 1-track3 ==== */

/* ⚠️ THE DEFECT THIS PART CLOSES: A WHITE LOCKUP ON A LIGHT GROUND.
   Reported from staging (James, 2026-08-28) — in LIGHT theme the splash, the
   two gate screens and `/login` drew `rd-square-white.svg` and the mark was
   invisible. The mark was never themed: `brand.rs` had ONE artwork for every
   surface, and `assets/brand/README.md` recorded `rd-square-black.svg` as
   "the light-ground lockup … nothing draws it".

   The ground under it IS themed, which is what turned an unthemed mark into an
   invisible one. `s02-splash.css` §4 dims the five-layer splash TO the app's
   own ink — `color-mix(in srgb, var(--rd-app-bg) 72%/88%, transparent)` — and
   `--rd-app-bg` is `#0B0B0B` on dark and `#F1F1EC` on light
   (`_legacy-tokens.css`). So on light the whole splash composites to a pale
   wash and a white lockup has nothing to sit against. The two RD_Assets squares
   are byte-identical path data differing only in their root `fill`
   (`brand.rs::the_vendored_marks_do_not_rely_on_the_empty_defs`), so the fix is
   to draw the OTHER one — never to invert the first.

   ⚠️ **NO `filter: invert()`, AND THAT IS A RULING AND NOT A TASTE.** Inverting
   the white lockup would produce a drawing nobody approved: `invert(1)` on
   `#FFFFFF` gives `#000000`, which happens to be right for this artwork today
   only because it is a single-ink mark. The moment the lockup gains a second
   ink — a gold rule, the `powered by T.REX ARMS` line in an accent — the filter
   ships a colour-negative of the brand. The vendored black export exists
   precisely so the light ground gets a DRAWN mark. The tablet rail head's old
   `invert(1)` stopgap on the wordmark was deleted for the same reason when the
   head moved to the icon mark (`brand.rs::ICON_MARK_B`).

   ⚠️ **TWO `<img>` ELEMENTS, NOT ONE WHOSE `src` CHANGES.** The alternatives
   and why they lost:

   - `<picture>` with `media="(prefers-color-scheme: …)"` — resolves the OS's
     mode, not the app's. This app's theme is an explicit stored preference
     (`theme.rs`, `rdp_theme` in localStorage); a shooter who forced Dark on a
     light phone would get the ink mark. That disagreement is the same one the
     `<meta name="theme-color">` note in `index.html` exists to prevent.
   - `img { content: url(…) }` — one element, no duplicate, and it is the
     tidiest thing CSS can do here. Not shipped: `content` on a replaced element
     is uneven across the engines this PWA installs into, and a mark that
     silently does not swap is the defect we are closing.
   - Swapping `src` from Rust on the theme signal — correct, and it makes the
     mark the one element in the boot sequence that needs the app's state to
     paint. The pre-boot frame cannot have that (see `index.html`), so the two
     halves of one artwork would then swap by two different mechanisms.

   So: both files are in the markup, and CSS shows exactly one. `display: none`
   removes the other from the accessibility tree as well as from the paint, so
   the duplicate `alt` is announced once — which is why both `<img>`s may carry
   the same accessible name (`brand.rs::MARK_NAME`) rather than one of them
   being `aria-hidden`, a state CSS cannot toggle.

   ⚠️ **THE BASE RULE IS DOUBLED (`.rd-mark__art.rd-mark__art--light`) ON
   PURPOSE.** The caller's sizing class is what carries `display` today —
   `.rd-gate__mark` in `_legacy-kit-components-and-screens.css` declares
   `display: block`, one class, and it lands on BOTH images. A single-class
   `.rd-mark__art--light { display: none }` would tie with it on specificity and
   the winner would be decided by which part `order.txt` appends last, which is
   exactly the silent, reorder-sensitive coupling that file's banner warns
   about. Doubled, this rule wins on specificity from anywhere in the sheet. */
.rd-mark__art--dark { display: block; }
.rd-mark__art.rd-mark__art--light { display: none; }

/* The explicit preference. `theme.rs::apply` stamps `data-theme` on the root
   with `painted()`'s output, which is only ever `dark` or `light` — `auto` is
   resolved before it reaches the DOM and there is no `[data-theme="auto"]`
   block anywhere (`theme.rs::painted_never_returns_auto_so_the_attribute_is_
   always_a_real_block`). So these two selectors are the whole of the in-app
   case, and the dark half needs no rule of its own: it is the base above.

   Equal specificity to the doubled base rule, and LATER in this file, which is
   how the light arm wins. Both rules live in one part precisely so that tie is
   decided here rather than by `order.txt`. */
[data-theme="light"] .rd-mark__art--dark { display: none; }
[data-theme="light"] .rd-mark__art--light { display: block; }

/* The unstamped frame — first paint, before the wasm bundle has run `apply`.
   `index.html`'s pre-boot script stamps `data-theme` from localStorage for this
   window, so in the app this matches only if that script did not run at all
   (an engine that threw, a stripped inline script). Kept anyway: the honest
   fallback for a document with no stated theme is the device's, and without it
   such a frame would paint the dark mark on whatever ground the sheet chose.

   `:root:not([data-theme])`, never a bare media query: once the attribute IS
   stamped it must win over the OS, which is the whole point of storing a
   preference. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) .rd-mark__art--dark { display: none; }
  :root:not([data-theme]) .rd-mark__art--light { display: block; }
}
/* ==== kit-tablet-shell · RdTabletShell, the tablet shell · owner: spine ==== */

/* Drawn as `TabShell` in rd-tablet-kit.jsx:318 ("function TabShell"). Component:
   apps/web/src/components/kit/tablet_shell.rs, which carries the two-mode
   ruling, the rail-less case and the modal's positioning argument. Only the
   values live here.

   ANATOMY, and the order is load-bearing exactly as .rd-shell's is:

     [ root, inset by env(safe-area-inset-top):
         frame, 1600px max, centred past it:
           rail 208px, full height | column: header -> tabs -> content ]
     + modal, a SIBLING of the frame at the root

   ⚠️ AMENDED 2026-08-28. This read `column: status 24 -> header -> tabs ->
   content`, and the paragraph under it explained that the status strip sat over
   the CONTENT pane and not over the rail — rd-tablet-kit.jsx:58 ("the rail
   starts below nothing") — which is why the strip lived inside the column
   rather than above both. **James ruled the fixed 24px reserve out (option (c),
   the rehearsal pass): the top inset is `env(safe-area-inset-top, 0px)` on the
   ROOT, above both columns, so all three heads in the two rooms start at one y.
   §4 carries the ruling, the measurements and what the slot held.** The drawn
   sentence still holds for what remains of it: the rail runs the full height
   and starts below nothing, and now so does the column beside it.

   ⚠️ THE ROOT IS THE POSITIONING CONTEXT AND THAT IS NOT DECORATION.
   rd-tablet-kit.jsx:320 ("THE SHELL OWNS THE POSITIONING CONTEXT"): "A modal
   rendered inside a content pane resolves inset:0 against that pane, which dims
   the pane and leaves the rail lit - less than a phone bottom sheet dims, while
   claiming to be the same pattern translated." `position: relative` on
   .rd-tshell is the whole of that guarantee, and it is one declaration away from
   being deleted as unused. Pinned by a test in the component.

   ⚠️ THIS PART DECLARES NO COLOUR ON THE RAIL SLOT OR THE TAB SLOT. Both are
   reservations, the way .rd-shell__nav is: "the shell reserves the slot and
   NEVER lets it shrink". The rail's ground, its border and its rows belong to
   RdTabletRail, which lands separately — its CONTENT is ruled (seven rows,
   closed, 2026-08-17) and its build is a different item; the tab row's gaps and
   its underline belong to the row component.
   A colour written here would paint under a component that has not been
   reviewed yet. */

/* ---------------------------------------------------------------------------
   1. The shell root — the positioning context, and the ground.
   --------------------------------------------------------------------------- */
.rd-tshell {
  /* 100dvh, NEVER 100vh — the same declaration and the same argument as
     .rd-shell in kit-shell.css: on mobile browsers 100vh is the URL-bar-
     collapsed height, so a 100vh shell is taller than the visible viewport and
     its last child sits below the fold. It matters here for a different reason
     than it does there: nothing walks off the bottom (the rail is a column, not
     a bottom bar), but the CONTENT REGION's scroll is what would be lost, and a
     shell taller than the glass scrolls the document instead of the region. */
  height: 100dvh;
  width: 100%;
  box-sizing: border-box;

  /* ⚠️ **THE TOP INSET, ONCE, AT THE SHELL ROOT — RULED 2026-08-28 (James,
     option (c)).** It replaces the fixed 24px status reserve that used to sit
     inside the content column; that rule is deleted and §4 now carries the
     ruling text, the measurements and what the slot actually held.

       "The fixed 24px status reserve goes. The top inset becomes
        `env(safe-area-inset-top, 0px)` applied ONCE at the shell level — the
        tablet shell root, both rail and page column, and the Pro shell root
        equally — so the shooter rail head, the shooter tablet page head and the
        Pro top all start at the same y: 0 in a browser tab, the real inset in
        the installed PWA."

     WHY THE ROOT AND NOT THE COLUMN: the reserve lived inside
     `.rd-tshell__col`, so it insetted the page head and NOT the rail beside it.
     Measured before it went: the rail head ran 0 -> 76 and the page head
     24 -> 100, against the console's 0 -> 76 for both of its own. Here the
     inset moves the whole frame, so all three heads keep one top offset by
     construction and no head needs an offset of its own. Pinned by
     `ruling_no_fixed_status_reserve_the_inset_is_the_shell_roots` and
     `ruling_the_three_heads_share_one_top_offset` (`tablet_shell.rs`).

     `env(safe-area-inset-top, 0px)` AND NOT A NUMBER. In a browser tab there is
     no system bar and this resolves to 0. In the installed PWA `index.html`
     ships `viewport-fit=cover`, which is the precondition for `env()` returning
     anything at all, so the inset is the platform's own measurement rather than
     an iPad-shaped guess. It is also the app's existing answer rather than a new
     one: `--rd-shell-inset-top` (`kit-shell.css`) already composes the same
     term on the phone.

     ⚠️ IT COSTS THE FRAME HEIGHT RATHER THAN OVERFLOWING IT, which is what
     `box-sizing: border-box` above buys: the padding comes OUT of the 100dvh,
     so the root still ends exactly at the fold and `.rd-tshell__frame`'s
     `height: 100%` measures the inset box. Without border-box this padding
     would push the frame's bottom below the glass and the content region's
     scroll — the thing the `100dvh` note above exists to protect — would become
     the document's again.

     MEASURED, NOT REASONED, because `env()` resolves to 0 in every browser this
     can be opened in: a scratch copy of the assembled sheet with a literal
     substituted for the token, 1194 x 813, 4000px of content in the region.
     With no inset all four heads (both rails, both page heads) run 0 -> 76.
     With a 24px inset all four run 24 -> 100, both roots still end at 813, the
     frame measures 789 = 813 - 24, and the document does not scroll in either
     room. The token itself is still only verifiable by reading. */
  padding-top: env(safe-area-inset-top, 0px);

  /* rd-tablet-kit.jsx:325 ("position: 'relative'"). Every tablet modal
     resolves `inset: 0` against THIS element, so it dims the rail too. See the
     banner above.

     ⚠️ CORRECTED 2026-08-18. This used to read "the modal is a CHILD of this
     element". It is a grandchild now — the persistent-chrome split
     (`pages/shell.rs`'s TabletChromeLayout) put `.rd-tshell__frame` between
     them, because the layout renders the frame and the SCREEN renders the
     column and its modal side by side inside it. What did not change is the
     containing block: a containing block is the nearest POSITIONED ancestor,
     and the frame declares no `position` (see §3, which now says so out loud).
     The two facts are pinned together in tablet_shell.rs by
     `ruling_nothing_between_the_modal_and_the_shell_root_is_positioned`. */
  position: relative;

  display: flex;

  /* The REGION scrolls, not the document — vertically. The horizontal axis is
     the breakpoint's business; see §8, which switches it. */
  overflow-y: hidden;
  overflow-x: auto;

  /* ⚠️ CORRECTED 2026-08-17 EVENING. This used to read "the ground is painted
     here and not on a capped column, because there is no capped column: this
     shell is full-bleed by construction … the whole structural difference
     from .rd-shell, which needs two elements precisely because it caps." That
     was true before the 18 Aug export and is not true now — see §3, which now
     caps `.rd-tshell__frame` at 1600 and centres it. This root is exactly
     .rd-shell's VIEWPORT layer (kit-shell.css §1: "the VIEWPORT layer paints
     the ground, the FRAME is the width-capped column"): it paints the ground
     full-bleed and stays uncapped so the ground can extend on both sides past
     1600, while the frame inside it is what caps and centres. The two shells
     still differ in HOW they centre — see §3 for why this one cannot reuse
     the flex-centring keyword .rd-shell's own root sets.

     ⚠️ `--rd-app-ground`, NOT `--rd-app-bg`, SINCE THE DARK GROUND GAINED THE
     CONCRETE TEXTURE. `rd-tablet-kit.jsx:319` (TabShell) draws the same
     gradient-over-texture value `rd-train-kit.jsx:148` (TShell) does, off the
     same per-theme `tex` flag — one ground for both shells, which is why one
     token serves both rules. The argument for the token split, the attachment
     decision and the asset's size live in `kit-shell.css` §0b, where the token
     is declared; do not restate them here and do not author a url() in this
     part.

     ⚠️ THIS ROOT DOES SCROLL ON ONE AXIS — `overflow-x: auto` above — and the
     ground is still fixed, which is worth saying because the phone shell's
     `overflow: hidden` makes it look as though "hidden" were the requirement.
     It is not. A background is painted on the element's own padding box and is
     fixed to it — `background-attachment: scroll` means fixed with regard to
     the ELEMENT, not scrolling with its contents — so the texture does not
     travel when this box's contents pan sideways at a narrow width.

     Full-bleed matters more here than on the phone: past 1600 the
     frame stops and this element is all the reader sees on either side, so a
     ground painted on `.rd-tshell__frame` would letterbox the photograph. */
  background: var(--rd-app-ground);

  /* Body type role, as .rd-shell sets it (substrate §2.1: family only). */
  font-family: 'Archivo', sans-serif;
}

/* ---------------------------------------------------------------------------
   2. The rail slot — 208px, full height, never scrolls.
   --------------------------------------------------------------------------- */
/* ⚠️ NESTED INSIDE `.rd-tshell__frame` (§3), NOT DIRECTLY INSIDE THE ROOT, since
   the 2026-08-17 centring fix. Nothing below this line changed: the rail's own
   width, flex behaviour and overflow are unaffected by which element is its
   parent. Only the parent moved, so the frame could cap and centre rail +
   column as the one 1600px block the ruling describes.

   rd-tablet-kit.jsx:19 ("RAIL_W = 208") and :157 ("width: RAIL_W").

   ⚠️ CORRECTED 2026-08-18 — James's red-line, 18 Aug (Design record held by
   the coordinator lane's head). This used to read "full height comes for
   free: it is a flex child of a full-height row and stretches", and that is
   only half the chain. THE SLOT (this element) does stretch for free: it is
   a flex child of `.rd-tshell__frame`, a row-direction flex container whose
   `align-items` is the unset default `stretch`, so the slot's own height is
   handed to it with no declaration here. But the slot's PAINTED CONTENT is
   not the slot — it is whatever fills `{rail}` (today, always
   `RdTabletRail`'s root, `.rd-trail` in kit-tablet-rail.css), and THIS
   element is itself `flex-direction: column`. `align-items: stretch`
   stretches a flex item on the CROSS axis; in a column container the cross
   axis is width, not height, so nothing here stretches a child's height for
   free. `.rd-trail` declares `flex: none`, which sizes it to its own content
   (seven rows + head, ~416px) and leaves it there — on a viewport taller
   than that content, the rail's dark ground ends at its last row and the
   unpainted remainder of the slot shows `.rd-tshell`'s own `--rd-app-bg`
   ground underneath. The drawn canvas (834px) never showed it because seven
   56px rows plus the head nearly fill it; only a desktop-height viewport
   above the drawn canvas exposes the gap. See the rule after this one for
   the fix.

   `overflow: hidden` and NOT `auto`: MEMBERSHIP IS CLOSED AT SEVEN ROWS (James,
   2026-08-17, ratifying the drawn rail — Dry Fire and Range Bag are NOT promoted
   and stay Home tiles), and seven rows at the ratified 56px is 392px inside an
   834px canvas. The rail cannot overflow the shortest drawn viewport, and a rail
   that scrolls is a rail you can lose your place in. An eighth row would need a
   ruling to reopen, and this is the declaration to revisit with it.

   ⚠️ 56, NOT 52. The corpus's own kit header says the rows "are 52px rather than
   the console's 38" (rd-tablet-kit.jsx:10 ("its rows are 52px")), and its DRAWN
   rows are `minHeight: 56` (rd-tablet-kit.jsx:181 ("minHeight: 56")). James
   ratified 56 on 2026-08-17, so the prose is stale and the drawing was right —
   the same "if a drawing and a written answer disagree, the drawing wins"
   precedent the tab bar's own label carries. No rule here sets it: the row
   height is RdTabletRail's, which is held. This is a note so the next reader of
   that header does not transcribe 52. */
.rd-tshell__rail {
  width: 208px;
  flex: none;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* THE SLOT'S CHILD, FLOORED AT THE SLOT'S OWN HEIGHT. ⚠️ ADDED 2026-08-18 —
   James's red-line, 18 Aug (Design record held by the coordinator lane's
   head), fixing the gap the corrected comment above describes.

   `min-height: 100%` rather than `height: 100%` or a `flex-grow` on the
   child: it FLOORS the child's height at the slot's own (already-definite,
   already-stretched — see above) height without forcing it there outright,
   so seven rows shorter than the slot get pulled down to fill it (the fix)
   and seven rows that somehow ran taller than the slot are not compressed
   below their content (consistent with "the rail cannot overflow the
   shortest drawn viewport" below — this rule only ever grows the painted
   ground, never shrinks the row content).

   SCOPED TO THE SLOT'S DIRECT CHILD, `.rd-tshell__rail > *`, NOT TO
   `.rd-trail` ITSELF, and that is deliberate: this is the SLOT's contract —
   whatever fills `rail` must reach the slot's full height — not a rule about
   what `RdTabletRail` in particular looks like. Keeping it here, rather than
   adding `min-height: 100%` to `.rd-trail` in kit-tablet-rail.css, is the
   same split the "THIS PART DECLARES NO COLOUR ON THE RAIL SLOT" note above
   already draws between the slot's ownership and the rail component's own:
   the slot reserves the space and (now) also floors the height of whatever
   occupies it; RdTabletRail's own file stays untouched and still free to be
   mounted bare (`/kit` review surface) with no shell around it to floor
   against — `min-height: 100%` against a non-flex, non-definite-height
   ancestor there resolves to nothing, which is the same no-op the previous,
   unstretched behaviour already was in that context. Pinned by
   `ruling_the_rail_slot_floors_its_childs_height_at_its_own` in
   tablet_shell.rs. */
.rd-tshell__rail > * {
  min-height: 100%;
}

/* ---------------------------------------------------------------------------
   3. The frame — rail + column together, the ONE 1600px-max block, pinned left.
   --------------------------------------------------------------------------- */
/* ⚠️ THE 1600 CAP — RULED 2026-08-17, IN THE CORPUS SINCE THE 18 AUG EXPORT,
   and it CLOSES the open question §8 below used to carry. `31 (Desktop) Pro
   Shell`, "The desktop growth question, ruled 18 Aug": "the tablet layout is
   the desktop layout, and the shell has a hard upper end. Nothing new appears
   with width. Shooter routes grow from the 1194 the tablet documents draw,
   console routes from the 1280 drawn here, and both stop at 1600 - content
   centres on ink past that, and the growth between floor and cap goes into
   pane measure and whitespace, never into new columns or new density. The rail
   does not grow at all."

   THE READING — CORRECTED 2026-08-19 BY THE PRODUCT OWNER, ON STAGING, AND
   THIS IS THE READING THAT STANDS. "Content" in "content centres on ink past
   that" means the CONTENT PANE'S INK — the measure of the text and cards
   inside the column — not the chrome. The rail is pinned to the shell's START
   EDGE at every width, and the free space past the cap all lands on the right:

     [ rail 208, fixed | column, up to 1392 ][ ground ]
      \________ frame, max 1600, flush left ________/

   THE DRAWINGS PIN IT, TWICE, AND NEITHER CENTRES. `rd-tablet-kit.jsx:319`-`:327`
   (`TabShell`) is `width: '100%', height: '100%', display: 'flex'` with
   `<TabRail>` as the first child — no `maxWidth`, no `margin`, no
   `justifyContent`. `rd-pro-kit.jsx:324`-`:286` (`ProShell`), the console twin,
   is the identical shape. A floating rail is drawn nowhere in the corpus.

   ⚠️ THE CENTRED READING WAS A MISREADING, AND THIS BLOCK IS ITS RETRACTION.
   Between 2026-08-17 evening and 2026-08-19 this file read "content centres on
   ink past that" as *the rail+column block centres*, and on that basis
   overturned the earlier build that capped `.rd-tshell__col` alone
   (`max-width: calc(1600px - 208px)`) and left the rail pinned to the root's
   left edge. James caught the result on staging — the rail floating ~165px
   inboard on a ~1930px viewport — and ruled the earlier reading right. The
   inference never had a drawing behind it; it came from one clause of prose in
   `31 (Desktop) Pro Shell.html:38` read against two drawings that both pin the
   rail flush. So the SUBSTANCE of
   `ruling_the_content_column_caps_at_sixteen_hundred_with_the_rail_fixed` is
   restored here, while the MECHANISM below stays the frame's (one cap, not
   two — see §3a).

   THE MECHANISM. `.rd-tshell__frame` wraps the rail and the column as one
   1600px-max block — the same VIEWPORT/FRAME split `.rd-shell` already uses
   (kit-shell.css §1: "the VIEWPORT layer paints the ground, the FRAME is the
   width-capped column"), reused here for a frame that happens to hold two
   children side by side instead of one.

   THE AUTO MARGIN IS ON THE END EDGE ONLY — `margin-inline-end: auto`, one
   declaration, and that asymmetry IS the flush-left reading. All positive
   free space past the cap is absorbed on the right, so the frame's start edge
   sits at the root's start edge at every width and the rail touches it.

   IT DOES NOT USE `justify-content: center`, AND MUST NOT ACQUIRE IT — the
   same hazard the centred reading already documented, kept because it is a
   hazard for any future re-read, not only for centring. `justify-content:
   center` is UNSAFE alignment per the CSS box-alignment spec: when the item
   overflows its container, an unsafe-centred item can be pushed past the
   container's START edge and become unreachable by scrolling, since §8 leaves
   `overflow-x: auto` below the 1024 breakpoint on purpose for a rail+list
   combination that is drawn wider than a 1024px floor.

   The end-only auto margin does not have that failure either, and the
   argument is unchanged: auto margins on a flex item resolve to 0 the moment
   free space along the axis goes negative (the same rule that makes
   `margin: 0 auto` on an ordinary block never strand content when the
   viewport is narrower than the block). So below 1600, and below 1024 where
   the frame can be wider than the viewport, the margin is inert and the left
   edge stays reachable by scroll. Above 1600 it takes the surplus. Either
   way the start edge never moves — which is now a stronger invariant than
   the centred build's, not a weaker one.

   NO CAP ON THE ROOT. The root paints the ground, and past the cap the ground
   shows on the END side only — the frame's own start edge is the root's.
   Pinned by `ruling_the_frame_caps_at_sixteen_hundred_and_pins_the_rail_left`
   in tablet_shell.rs. */
/* ⚠️ NO `position` HERE, AND THAT ABSENCE IS LOAD-BEARING (2026-08-18).
   Since the persistent-chrome split this element sits between a screen's modal
   and `.rd-tshell`, and an absolutely-positioned scrim resolves `inset: 0`
   against the nearest POSITIONED ancestor. Add `position: relative` — the most
   ordinary thing anyone ever does to a wrapper — and every tablet modal starts
   dimming the frame instead of the shell, which stops dimming the ground
   beside the capped frame past 1600. `transform`, `filter` and
   `contain` do the same thing without looking like it. Pinned by
   `ruling_nothing_between_the_modal_and_the_shell_root_is_positioned`. */
.rd-tshell__frame {
  display: flex;
  flex: 1 1 auto;
  min-width: 0;
  max-width: 1600px;
  margin-inline-end: auto;       /* END only: the rail stays flush against the
                                    root's start edge at every width, and all
                                    free space past the cap lands on the right.
                                    0 below the cap (negative free space), the
                                    only thing that moves past it (positive free
                                    space) */
}

/* ---------------------------------------------------------------------------
   3a. The content column — everything inside the frame that is not the rail.
   --------------------------------------------------------------------------- */
/* No `max-width` here any more. The frame above already caps the pair of them
   at 1600 and the rail is `flex: none` at a fixed 208, so `flex: 1` on this
   column takes exactly what is left of the frame's width — `calc(1600px -
   208px)` at the cap, less below it — with no second literal to keep in sync
   with the rail's. */
.rd-tshell__col {
  flex: 1;
  min-width: 0;                  /* a flex item defaults to min-width:auto and
                                    refuses to shrink below its content */
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* ---------------------------------------------------------------------------
   4. The status strip — DELETED 2026-08-28. The top inset is the shell root's.
   --------------------------------------------------------------------------- */
/* ⚠️ **THIS SECTION HELD `.rd-tshell__status`, A FIXED 24px OF RESERVED AIR
   INSIDE THE CONTENT COLUMN. JAMES RULED IT OUT 2026-08-28 — option (c) of
   three put to him on the rehearsal finding — AND THE RULE, THE SLOT AND THE
   `status` PROP ALL WENT WITH IT.** Kept as a banner rather than deleted
   silently, because the thing it replaced was itself a ruling (20 Aug) and the
   next reader to find the drawn `height: 24` will otherwise re-transcribe it.

     "The fixed 24px status reserve goes. The top inset becomes
      `env(safe-area-inset-top, 0px)` applied ONCE at the shell level — the
      tablet shell root, both rail and page column, and the Pro shell root
      equally — so the shooter rail head, the shooter tablet page head and the
      Pro top all start at the same y: 0 in a browser tab, the real inset in the
      installed PWA."

   The declaration is on `.rd-tshell` in §1, and on `.rd-pro` in adm0-chrome.css.
   One per shell root, no per-head offsets.

   WHAT THE SLOT HELD, since a removal has to say: **nothing, on every screen in
   the app.** The drawn contents are a simulated iPadOS clock and indicators
   (`rd-tablet-kit.jsx:60` ("function TabStatus")) — the SYSTEM's chrome, which
   the 20 Aug ruling explicitly refused to mimic — so the `status` prop was
   optional and **no call site in the app ever passed it**. Both arms of the
   slot rendered the same empty box; the 24px of air WAS the whole feature.
   Nothing to keep, so nothing was kept.

   WHY IT HAD TO GO RATHER THAN MOVE. It sat inside `.rd-tshell__col`, which
   inset the page head and not the rail beside it. Measured at 1194 wide in the
   assembled sheet: `.rd-trail__head` 0 -> 76, `.rd-tshell__head` **24 -> 100**,
   while the console's `.rd-pro-rail__head` and `.rd-pro-top` both ran 0 -> 76.
   That 24px is the "shooter page headers render taller" James caught at the
   rehearsal — the head box was already a hard 76 in all four of its arms.

   AND THE 20 AUG RULING'S OWN PREMISE DID NOT SURVIVE THE MEASUREMENT. It read
   the drawn anatomy as requiring the height — *"the rail starts below nothing"
   only reads if the strip's height exists* — but the strip's height is the
   SYSTEM BAR's, and `index.html` ships `viewport-fit=cover` with an OPAQUE
   `apple-mobile-web-app-status-bar-style: black`, so the platform has already
   inset the web view before our first pixel. The 24 was a second reserve over
   an inset that had been applied, which is why it read as air in a browser tab
   and as double air in the installed app. `env(safe-area-inset-top, 0px)` is
   the same claim made in the units the platform can answer.

   Pinned by `ruling_no_fixed_status_reserve_the_inset_is_the_shell_roots`,
   which asserts the ABSENCE of this rule and of the class in the markup as
   well as the presence of the token — a reserve reintroduced beside the inset
   is the 24 coming back under a different name. */

/* ---------------------------------------------------------------------------
   5. The page header — title, subtitle, and the outlined action slot.
   --------------------------------------------------------------------------- */
/* rd-tablet-kit.jsx:337 ("padding: '10px 28px 16px'"). `align-items: flex-end`
   is drawn and it is what puts the 44px action box on the title's baseline edge
   rather than centred against a two-line header.

   ⚠️ BUILT 2026-08-19 — CONSTANT HEADER HEIGHT, relay5 §6 ("TabShell's header
   bar takes min-height 76, contents bottom-aligned... Per-screen variation is
   not blessed. The height belongs to the kit, once"), drawn at
   `rd-tablet-kit.jsx:336` [gone] ("minHeight: 76, boxSizing: 'border-box'") — export
   30 re-ruled the floor into a hard height, so the drawn declaration is now
   `rd-tablet-kit.jsx:337` ("height: 76, boxSizing: 'border-box'"), with the
   ruling's own prose at `:329`-`:335`:

     "CONSTANT HEADER HEIGHT, ruled 18 Aug after James caught the bounce on
      staging: /orgs draws title+sub, /profile/friends draws title+action, and a
      slot-conditional header made sibling screens sum to different heights -
      hopping between them shifted the whole content column. Hard 76 (re-ruled
      27 Aug from 76-minimum): content that doesn't fit is redesigned, never a
      taller bar; contents stay bottom-aligned so the title row does not float
      on shorter screens."

   WHAT CARRIED THE HEIGHT BEFORE, AND WHY THE BOUNCE WAS REAL HERE TOO: nothing
   did. This block was `flex: none` with `padding: 10px 28px 16px` and no floor,
   so its height was content + 26px — exactly the slot-conditional sum the
   ruling names. `/orgs` (title+sub) and `/profile/friends` (title+action)
   landed on different totals in this build the same way they did in the
   drawing, before this declaration.

   THE 18 AUG FIX WAS ONE DECLARATION: `min-height: 76px` (now a hard `height`,
   see the ruling below — the history is kept because the floor's reasoning is
   still why the number is 76). Nothing else moved —
   `align-items: flex-end` was already drawn and already here, and
   `box-sizing: border-box` was already here, which is what makes 76 the OUTER
   height including the 10/16 padding, as the drawing's own
   `boxSizing: 'border-box'` states.

   THE CONDITIONALITY ITSELF IS UNCHANGED AND IS NOT WHAT THE RULING KILLS: the
   drawing's `{(title || action) && (...)}` at `rd-tablet-kit.jsx:336` is
   byte-identical before and after the 18 Aug edit — the diff touches only the
   inline style object inside the still-conditional block. Every drawn
   `TabShell` call in the corpus (training, livefire, solo, authoring, social,
   identity, relay, logistics — the relay5 sweep report) passes a `title`, so
   the header renders on every sibling screen the ruling names; "sibling
   screens sum equal" is satisfied by the floor applying whenever the header is
   present, not by forcing the header to render when a screen passes neither
   `title` nor `action`. A screen that truly omits both stays outside the
   ruling's stated comparison (it never had a header to bounce against).

   Pinned by `ruling_constant_header_height_floors_the_head_at_76px` in
   `tablet_shell.rs`, `ruling_`-prefixed per house convention (Design's ruling,
   not a value a build may bump).

   ⚠️ A HARD `height`, NOT A FLOOR — JAMES, 2026-08-19: "ONE FIXED HEIGHT,
   BUTTONS OR NOT." A floor only raises short headers; it cannot cap tall ones,
   which is why the bounce survived the 18 Aug build. Two things had to be true
   before `min-height` could become `height`, and both shipped in the change
   that made it:

   1. `.rd-tshell__sub` declares its own `line-height` (§4a). Without it the
      subtitle inherits `body`'s 1.5 and the title+sub column measures
      26 + 7 + 18 = 51 against a 49px content box — a cap would have CLIPPED
      that overshoot rather than removed it. At 1.25 the column is
      26 + 7 + 15 = 48, inside 49.

   2. No action slot exceeds the budget. `.rd-tshell__action` floors at 44 and
      does NOT cap, and its contents are the caller's, so every slot in the app
      is a clipping risk the moment this stops growing.

   THE BOX MODEL, because the budget is easy to get wrong by a pixel. This is
   `box-sizing: border-box`, so the declared 76 includes the padding AND the
   border-bottom: 76 - 26 of padding - 1 of border = a **49px content box**.
   `.rd-tshell__action` is border-box too (the global `*` reset in
   _legacy-base.css), so its `min-height: 44px` already contains its own
   2 x 1.5 border and a slot's outer height is `max(44, content + 3)`.
   The budget is therefore 49, not 50 — content over 46 overflows.

   THE SWEEP, 2026-08-19, all eight `RdTabletShell` action slots in the app:

     training.rs        .rd-tdrills__act        Link, inherit-only reset   44
     courses.rs         .rd-courses-tab__act    Link, inline-flex          44
     friends.rs         .rd-tppl__act           button, bare reset         44
     range_bag.rs       .rd-bag-tab__add-act    button, reset (optional)   44
     range_day_form.rs  .rd-plan-t__act         button, bare reset         44
     stage_editor.rs    .rd-se-tab__acts        2 buttons, 32 + 34 tall    44
     challenges.rs      .rd-chal-t__act         Link, flex reset           44
     events_tablet.rs   .rd-tev__act            Link, inherit-only reset   44

   Every one lands on the 44 floor — 5px of headroom against 49. The tallest
   content in any slot is stage_editor's 34px control cluster, which still only
   needs 37 outer. `range_day_form.rs` is the one that did NOT: it was the only
   slot in the app passing a full `RdButton`, whose LAYOUT box is 52px (48
   painted, plus the 4px of always-present focus-ring padding on `.rd-btn` that
   `button::hit_target_height_px` accounts for), making the slot 55 and the
   header 82. It was converted to the house reset control in this same change —
   which also fixed the accent rectangle the slot was drawing around it.

   ⚠️ SO THE COST OF THIS RULING IS PAID BY EVERY FUTURE ACTION SLOT. `height`
   does not grow, so a slot whose content exceeds 46px now CLIPS silently
   rather than pushing the header taller. Re-introducing an `RdButton` here is
   the obvious way to do it — 55 outer, 82 header, no error anywhere. Guarded
   by `the_plan_forms_header_action_is_a_reset_control_not_a_kit_button`.

   Pinned by `ruling_constant_header_height_fixes_the_head_at_76px`, which
   supersedes `..._floors_the_head_at_76px` and asserts the ABSENCE of
   `min-height` as well as the presence of `height`: a floor left beside a cap
   at the same number is the cap written twice.

   ⚠️ **RULED 2026-08-28 (James, the rehearsal pass) — THIS HEAD AND THE PRO
   PAGE HEAD ARE ONE BOX**, the page-head half of the same ruling that made the
   two rail heads one box (`.rd-trail__head`, kit-tablet-rail.css §2):

     "ONE header geometry across both rooms - hard 76, contents bottom-aligned,
      matching ProTop declaration by declaration. If the shooter head carries an
      action slot or a larger title face, it takes the Pro values, not the other
      way round."

   MEASURED FIRST, which is why this block gained a note and not a new number.
   1194 wide, dark, the assembled sheet, `getBoundingClientRect().height` on the
   head box itself:

     shooter, title+sub                76      Pro, title+sub           76
     shooter, title+sub+action         76      Pro, title+sub+action    76
     shooter, title+sub+tab band       76
     shooter, title+waiting sub        76

   The head is ALREADY the console's box in all four arms — the hard 76 that
   `.rd-pro-top` took on 27 Aug was copied off this rule. What differed was the
   title COLUMN inside it: 48.0 here against 47.625 there, because the two
   drawings disagree about the sub's size and its offset. Those two numbers now
   take the console's values; see `.rd-tshell__sub`.

   ⚠️ **AND WHAT ACTUALLY RENDERED TALLER WAS NOT THIS BOX — IT WAS THE 24px
   STATUS RESERVE ABOVE IT. RULED AND FIXED, SAME DAY (James, option (c)).**
   The finding, from the same harness, offsets from the top of the shell:

     BEFORE  shooter   .rd-trail__head     0 -> 76   .rd-tshell__head  24 -> 100
             console   .rd-pro-rail__head  0 -> 76   .rd-pro-top        0 -> 76

     AFTER   all four heads 0 -> 76, no system bar; one shared inset with one.

   The reserve was a fixed 24 inside `.rd-tshell__col`, so it insetted this head
   and not the rail beside it. Three facts decided it, and all three are now
   recorded in §4, which is what the reserve's section became:

   1. The strip was ruled (James, 2026-08-20) and drawn
      (`rd-tablet-kit.jsx:62`) — it was not drift, which is why it took a ruling
      to remove rather than a tidy-up.
   2. `kit-tablet-rail.css` §1 removed the rail's drawn `padding-top: 24` the
      same day, reasoning that "the 24 would push the head's hairline to y=100
      while `.rd-tshell__head`'s sits at y=76". **That premise was false while
      the strip rendered, which was always.** It is true now — which is the only
      reason that removal stands.
   3. The reserve was a hard 24 where the rest of the app uses
      `env(safe-area-inset-top)` (`--rd-shell-inset-top`, kit-shell.css), and
      `index.html` ships `viewport-fit=cover` with an OPAQUE
      `apple-mobile-web-app-status-bar-style: black`, which already insets the
      web view; a browser tab has no bar at all.

   So the inset is `env(safe-area-inset-top, 0px)`, declared once on `.rd-tshell`
   (§1) and once on `.rd-pro` (adm0-chrome.css). **THIS HEAD DECLARES NO TOP
   OFFSET OF ITS OWN AND MAY NOT GAIN ONE** — nor may the two rail heads, nor
   `.rd-pro-top`: a per-head offset is how the 24 comes back one room at a time.
   Pinned by `ruling_the_three_heads_share_one_top_offset`.

   ⚠️ **TWO DECLARATIONS DELIBERATELY NOT MATCHED TO THE CONSOLE, both flagged
   to Design with this change.** Neither changes the height of anything, and the
   ruling's subject is the box:

   * THE GUTTER. 28 here (`rd-tablet-kit.jsx:337`), 32 in `.rd-pro-top`. The tab
     band below (`.rd-stabs`), the activity band (`.rd-aband`) and the drawn
     panes in this room are all on 28, so moving only the head to 32 would fix
     one alignment across rooms by breaking three inside one. The rail heads
     took the console's 16 over the rows' 18 for the opposite reason — nothing
     in a rail head lines up with a row below it. VERTICALLY the two heads
     already agree exactly: 10 top, 16 bottom, a 1px hairline, `gap: 20`.
   * THE TITLE FACE. tdisp 900/62%/.01em here, 800/72%/.02em in the console —
     but the same 26px at the same `line-height: 1`, so the same 26px box. The
     ruling's "larger title face" clause does not bite (neither face is larger),
     and 62% is this room's display role everywhere else; the note on
     `.rd-tshell__title` already carries an open width question with Design.

   Pinned by `ruling_the_shooter_page_head_is_the_pro_page_heads_seventy_six`
   (`tablet_shell.rs`), which reads `.rd-pro-top` out of adm0-chrome.css and
   compares the geometry set declaration by declaration — so a change to the
   console's head must move both heads or go red — and which asserts the two
   deviations above AS deviations, so neither can be quietly "corrected" in
   either direction. */
.rd-tshell__head {
  flex: none;
  display: flex;
  align-items: flex-end;
  gap: 20px;
  padding: 10px 28px 16px;
  border-bottom: 1px solid var(--rd-line);
  box-sizing: border-box;
  height: 76px;
}

.rd-tshell__titles {
  flex: 1;
  min-width: 0;
}

/* rd-tablet-kit.jsx:339 ("fontSize: 26") — tdisp(900, 66, '.01em') at 26px,
   uppercase, line-height 1, no margin. tdisp is always uppercase; the transform
   is in the helper (substrate §2.1), so it is written out here rather than
   assumed.

   ⚠️ THE WIDTH IS 62 HERE AND THE DRAWING SAYS 66. A DELIBERATE DEVIATION,
   FLAGGED — not a transcription slip, and not a value to "correct" back.

   66 is outside the allowed width set (`ALLOWED_STRETCH` in cli/src/css.rs:
   62, 72, 75-78, 100), so `rangeday check css` refuses it by design. That guard
   carries the designer's own rule for admitting a new width, and his own test
   for whether one has been earned:

     "62 is right at 30px and slightly too tight below about 18px. Five sites
      had drifted to 66 for exactly that reason and I collapsed them because
      five sites are drift, not a rule. If a small-display size ever earns its
      own token, it will want something in the 64-66 range - as a named role
      with a stated size band, added to the allowed set deliberately."

   MEASURED BEFORE DEVIATING: 66 appears at exactly TWO sites in the whole
   corpus, both in rd-tablet-kit.jsx — this title at 26px (:338) and the RAIL
   WORDMARK at 15px (:174 ("fontSize: 15")). Two is under his own five-site
   threshold, so widening the allowed set on this change's authority would be
   the call-site improvisation the guard exists to stop.

   AND THE TWO SITES ARE NOT THE SAME CASE. His band statement decides them
   differently: 26px is "right at 30px" territory, where 62 is the correct
   width; 15px is "slightly too tight below about 18px", which is exactly why
   the drawing reached for 66 there. The site that actually needs 66 is the
   wordmark — and that belongs to RdTabletRail, which is not built. So the
   evidence for a new named role is stronger when the rail lands, and it should
   be argued there with both sites in hand rather than here with one.

   TO FLIP IT, if Design rules 66 the tablet display width: one line here, and
   one line in `ALLOWED_STRETCH` carrying the role, the band (15-26px) and the
   two citations. Pinned by `the_title_width_deviates_from_the_drawing_on_purpose`
   in tablet_shell.rs, so this cannot be quietly changed in either direction.

   ⚠️ ARGUED AND STILL 62, 2026-08-17. The rail has since landed and the second
   site is in hand: see the block on `.rd-trail__mark` in kit-tablet-rail.css,
   which measures the corpus at two 66 sites of which the BAND STATEMENT
   disqualifies one — leaving exactly one in-band site, against the seven that
   earned 72 its place in the allowed set. `ALLOWED_STRETCH` is untouched and
   this value does not move. The question is still open with Design; it is no
   longer waiting on evidence. */
.rd-tshell__title {
  font-weight: 900;
  font-stretch: 62%;
  font-size: 26px;
  letter-spacing: .01em;
  text-transform: uppercase;
  line-height: 1;
  margin: 0;
  color: var(--rd-fg);
  /* A screen's title is a drill or course name of unbounded length here, the
     same as .rd-shell__label's is. The drawing never exercises it — its longest
     title is "Schedule A Range Day" — so this is a defect fixed, not ported. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* rd-tablet-kit.jsx:340 ("marginTop: 7").

   ⚠️ `line-height` IS LOAD-BEARING HERE, NOT COSMETIC (2026-08-19). Without it
   this box inherits `body { line-height: 1.5 }` (_legacy-base.css) → a 12px
   font in an 18px line box, which makes the titles column measure
   26 + 7 + 18 = 51px against the head's 49px content box (§4: 76 less 26 of
   padding and 1 of border-bottom). That is the header bounce James caught on
   staging hopping /orgs → /profile/friends: every title+sub screen (/orgs,
   /run) overshot while every title+action screen sat on the floor, and a
   `min-height` floor cannot cap a tall header — it only raises short ones.

   The drawing got away without declaring it: `rd-train-kit.jsx:30` sets
   `tbody` to font-family only, so the drawn sub rendered at the UA's `normal`.
   1.25 restores that intent explicitly — 12 x 1.25 = 15px, so the column
   measures 26 + 7 + 15 = 48px, inside 49 rather than over it.

   ⚠️ ONE PIXEL OF HEADROOM, AND THE HEAD IS A HARD `height` NOW, so this is
   the tighter of the two constraints on the title block: raise the title's own
   26px, its 7px offset, or this line-height, and the subtitle CLIPS rather
   than growing the header. Pinned by
   `the_header_subtitle_declares_a_line_height`, which guards the DECLARATION
   and not the literal (a type-scale pass may retune the value; it may not
   delete the line). Delete it and that test goes red.

   ⚠️ THE TWO NUMBERS EITHER SIDE OF IT MOVED 2026-08-28 — 12 -> 12.5 and the
   7px offset -> 6 — so the arithmetic quoted above is the OLD arithmetic, kept
   because it is the reasoning that put the `line-height` here. The new column
   is 26 + 6 + 15.625 = 47.625 against the same 49px box: one and a third pixels
   of headroom instead of one. See the ruling note on the rule itself. */

/* ⚠️ **12 -> 12.5 AND 7 -> 6, RULED 2026-08-28 (James, the rehearsal pass) —
   ONE HEADER GEOMETRY ACROSS BOTH ROOMS.**

     "Every shooter page header is the Pro page header's box: hard 76, contents
      bottom-aligned, matching ProTop declaration by declaration. Where the two
      disagree the shooter head takes the Pro values, not the other way round."

   The two drawings disagree here, and inside the title block only here:
   `rd-tablet-kit.jsx:340` draws `fontSize: 12, marginTop: 7`;
   `rd-pro-kit.jsx:233`-`:234` draw `12.5` and `marginTop: 6`. Under the ruling
   the console's pair wins, so this rule transcribes `.rd-pro-top__sub`
   (adm0-chrome.css) rather than its own drawing, and the two title columns
   measure the same. That is the whole of the point: both heads are a hard 76
   with `align-items: flex-end`, so a title column that differs by even a third
   of a pixel puts the title on a different baseline in each room, which is the
   shift a shooter sees crossing the door.

   MEASURED, 1194 wide, dark, `getBoundingClientRect` on both title columns:
   48.0 (shooter) against 47.625 (Pro) before; 47.625 in both after.

   IT RELAXES THE BUDGET RATHER THAN TIGHTENING IT. Against the head's 49px
   content box (76 less 26 of padding and 1 of border-bottom): 26 (title,
   line-height 1) + 6 + 15.625 (12.5 at 1.25) = 47.625, so 1.375px of headroom
   where the 12/7 pair left 1.0.

   ⚠️ WHAT THIS RULING DID **NOT** MOVE, and both are flagged to Design rather
   than settled here — see the note on `.rd-tshell__head`: the head's 28px
   horizontal gutter (the console's is 32) and the title's own tdisp face
   (900/62% here, 800/72% in the console). Neither changes the height of
   anything; the ruling's subject is the box.

   Pinned by `ruling_the_shooter_page_head_is_the_pro_page_heads_seventy_six`
   in `tablet_shell.rs`, which reads BOTH parts and compares them declaration by
   declaration rather than asserting a literal twice. */
.rd-tshell__sub {
  font-size: 12.5px;
  line-height: 1.25;
  color: var(--rd-fg3);
  margin-top: 6px;
  /* ⚠️ THE FLOOR IS THE ANTI-JANK HALF OF THE 27 AUG RULING (James: no page
     header without a sub-line, ever). The settled line's own box, restated as a
     floor so the WAITING arm — which holds a 9px skeleton and no text —
     reserves exactly the same height. `.rd-tshell__head` is
     `align-items: flex-end`, so without this the title drops 6px at the moment
     a read lands: the bounce the 76px head height removed between screens,
     reappearing inside one. It cannot push the head over its budget either —
     it is what the settled line already measures (26 + 6 + 15.625 = 47.625
     against a 49px content box), so this raises nothing that was not there.

     ⚠️ `em`, NOT A PIXEL FIGURE, SINCE 2026-08-28. It was `15px`, which was
     12 x 1.25 written out — correct until the type moved to 12.5 under the
     same-geometry ruling, at which point a hand-computed floor is a second
     opinion about the line box that goes stale silently and reintroduces the
     0.625px of bounce it exists to prevent. `1.25em` tracks the size above and
     the `line-height` beside it by construction. `.rd-pro-top__sub--pending`
     carries the identical floor for the identical reason, and its own comment
     records the same 1.2 -> 1.25 correction. */
  min-height: 1.25em;
}

/* The waiting arm — the kit's loading line, centred in the reserved box.
   `.rd-skel--line` is 82% x 9px (kit-loading.css), so it needs the centring or
   it would sit at the top of the reserved box and read as a line that moved.
   The cap is this part's, not the skeleton's: a sub-line is a count or a date, and 82%
   of the titles column is a claim about a sentence. */
.rd-tshell__sub--wait {
  display: flex;
  align-items: center;
  max-width: 180px;
}

/* THE ACTION SLOT'S GEOMETRY, and only its geometry.
   rd-tablet-kit.jsx:342 ("minHeight: 44") — a 44px outlined box in the accent,
   18px of horizontal inset, tdisp(800, 78, '.1em') at 11px.

   44 IS THE TOUCH MINIMUM AND IT DOES NOT SHRINK BECAUSE A MOUSE MIGHT BE
   PRESENT — rd-tablet-kit.jsx:15 ("TOUCH SIZES DO NOT SHRINK"): "The same build
   serves an iPad and a laptop browser, and there is no way to know which is in
   front of you."

   ⚠️ --rd-acc, NEVER --rd-volt. This is a BORDER and TEXT, and per substrate
   §1.1 both take acc, which goes deep warm brown in light theme. Volt here yields an
   unreadable tan outline on the light ground.

   The slot's CONTENTS are the caller's — the drawing puts a bare label in it,
   and a real screen will put an RdButton or a link there. This rule sizes the
   box and paints its edge; it writes no copy and sets no cursor. */
.rd-tshell__action {
  flex: none;
  min-height: 44px;
  display: flex;
  align-items: center;
  padding: 0 18px;
  border: 1.5px solid var(--rd-acc);
  color: var(--rd-acc);
  font-weight: 800;
  font-stretch: 78%;
  font-size: 11px;
  letter-spacing: .1em;
  text-transform: uppercase;
  box-sizing: border-box;
}

/* ---------------------------------------------------------------------------
   6. The section-tab row slot — present only in destination mode.
   --------------------------------------------------------------------------- */
/* A reservation, like .rd-shell__nav: the shell decides whether the slot is
   occupied and never draws what is in it. The row's gaps, its 28px gutter and
   its selected underline belong to the row component
   (rd-tablet-kit.jsx:278 ("function TabTabs")), which lands with the rail.

   THE TAB ROW'S ABSENCE IS THE FEATURE — rd-tablet-kit.jsx:301 ("THE TAB ROW
   BELONGS TO DESTINATIONS"), ruled 17 Aug. See the component's doc.

   ⚠️ AMENDED 19 Aug — THE BAND ITSELF IS NOT ABSENT ANY MORE.
   `uploads/loading-and-rulings-reply.md` §2: "activity screens KEEP the 45px
   band, and the band is never blank - it carries the activity's own header:
   the back/exit control and the run's identity (course name, stage counter),
   in the same band geometry the section tabs occupy on destination screens."
   That band is `.rd-aband` (kit-tablet-activity-band.css), and it transcribes
   `.rd-stabs`'s geometry precisely so this slot measures the same on both kinds
   of screen — the content origin never moving is the ruling.

   ⚠️ THE BAND IS **UNDER** THE 76, NEVER INSIDE IT — and that is the drawing's
   own word, not an inference from the box model. `03 (Tablet) Training`, the
   pattern document, ruled 15 Aug: *"Every rail row is a destination; depth is a
   tab row **under the page header**."* The same paragraph explains what the
   band is claiming — *"four tabs under it read as a fifth section"* — which
   only parses if the tabs sit below the title and its sub, outside the head's
   box. So the slot is the head's SIBLING in the column (see `tablet_shell.rs`'s
   `rsx!`: status, head, tabs, content), the head keeps its hard 76 whether the
   band is occupied or not, and a head measured with a band under it is still 76
   — verified in the harness on 2026-08-28 as part of the one-header-geometry
   ruling. Pinned by `the_tab_band_sits_under_the_seventy_six_head_not_inside_it`.
   The console holds the same line for the row it does have above the fold:
   `.rd-prom__back` is *"a SIBLING of `.rd-pro-top`, never a child — and the
   hard 76 above stays a hard 76 whether the row is there or not"*
   (adm0-chrome.css, the form-act/back-row block). Same reading, both rooms.

   THIS RULE IS UNCHANGED AND MUST STAY UNCHANGED. `flex: none` and nothing
   else: the slot still reserves and still paints nothing, and the day it grows
   a height or a border of its own, the two occupants stop being able to differ
   and the reservation becomes a third opinion about a band two parts already
   describe. */
.rd-tshell__tabs {
  flex: none;
}

/* ---------------------------------------------------------------------------
   7. The content region — the only thing that scrolls.
   --------------------------------------------------------------------------- */
/* rd-tablet-kit.jsx:346 ("flex: 1, minHeight: 0"). `min-height: 0` is the same
   single most important declaration it is in kit-shell.css: a flex child
   defaults to min-height:auto and refuses to shrink below its content, so as
   soon as the content exceeds the fold the column grows and the region stops
   being what scrolls.

   `display: flex` with NO direction, deliberately: the drawn content region is a
   ROW, because the two-pane case is two children side by side
   (rd-tablet-kit.jsx:356 ("function TabPanes")). A single pane is the same region
   with nothing beside it. That is the opposite of .rd-shell__content, which is a
   column, and it is the difference between a phone screen and a tablet one.

   NO GUTTER. The phone shell supplies one; this one does not, because a tablet
   screen's panes carry their own insets and a shell-level gutter would inset the
   pane BORDERS too — the 1px rule between list and detail would stop reaching
   the shell's edges. Every drawn screen pads inside its panes. */
.rd-tshell__content {
  flex: 1;
  min-height: 0;
  display: flex;
  overflow: hidden;
}

/* ---------------------------------------------------------------------------
   8. The breakpoint.

   ⚠️ THIS NUMBER HAS A TWIN IN RUST: `TABLET_MIN_WIDTH_PX` in
   apps/web/src/device.rs, which carries the full argument for 1024 and its two
   rejected candidates (768, 1194). The two cannot share a literal across
   languages, so they are pinned to each other by a test that reads THIS FILE —
   `device::tests::the_css_breakpoint_is_the_rust_breakpoint`. A query whose
   number drifts from the constant does not error and does not warn; it simply
   stops firing where the Rust thinks it fires.

   THERE IS EXACTLY ONE `@media (min-width:` IN THIS FILE, and the test asserts
   that too. Two queries with two numbers is the same defect wearing a matching
   value on one of them.

   ⚠️ `AND (ORIENTATION: LANDSCAPE)`, ADDED 2026-08-18 — RULED, NOT A STOPGAP.
   James, 2026-08-18: a portrait-orientation tablet classifies as Phone. Full
   argument in device.rs's module doc ("The portrait question, ruled") and the
   corpus's own framing of the same call, `03 (Tablet) Training.html:69`
   ("Landscape only, ruled 15 Aug."): "Portrait would be the phone layout at a
   larger size, which is a stretch, not a design." The reason the clause lives
   here and not just in Rust: web cannot lock landscape natively — the Screen
   Orientation API's lock() needs fullscreen and iPad ignores the manifest's
   landscape hint — so a portrait iPad really does reach this stylesheet at
   >=1024px wide, and without this clause it would fire the tablet geometry
   while DeviceClass::from_viewport (device.rs) has already refused it Tablet.
   Pinned on both sides by the same test named above, which now also asserts
   this clause is present on the one declaration line — not merely mentioned in
   prose, which is a distinction that test learned the hard way once already
   (see the "A DECLARATION, not a mention" note in device.rs's twin test).

   WHAT THE QUERY DOES, AND WHAT IT DELIBERATELY DOES NOT DO. It sets the shell's
   own drawn geometry, which is only correct at tablet width. It does NOT lift
   the phone shell's 448px cap — see the block in kit-shell.css that says why,
   and the ruling test beside it. A blanket lift would widen 40-odd screens whose
   markup was written against a 448px column, all at once, on the strength of a
   viewport width.
   --------------------------------------------------------------------------- */
/* WHAT THE QUERY SWITCHES: the horizontal overflow of the shell root, and
   therefore what happens when this shell is rendered narrower than it was drawn
   for.

   BELOW THE BREAKPOINT the base rule in §1 leaves `overflow-x: auto`. That state
   should not be reachable — the device signal picks the phone shell below 1024
   — but "should not be reachable" is a claim about today's call sites, not a
   property of the component, so it is handled rather than asserted. The drawn
   rail is 208px and the drawn list beside it is 380px; on a 390px phone those
   two would be crushed to roughly half each, which does not look like a bug, it
   just looks bad. Scrolling looks like exactly what it is.

   AT AND ABOVE THE BREAKPOINT, IN LANDSCAPE, the shell fits by construction,
   and the arithmetic is one of the reasons 1024 is the number: 208 rail + 380
   list leaves 436 for the detail pane at the floor. So the horizontal scroll is
   switched off and the root clips as drawn (rd-tablet-kit.jsx:325,
   "position: 'relative'" — the same element carries overflow hidden there).
   "IN LANDSCAPE" is doing real work in that sentence since 2026-08-18: a
   portrait iPad at exactly the floor width (1024) is exactly the case the
   orientation clause exists for, and `.rd-tshell` should not even be in the
   DOM there — the device signal renders the phone shell for it — but this rule
   is written for what it protects against if that ever stops being true, the
   same "handled rather than asserted" posture the paragraph above takes with
   the phone case.

   ⚠️ NO HEIGHT RULE HERE, deliberately. The drawn canvas is 1194x834
   (rd-tablet-kit.jsx:1 ("Landscape only, 1194x834")), but a canvas is where the
   ink was put and not a constraint.

   ⚠️ AND NO MAX-WIDTH HERE EITHER — but the reason CHANGED on 2026-08-17 and
   the block that used to say "nothing here caps anything" was wrong the moment
   it was read. The desktop-growth question is ruled on `31 (Desktop) Pro
   Shell` ("The desktop growth question, ruled 18 Aug"): the tablet layout IS
   the desktop layout, both floors stop at 1600, and the rail does not grow.
   The cap therefore exists, and it is in §3 on `.rd-tshell__frame` rather than
   here, for two reasons: it is a property of the FRAME (rail + column
   together) and not of the ground, and it needs no query at all — `max-width`
   is inert below the width it names, so a media query around it would only
   add a second `min-width` breakpoint to a file whose whole §8 argument is
   that there must be exactly one. Read §3 for the geometry, the centring
   mechanism and the reading behind both. */
@media (min-width: 1024px) and (orientation: landscape) {
  .rd-tshell {
    overflow-x: hidden;
  }
}
/* ==== kit-tablet-rail · RdTabletRail, the shooter tablet's rail · owner: spine ==== */

/* Drawn as `TabRail` in rd-tablet-kit.jsx:170 ("function TabRail"), over the row
   set `TAB_RAIL` at rd-tablet-kit.jsx:117 ("const TAB_RAIL"). Component:
   apps/web/src/components/kit/tablet_rail.rs, which carries the ratification,
   the landing-route table, the derived-subtitle contract and the width
   decision. Only the values live here.

   ANATOMY:

     [ head: mark + RANGE DAY + sub-line, a hard 76 — the Pro OrgHead's box,
       ruled 2026-08-28; it was `wordmark, 0 18px 18px` until then ]
     [ rows: seven, 56px each, 3px left rule + surf2 ground when selected ]
     [ foot: the door into Pro — .rd-door, kit-switch-door.css, STAFF ONLY ]

   ⚠️ THE FOOT IS NOT DECLARED HERE, and it is not a row. Added 2026-08-20 from
   `44 The Doors.html` row 02: the door is one fixture drawn once and mounted at
   the foot of BOTH rails — this one and the Pro rail — so its rules live in its
   own part and this file declares nothing about it but the `flex: none` on the
   rows above it. "The shooter door sits below a spacer and a hairline — a
   fixture under the sections, never an eighth rail row. Rail rows are flush
   ground; the door is an outlined object."

   ⚠️ AND IT IS OFTEN ABSENT. The door renders only for an account holding admin,
   instructor or RO in a team org: "Non-staff accounts: no fixture at all, per
   the 6 Aug gate. The rail simply ends — not a locked door, no door." Nothing in
   this file may assume a last child.

   ⚠️ RATIFIED AS DRAWN — James, 2026-08-17, via the coordinator. Seven flat
   rows, fixed 208px, NON-COLLAPSIBLE, no children; depth lives in the
   horizontal section tabs per the 15 Aug rework. Every number in this file is
   read off the drawing above and every one of them is now a ruled value rather
   than a transcription: changing one is a designer question. Dry Fire and Range
   Bag are NOT rows — they stay Home tiles, which `rail_for` already encodes.

   ⚠️ 56, NOT 52 — THE CORPUS KIT HEADER IS STALE. rd-tablet-kit.jsx:10 ("its
   rows are 52px") says the rows "are 52px rather than the console's 38". Its
   own drawn row is `minHeight: 56` (rd-tablet-kit.jsx:181 ("minHeight: 56")),
   and `03 (Tablet) Training` says 56 twice — in the decided block ("56px rows
   rather than the console's 38") and again in the rail row's own notes ("Rows
   are 56px, not the console's 38"). James ratified 56 on 2026-08-17. So the
   prose is stale and the drawing was right, which is the same "if a drawing and
   a written answer disagree, the drawing wins" precedent the tab bar's label
   carries. Quote that header at your peril: its 52 is stale per ratification.

   ⚠️ THIS IS NOT THE PRO RAIL. rd-tablet-kit.jsx:9 ("THE RAIL IS NOT THE PRO
   RAIL") and `03 (Tablet) Training`: "It is not the Pro rail. Same shape,
   different app: no org mark, 56px rows rather than the console's 38 because
   this is held at arm's length, and the shooter kit's volt selection rather
   than the console's grey. A rail is a shape, not a brand." So: `--rd-acc` and
   `--rd-surf2` here, never the console's greys, and NO org mark in the head.

   ⚠️ THE HEAD'S BOX IS THE PRO HEAD'S SINCE 2026-08-28 (§2) AND "NO ORG MARK"
   STILL HOLDS. The ruling matched the head's GEOMETRY to `OrgHead` so the rail
   does not shift when you cross the door; it did not import the console's
   CONTENT. The mark in it is the APP's mark, the name line is the product's
   name, and the sub-line is the shooter's own tag — no org initials, no org
   name unless that is the only identity the session holds, and no menu. Rows,
   selection and ground are untouched.

   ⚠️ NO HOVER TREATMENT, AND THE ABSENCE IS DRAWN. rd-tablet-kit.jsx:145 ("anchor
   inherits every style the div had"): "there is deliberately NO hover treatment -
   a hover state invented for the convenience of reading the corpus would end up
   quoted back as a design decision." Nothing here sets one. If one is ever
   ruled in, it goes under `@media (hover: hover)` and NEVER under a width
   query. `31 (Desktop) Pro Shell`, "The desktop growth question, ruled 18
   Aug": "Hover and keyboard affordances are not a breakpoint in either
   direction: they key on the input, not the width - `hover:hover` and
   `focus-visible` ship at every size, so a mouse at 1024 gets all of them and
   a touch tablet at 1280 gets none." A wide viewport says nothing about what
   is in front of it. */

/* ---------------------------------------------------------------------------
   1. The rail — 208px, full height, its own ground.
   --------------------------------------------------------------------------- */
/* rd-tablet-kit.jsx:172 ("width: RAIL_W") over rd-tablet-kit.jsx:19
   ("RAIL_W = 208").

   ⚠️ 208 IS DECLARED TWICE ON PURPOSE, HERE AND ON `.rd-tshell__rail`. The
   shell's is a RESERVATION — it holds the column open and paints nothing — and
   this one is the rail's own width, so the component stands up outside the
   shell (the `/kit` review surface mounts it bare). Same drawn value, one
   citation, two owners; they cannot disagree without one of the two pins going
   red.

   `overflow: hidden` and NOT `auto`, for the reason `.rd-tshell__rail` states:
   membership is closed at seven, and seven ratified 56px rows is 392px of rows
   inside an 834px canvas. The rail cannot overflow the shortest drawn viewport.

   The ground is `--rd-surf` and the edge is `--rd-line` — the drawn
   `background: t.surf` and `borderRight: 1px solid t.line`
   (rd-tablet-kit.jsx:172).

   ⚠️ **THE DRAWN `padding-top: 24` IS GONE, AND ITS REMOVAL IS THE RULING
   BELOW, NOT A TIDY-UP** (James, 2026-08-28, the rehearsal pass). It was "the
   air above the wordmark" — never a status-strip allowance, since the strip
   sits over the CONTENT pane and "the rail starts below nothing"
   (rd-tablet-kit.jsx:58 ("the rail starts below nothing")). The head is a hard
   76 with its own bottom-aligned padding now (§2), so the air it used to hold
   is INSIDE that box. Left in place, the 24 would push the head's hairline to
   y=100 while `.rd-tshell__head`'s sits at y=76 — the rail's own bottom border
   off the page head's, which is the exact seam the 76 exists to keep straight,
   and a 24px vertical jump in the rail every time you cross between the shooter
   app and Pro.

   ⚠️ **THE SENTENCE ABOVE WAS WRONG ON THE DAY IT WAS WRITTEN AND IS TRUE NOW
   — corrected 2026-08-28, same day.** `.rd-tshell__head`'s hairline did NOT sit
   at y=76: the page column carried a fixed 24px status reserve above the head
   (`.rd-tshell__status`), so the page head ran 24 -> 100 while this head ran
   0 -> 76. Measured at 1194 wide in the assembled sheet. Removing the 24 here
   therefore did not close the seam — it OPENED it, which is what James caught
   at the rehearsal ("the shooter page headers render taller").

   He ruled it the same day, option (c): the fixed reserve goes, and the top
   inset becomes `env(safe-area-inset-top, 0px)` declared once on each shell
   ROOT — `.rd-tshell` (kit-tablet-shell.css §1) and `.rd-pro`
   (adm0-chrome.css) — above BOTH columns instead of inside one of them. With
   the reserve gone the conclusion above rests on a premise that is now true,
   and this rail keeps no top inset of its own: all four heads across the two
   rooms start at the same y.

   ⚠️ SO THIS RULE MUST NOT REGAIN A `padding-top`, AND NEITHER MAY §2's HEAD.
   A per-rail or per-head offset is how the 24 comes back one room at a time,
   and it is invisible in review because each site looks locally reasonable.
   Pinned by `ruling_the_three_heads_share_one_top_offset` (`tablet_shell.rs`),
   which reads this part.

   Body family is set here rather than inherited: inside the shell it would
   arrive from `.rd-tshell`, and mounted bare it would not. */
.rd-trail {
  width: 208px;
  flex: none;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--rd-surf);
  border-right: 1px solid var(--rd-line);
  font-family: 'Archivo', sans-serif;
}

/* ---------------------------------------------------------------------------
   2. The head — mark, wordmark, sub-line. THE PRO ORG HEAD'S GEOMETRY.
   --------------------------------------------------------------------------- */
/* ⚠️ **RULED 2026-08-28 (James, the rehearsal pass) — THIS HEAD IS THE PRO
   RAIL'S `OrgHead`, MEASURE FOR MEASURE.** It shipped as
   `padding: 0 18px 18px` around a wordmark and nothing else
   (rd-tablet-kit.jsx:173 ("padding: '0 18px 18px'")), which is what
   `51 Shell Lab` and `rd-tablet-kit.jsx` still draw — a head SMALLER than the
   console's.

   The ruling:

   > On the tablet PWA the shooter rail's "RANGE DAY" head must match the SIZE
   > and geometry of the Pro rail's `OrgHead`, so switching rooms does not shift
   > the rail.

   A shooter who crosses the door into Pro and back is looking at the same
   208px column in the same place on the same glass. With a 0/18/18 head over a
   24px rail inset on one side and a hard-76 bottom-aligned head on the other,
   every row below jumped by ~26px on the crossing — chrome that moves under a
   navigation that did not ask it to. So the numbers below are
   `rd-pro-kit.jsx:123` ("height: 76") verbatim, not values chosen here:

     height 76 (HARD, no floor) · padding 10px 16px 16px · border-bottom 1px
     align-items: flex-end · gap 11 · mark box 34 · name 13.5/800/78%/.03em

   ⚠️ **DEVIATION FROM `51 Shell Lab` / `rd-tablet-kit.jsx:173-174`, BY
   RULING.** Both still draw this head smaller. That disagreement is recorded
   for the designer to draw back rather than silently transcribed either way,
   and the redraw request is in the relay note of 2026-08-28. A later reader
   who finds the smaller head in the drawing is reading a document that has not
   caught up with a ruling — do not "correct" this back.

   ⚠️ **THE HARD 76 IS A CAP AND A FLOOR AT ONCE, so this rule asserts the
   ABSENCE of `min-height` as well as the presence of `height`** — the same
   reading `.rd-pro-rail__head` (adm0-chrome.css) and `.rd-tshell__head` carry:
   "content that doesn't fit gets redesigned, never a taller bar". Everything
   in the head is therefore bounded: the wordmark is a constant, the sub-line
   is one ellipsised line, and the waiting arm reserves that line's exact box.

   ⚠️ **THE 16px INSET IS THE CONSOLE'S, AND THE ROWS BELOW KEEP THEIR DRAWN
   18** (rd-tablet-kit.jsx:182 ("padding: '9px 18px'")), so the head's mark sits
   2px outside the rows' left inset. Taken deliberately: the ruling is that the
   head does not move when the room changes, and the Pro rail's rows are on 16
   — matching the rows here instead would move the mark 2px on every crossing,
   which is the thing being fixed. Flagged to the designer with the redraw.

   ⚠️ NO ACCOUNT CHIP AND NO AVATAR — that half of `03 (Tablet) Training` is
   untouched: "No org mark and no account chip in the rail head. This is one
   person's app". What the ruling adds is an identity LINE (§2c), which is a
   sub-line of the product's name and not a chip, a control or a menu. The Pro
   head is a control (`button.rd-pro-rail__head`); this one is still not one. */
.rd-trail__head {
  flex: none;
  height: 76px;
  box-sizing: border-box;
  padding: 10px 16px 16px;
  border-bottom: 1px solid var(--rd-line);
  display: flex;
  align-items: flex-end;
  gap: 11px;
}

/* The identity column beside the mark — `rd-pro-kit.jsx:126`
   ("flex: 1, minWidth: 0"), the same two lines in the same box. */
.rd-trail__ident {
  flex: 1;
  min-width: 0;
}

/* 2a. THE MARK — the app-icon mark, in the Pro `OrgMark`'s 34px box.

   ⚠️ **THIS SLOT USED TO BE THE WORDMARK ARTWORK** (`brand::TYPE_WHITE`,
   `rd-type-white.svg`, sized to the drawn type's 11px cap height). The 28 Aug
   ruling splits the head into the Pro head's two parts — a MARK and a NAME
   LINE — so the wordmark is type again (§2b) and this box holds a mark.

   WHICH MARK, AND WHY IT IS NOT THE SQUARE LOCKUP. Three candidates were on
   the shelf (`brand.rs`): the square lockup in white/black, the horizontal
   wordmark, and `ICON_MARK_B` — the corpus's icon study `57 Icon Mark Drafts`
   mark B, an ink RD monogram on its own `#A89968` ground, already the source of
   every OS-level icon this app ships.

   - The square lockup is drawn at 134-155px everywhere it ships (`s02-auth.css`
     §2) and carries "powered by T.REX ARMS" inside the artwork; at 34px that
     line is illegible mush, and the lockup is near 1:1 against a 34px box.
   - The wordmark is a strip — it cannot fill a square box at all, and its words
     are now the name line beside it, which would say the product's name twice.
   - Mark B is drawn FOR a small square (a 512x512 viewBox with a full-bleed
     ground), which is the same job the Pro `OrgMark`'s 34px chamfered square
     does, and it reads at that weight rather than louder or quieter.

   AND IT IS THEME-INVARIANT BY CONSTRUCTION, which is the other half of the
   pick. Its ground is part of the drawing (`fill="#A89968"`) and its monogram
   is ink (`#0B0B0B`), so it reads on `--rd-surf` in both themes with no rule of
   its own. That is what let the light-theme `filter: invert(1)` stopgap below
   be DELETED rather than carried forward — see §2d.

   ⚠️ THE 62-vs-66 QUESTION BELOW IS STILL OPEN. It has NOT been resolved by
   this change, and it must not be read as resolved: the argument is preserved
   verbatim because it was never only about this rule. Its OTHER site,
   `.rd-tshell__title` (kit-tablet-shell.css), is still type and still ships
   62 against a drawn 66. What moved, twice: the 2026-08-25 logo pass stopped us
   drawing the in-band 15px witness as type at all, and the 28 Aug ruling put
   type back in the head at the PRO NAME LINE's role instead — 13.5px at
   `font-stretch: 78%`, an already-allowed width. So the corpus's 15px/66 site
   has no counterpart in our build in either direction, which if anything
   weakens the case for admitting 66 to `ALLOWED_STRETCH` further. Pinned by
   `the_sixty_two_versus_sixty_six_question_is_still_open` in tablet_rail.rs.

   ─── the argument as it stood, unedited ───

   THE WIDTH IS 62 HERE AND THE DRAWING SAYS 66. DECIDED, WITH THE EVIDENCE
   IN HAND — this is the site #830 deferred the question to, and the answer is
   still 62.

   #830 shipped `.rd-tshell__title` at 62 and wrote: "the site that actually
   needs 66 is the wordmark - and that belongs to RdTabletRail, which is not
   built. So the evidence for a new named role is stronger when the rail lands,
   and it should be argued there with both sites in hand rather than here with
   one." The rail has landed. Argued, with both sites in hand:

   THE COUNT. 66 appears at exactly TWO sites in the whole corpus, both in
   rd-tablet-kit.jsx — this wordmark at 15px (:159) and the shell title at 26px
   (rd-tablet-kit.jsx:339 ("fontSize: 26")). The designer's own test is "five
   sites are drift, not a rule"; five was not enough, and two is fewer.

   THE BAND. His statement decides the two sites differently: "62 is right at
   30px and slightly too tight below about 18px." 26px is in the range where 62
   is correct, so the shell title is not evidence for 66 at all — which leaves
   exactly ONE in-band site in the corpus, this one. One site is a site, not a
   named role, and `ALLOWED_STRETCH`'s own admitted precedent (72, the Pro
   page-title role) arrived on seven sites with one weight, one letter-spacing
   and one size band. There is no comparable body of evidence here.

   THE ASYMMETRY THAT DECIDES IT. Shipping 62 at 15px is display type that is
   slightly tight — visible, and one line to flip. Admitting 66 to
   `ALLOWED_STRETCH` widens the set permanently for every future call site, on
   the strength of one drawn number, and the guard exists precisely to stop a
   width arriving that way. A ratchet is loosened deliberately or not at all.

   SO: 62 SHIPS, AND THE SHELL TITLE DOES NOT MOVE. Both sites stay at 62 and
   the question stays open rather than closed by an implementer.

   TO FLIP IT, if Design rules a small-display role: one line in
   `.rd-tshell__title` and one entry in `ALLOWED_STRETCH` (cli/src/css.rs)
   carrying the role name, the band (15-26px) and both citations — which is the
   guard's own documented standard for admitting a value. (This rule used to be
   the other line; it is artwork now, so the flip is one site smaller than the
   argument above describes.)

   15px was above the tablet type floor (9.5px for tracked uppercase — `01
   Foundations` §05 Type Scale, "Two floors, one exemption"), so the SIZE was
   the drawn one.

   ─── end of the preserved argument ───

   THE BOX IS 34, WHICH IS THE PRO `OrgMark`'s DEFAULT AND NOT A NUMBER CHOSEN
   HERE: `components/pro/org_mark.rs` ("const DEFAULT_SIZE: f32 = 34.0") over
   `rd-pro-kit.jsx:108` ("size = 34"). `object-fit: contain` because the mark's
   own viewBox is square and the box is square — the property is there so a
   re-vendored asset with a different aspect letterboxes inside the ruled box
   instead of silently resizing the head. */
.rd-trail__mark {
  display: block;
  flex: none;
  width: 34px;
  height: 34px;
  object-fit: contain;
}

/* 2b. THE NAME LINE — the Pro rail's org-name role, at its own numbers.
   `rd-pro-kit.jsx:127` and `.rd-pro-rail__org-name` (adm0-chrome.css):
   `pdisp(800, 78, '.03em')` at 13.5px, line-height 1.15, `--rd-fg`.

   ⚠️ IT IS A CONSTANT, SO IT IS ONE LINE AND IT NOWRAPS. The Pro line wraps to
   two and is never ellipsised (C-44.1) because an ORG NAME is user-supplied and
   can be anything. This line is the string "Range Day", which fits 208px at
   13.5px with room to spare, and a wrap here could only ever be a defect.
   `text-transform` rather than an uppercase literal: the component holds the
   product's name in its written form (`WORDMARK`, "Range Day") because that is
   also its accessible name.

   Theme-correct by TOKEN, not by a `[data-theme]` rule: `--rd-fg` is `#FFFFFF`
   dark and `#0B0B0B` light (`rd-train-kit.jsx:19-20`), which is exactly what the
   drawn head did (`rd-tablet-kit.jsx:174`, `color: t.fg`) before the logo pass
   traded it for fixed-white artwork. */
.rd-trail__name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .03em;
  font-size: 13.5px;
  line-height: 1.15;
  color: var(--rd-fg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 2c. THE SUB-LINE — the shooter's tag, else their org, else the reserved box.
   The Pro head's second line is the ROLE (`.rd-pro-rail__org-role`, 9px tracked
   uppercase from a fixed `ROLE_LABEL` table). This one is the same SLOT — same
   4px offset, same `--rd-fg3`, inside the same 76 — carrying the one fact this
   side of the app has about who is holding it (`tablet_rail.rs::rail_head_sub`).

   ⚠️ **11px, NOT THE PRO LINE'S 9, AND THAT IS THE TYPE FLOOR RATHER THAN A
   DEVIATION FROM THE RULING.** The ruling is about the head's GEOMETRY; the
   9px on the console line is legal because `ROLE_LABEL` is tracked uppercase
   (floor 9.5px) and the console is not a tablet document. This line is a `@tag`
   or an org name — mixed-case body copy — and `01 Foundations` §05 floors that
   at 11px on the tablet, the same floor `.rd-trail__sub` ships under. The
   height budget absorbs it: 13.5x1.15 + 4 + 11x1.3 = 33.8px against the head's
   49px content box (76 - 10 - 16 - 1).

   ⚠️ ONE LINE, ELLIPSISED — the opposite of the Pro name line's ruling, and for
   the opposite reason. There the name IS the fixture's point, so it wraps
   rather than cuts; here the head's point is the product's name above it, and a
   long org name must not be allowed to push a hard 76.

   ⚠️ THE FLOOR IS THE ANTI-JANK HALF, exactly as `.rd-tshell__sub` carries it:
   the head is `align-items: flex-end`, so a sub-line that is absent until a
   session read lands drops the mark and the name at the moment it does. 11 x
   1.3 = 14.3, the settled line's own box, restated as a floor so the waiting
   arm reserves the same height. */
.rd-trail__head-sub {
  font-size: 11px;
  line-height: 1.3;
  color: var(--rd-fg3);
  margin-top: 4px;
  min-height: 14.3px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The waiting arm — the kit's loading line centred in the reserved box, the
   same construction and the same citation as `.rd-tshell__sub--wait`:
   `43 Loading States.html:52`, "the choice is a true skeleton or this, never
   text". The cap is this part's: the sub-line is a handle or an org name, and
   82% of a 208px rail is a claim about a sentence. */
.rd-trail__head-sub--wait {
  display: flex;
  align-items: center;
  max-width: 120px;
}

/* 2d. ⚠️ **THE LIGHT-THEME `filter: invert(1)` STOPGAP IS GONE — DELETED, NOT
   MOVED, AND THE HEAD IS STILL THEME-REACTIVE.**

   What stood here was a whole rule and a designer rider. Its argument, kept
   because it is why this section can now be two lines: `rd-tablet-kit.jsx:174`
   draws the head as TYPE at `color: t.fg` — `#FFFFFF` dark, `#0B0B0B` light
   (`rd-train-kit.jsx:19-20`) — so the drawn head FLIPS with the theme. The
   2026-08-25 logo pass replaced that type with `brand::TYPE_WHITE`, fixed
   `#FFFFFF`, over a rail ground of `var(--rd-surf)` that light theme sets to
   `#FFFFFF` (`_legacy-tokens.css:182`): white on white, and the one thing in
   the rail that names the product was invisible. `invert(1)` restored the drawn
   behaviour and was described honestly as a stopgap, with the rider that the
   real answer was an asset James had not been asked for.

   The 28 Aug ruling answers it without one. The head's two theme-bearing parts
   now carry their own colour the way the kit rules colour should be carried
   (`chamfer_box.rs`, architect ruling ②: *"theme-conditional treatments are
   expressed as token values wherever a value can carry them"*):

   - the NAME LINE is type again and takes `--rd-fg` (§2b), which IS `t.fg`;
   - the MARK is `ICON_MARK_B`, whose ground and ink are inside the artwork
     (§2a), so it needs no theme treatment at all.

   So there is no structural `[data-theme]` rule left in this part, and no
   filter. `run_shooter.rs` cited this rule as the kit's one legitimate
   structural-light exception; that citation has been updated rather than left
   pointing at a deleted rule. */

/* ---------------------------------------------------------------------------
   3. The rows.
   --------------------------------------------------------------------------- */
/* rd-tablet-kit.jsx:176 ("display: 'grid'"). A grid of one column, which is what
   the drawing writes and what keeps every row the same width regardless of its
   subtitle's length. */
/* `flex: none` ADDED 2026-08-20 WITH THE FOOT, and it is a fix rather than a
   tidy-up. The rail is a flex column and this grid was `flex: 0 1 auto` by
   default, so once a fixture with `margin-top: auto` sat below it (the door —
   `.rd-door`, kit-switch-door.css), a tall enough rail would have taken the
   difference out of the ROWS rather than out of the space between them. Seven
   ratified 56px rows do not shrink; the door has no height to lose to. */
/* `padding-top: 10px` ADDED 2026-08-28 WITH THE HEAD, and it is the head's
   change rather than a taste edit: the head now draws a `border-bottom`, and
   the drawn 24px of air that used to sit above it moved inside it. Without this
   the first row is flush against that new hairline, which reads as a row with a
   top border. 10 is the Pro rail's own list inset (`.rd-pro-rail__list`,
   adm0-chrome.css; `rd-pro-kit.jsx:211`, "paddingTop: 10") — the same figure
   under the same hairline, for the same reason. */
.rd-trail__rows {
  display: grid;
  flex: none;
  padding-top: 10px;
}

/* rd-tablet-kit.jsx:181 ("minHeight: 56") and rd-tablet-kit.jsx:182
   ("padding: '9px 18px'").

   56 IS RATIFIED AND IT IS ALSO WELL OVER THE TOUCH FLOOR. The kit's 44px
   minimum (rd-tablet-kit.jsx:15) is satisfied with 12px to spare, so nothing
   here needs a separate min-height for reachability — which is worth stating,
   because the next reader looking for the 44 will not find one and should not
   add one.

   `min-height`, not `height`: a subtitle that wraps to two lines grows the row
   rather than clipping. The drawn subtitles are one line each, so this is a
   defect fixed rather than ported.

   THE 3px LEFT RULE IS DECLARED ON EVERY ROW AND COLOURED ON ONE. Declaring the
   border only in the `--on` rule would shift all seven rows sideways by 3px the
   moment a row lit up. `transparent` here, `--rd-acc` there.

   ⚠️ --rd-acc, NEVER --rd-volt. This is a BORDER, and per substrate §1.1 a
   border takes acc, which goes deep warm brown in light theme. Volt yields an
   unreadable tan rule on the light ground. `03 (Tablet) Training` calls it
   "the shooter kit's volt selection" — that names the IDIOM, not the token. */
.rd-trail__row {
  min-height: 56px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  padding: 9px 18px;
  border-left: 3px solid transparent;
  text-decoration: none;
}

/* rd-tablet-kit.jsx:182 ("padding: '9px 18px'") — `borderLeft: 3px solid t.acc`
   and `background: t.surf2` on the selected row. */
.rd-trail__row--on {
  border-left-color: var(--rd-acc);
  background: var(--rd-surf2);
}

/* No drawn focus state, so this is the house treatment: the kit's 2px
   `--rd-acc` ring, `:focus-visible` so a pointer press does not leave one
   behind. THE OFFSET IS NEGATIVE, and for the same reason
   `.rd-repstep__btn:focus-visible` in kit-rep-stepper.css uses a negative one —
   the rows are flush against each other and against the rail's own edge, so a
   +2px ring would paint over the row above, the row below, and the 3px
   selection rule it sits on top of.

   An outline is safe here because nothing in this rail clips: there is no
   chamfer anywhere in the chrome (substrate §1.5), which is the same argument
   `.rd-shell__back:focus-visible` makes. */
.rd-trail__row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
  border-radius: 0;
}

/* rd-tablet-kit.jsx:183 ("fontSize: 12.5") — tdisp(800, 78, '.06em') at 12.5px,
   `color: on ? t.fg : t.fg2`. Quiet by default; full strength when lit.

   12.5px tracked uppercase is above the tablet floor of 9.5px for tracked
   uppercase (`01 Foundations` §05 Type Scale, "Two floors, one exemption"), so
   the drawn size ships unchanged. */
.rd-trail__label {
  font-weight: 800;
  font-stretch: 78%;
  font-size: 12.5px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--rd-fg2);
}

/* FULL-STRENGTH LABEL ON THE LIT ROW — the drawn `on ? t.fg : t.fg2`.

   ⚠️ THE PROSE SAYS VOLT AND THE DRAWING SAYS FULL-STRENGTH, AND THE DRAWING
   WINS. `03 (Tablet) Training`'s rail notes read "Volt left rule and volt label
   for the selected row"; the drawn label is `t.fg` (rd-tablet-kit.jsx:183), a
   plain full-strength foreground, and the ratification James gave on 2026-08-17
   describes the selection as the 3px volt left rule plus the surf2 ground plus
   a full-strength label. Same shape as the 52/56 mismatch one section up, and
   resolved the same way. Recorded rather than silently transcribed, because a
   later reader with the prose in hand would otherwise "fix" this to
   `var(--rd-acc)` and get a tan word on a lit row. */
.rd-trail__row--on .rd-trail__label {
  color: var(--rd-fg);
}

/* rd-tablet-kit.jsx:186 ("lineHeight: 1.3") — `...tbody, fontSize: 11,
   color: t.fg3, lineHeight: 1.3`. `tbody` is font-family only, so NO
   `font-stretch` is authored here and 100 arrives as the CSS initial value,
   which is what the body role means everywhere in this kit.

   THE DRAWING AND THIS SHEET NOW AGREE AT 11, AND THE HISTORY IS THE POINT.
   The drawing said 10 and this sheet shipped 11, floored rather than
   transcribed, against a ruling that had been given but not yet exported. The
   18 Aug export lands it in the corpus twice over: `01 Foundations` §05 Type
   Scale gained the tablet scale — "Mixed-case text never goes below 11px on
   the tablet. Condensed uppercase labels with tracking may go to 9.5px", and
   "the kit's own 10px rail sublines are corrected with this entry" — and the
   drawing itself now writes 11 with the reason inline (rd-tablet-kit.jsx:184,
   "11, not 10 - the tablet mixed-case floor").

   The reading rule that comes with the scale: sub-11 values in PHONE-FRAME
   documents (312 wide, built at x1.25) are frame numbers rather than device
   numbers — but this file is tablet-canvas JSX at 1194 (rd-tablet-kit.jsx:19
   ("RAIL_W = 208") sits beside TAB_W = 1194), so its numbers are device
   numbers and the floor applies to them directly.

   This subtitle is mixed-case body copy — `tbody` sets no `text-transform` —
   so the 11px floor is the one that governs, not the 9.5px tracked-uppercase
   one.

   The row still fits: 9 + 9 padding, a ~15px label, a 3px gap and 11 x 1.3 of
   subtitle is about 50px inside a 56px minimum. */
.rd-trail__sub {
  font-size: 11px;
  line-height: 1.3;
  color: var(--rd-fg3);
}
/* ==== kit-tablet-panes · RdPanes, the tablet two-pane primitive · owner: spine ==== */

/* Drawn as `TabPanes` in rd-tablet-kit.jsx:356 ("function TabPanes"). Component:
   apps/web/src/components/kit/panes.rs, which carries the width scale, the
   fragment argument and the scroll deviation. Only the values live here.

   ANATOMY — two siblings, no wrapper:

     [ list, fixed width, 1px right rule, own scroll | detail, flex 1, own scroll ]

   The two are direct children of `.rd-tshell__content`, which is the flex ROW
   the shell already declares (kit-tablet-shell.css §7). There is no third
   element between them and no element around them; see the component's doc for
   why a wrapper is not free.

   ⚠️ THE LIST IS FIXED AND THE DETAIL TAKES THE REST, and that is an argued
   choice rather than a layout convenience. rd-tablet-kit.jsx:354 ("proportional
   split makes the list grow"): "The list keeps a fixed width and the detail
   takes the rest, because a proportional split makes the list grow on a laptop
   for no reason - a drill name needs 380px and never more."

   ⚠️ THIS PART DECLARES NO TYPE AND NO COLOUR BEYOND THE ONE RULE. A pane is a
   box with a scroll and an edge; its rows, its selection treatment and its empty
   state belong to the screen inside it. The drawn `TabPanes` sets no font and no
   foreground either — every drawn pane's contents style themselves. So this file
   has no size to floor against `01 Foundations` §05, because it writes no size:
   the type floor applies to the screens that fill these panes. */

/* ---------------------------------------------------------------------------
   1. The list pane — fixed width, the 1px rule, its own scroll.
   --------------------------------------------------------------------------- */
/* rd-tablet-kit.jsx:359 ("width: listW") — `width: listW, flex: 'none',
   borderRight: 1px solid t.line, display: flex, flexDirection: column`.

   `flex: none` is the whole of "fixed": without it the pane is a flex item with
   `flex-shrink: 1` and a 380px list crushes to 300 the moment a detail pane
   holds a wide table. The drawn value is `flex: 'none'` and it is not decoration.

   ⚠️ THE SCROLL IS A STATED DEVIATION FROM THE DRAWING, AND IT IS FORCED.
   `TabPanes` writes `overflow: 'hidden'` on both panes, because a drawing is a
   still frame on a 1194x834 canvas holding a nine-row fixture — clipping is
   correct there and only there. In the shipped app nothing above these two
   elements scrolls vertically: `.rd-tshell` is `overflow-y: hidden` and
   `.rd-tshell__content` is `overflow: hidden` (kit-tablet-shell.css §1 and §7),
   which is deliberate — "the REGION scrolls, not the document". Transcribing
   `overflow: hidden` here would therefore make a list of 200 drills unreachable
   below the fold, with no scrollbar anywhere on the screen to say so. So each
   pane owns its own vertical scroll, which is also what makes the two panes
   independent — the drawn pattern's entire point over a stacked phone screen.

   `overflow-x: hidden` and not `auto`: the horizontal axis is the shell's
   business (kit-tablet-shell.css §8 switches it at the breakpoint), and a second
   horizontal scroller nested inside the first is two scrollbars for one gesture.

   Pinned by `the_panes_own_the_scroll_because_nothing_above_them_does` in
   panes.rs. */
.rd-tpanes__list {
  width: 380px;
  flex: none;
  border-right: 1px solid var(--rd-line);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overflow-x: hidden;
  box-sizing: border-box;
}

/* ---------------------------------------------------------------------------
   2. The width scale — five drawn values, declared.
   --------------------------------------------------------------------------- */
/* ⚠️ A SCALE, NOT A NUMBER PER SCREEN. The corpus draws nine `TabPanes` call
   sites and they take exactly five widths between them — 320, 340, 360, 380,
   400 — so the width is modelled as an enum in panes.rs and rendered here as
   four modifiers over a base. 380 is the drawn signature default
   (rd-tablet-kit.jsx:356 ("function TabPanes"), `listW = 380`) and therefore
   carries NO modifier: the base rule above is the default, exactly as
   `.rd-ccard__card`'s is for `CardWidth::Standard`.

   The scale and its drawn consumers, one row per width — the table lives in
   panes.rs beside the enum, because the citations belong with the variants.

   ⚠️ 300 IS NOT ON THIS SCALE, AND IT WAS CHECKED RATHER THAN ASSUMED. There is
   no `listW={300}` anywhere in the corpus. The corpus does hold four structural
   300px columns and none of them is a list pane: two are right-hand
   side-columns, which the drawn `borderLeft` settles on its own
   (rd-tablet-livefire.jsx:220 ("width: 300, flex: 'none', borderLeft") and
   rd-tablet-relay.jsx:231 ("width: 300, flex: 'none', borderLeft")); one is the
   drill wizard's step column (rd-tablet-authoring.jsx:139 ("AU_STEPS.map"));
   and one is the leaderboard's standing-and-period
   column (rd-tablet-social.jsx:347 ("width: 300, flex: 'none', borderRight")). A side-column beside a
   single content pane
   is not a list feeding a detail, and admitting 300 here would put a sixth step
   on the scale on the strength of four elements that never call this component.
   Pinned by `the_width_scale_is_the_five_drawn_list_widths` in panes.rs. */
.rd-tpanes__list--320 {
  width: 320px;
}

.rd-tpanes__list--340 {
  width: 340px;
}

.rd-tpanes__list--360 {
  width: 360px;
}

.rd-tpanes__list--400 {
  width: 400px;
}

/* ---------------------------------------------------------------------------
   3. The detail pane — takes the rest, and may be narrower than its content.
   --------------------------------------------------------------------------- */
/* rd-tablet-kit.jsx:360 ("flex: 1, minWidth: 0").

   `min-width: 0` is the declaration this rule exists for. A flex item defaults
   to `min-width: auto` and refuses to shrink below its content's intrinsic
   width, so a detail pane holding a long unbroken drill name or a wide table
   pushes the LIST pane narrower than the width §1 and §2 just fixed — the fixed
   list stops being fixed, and the symptom shows up on the element that is
   correct. Same declaration and same trap as `.rd-tshell__col`'s.

   No border: the 1px rule between the panes is the LIST's `border-right`, drawn
   once. Declaring a matching `border-left` here would paint two hairlines at
   the seam on any display where they do not land on the same device pixel. */
.rd-tpanes__detail {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overflow-x: hidden;
  box-sizing: border-box;
}
/* ==== kit-tablet-section-tabs · RdSectionTabs, the section tab row · owner: spine ==== */

/* Drawn as `TabTabs` in rd-tablet-kit.jsx:278 ("function TabTabs") over the
   section table `TAB_SECT` at rd-tablet-kit.jsx:261 ("const TAB_SECT").
   Component: apps/web/src/components/kit/section_tabs.rs, which carries the nav
   table, the route derivation and the pinned labels. Only the values live here.

   ANATOMY:

     [ row: flex, 26px gaps, 28px gutter, 1px bottom rule ]
     [ tab: 44px touch target, 2px underline, pulled 1px down onto the rule ]

   It fills RdTabletShell's `tabs` slot, which is a reservation that paints
   nothing (kit-tablet-shell.css §6) — so the row's gaps, its gutter and its
   underline are all declared here.

   ⚠️ SELECTION IS AN UNDERLINE PLUS FULL-STRENGTH TEXT, AND NEITHER HALF IS
   OPTIONAL. rd-tablet-kit.jsx:275 ("Selection is the underline plus full-strength
   text"): "Selection is the underline plus full-strength text - never colour
   alone, and never a filled pill, because a pill row under a page header
   competes with the header for the top of the screen." `03 (Tablet) Training`,
   the row-06 notes, says it again: "Tab selection is an underline plus
   full-strength text. Never colour alone, and never a filled pill, which would
   compete with the page header for the top of the screen."

   So there is NO `background` anywhere in this file, and that absence is the
   rule rather than an omission. A pill is one `background` declaration away and
   it is the shape a later hand reaches for, because every other tab control they
   have seen has one. Pinned by
   `ruling_selection_is_an_underline_and_never_a_filled_pill` in section_tabs.rs.

   ⚠️ TYPE FLOOR — CHECKED, NOT ASSUMED. `01 Foundations` §05, "Two floors, one
   exemption": "Mixed-case text never goes below 11px on the tablet. Condensed
   uppercase labels with tracking may go to 9.5px". The one size in this file is
   the 11px label at §2, which is `tdisp` — condensed uppercase with .08em of
   tracking — so it sits above BOTH floors and nothing here is floored up. The
   drawn value is transcribed unchanged. */

/* ---------------------------------------------------------------------------
   1. The row — the gutter, the gaps and the rule it sits on.
   --------------------------------------------------------------------------- */
/* rd-tablet-kit.jsx:281 ("gap: 26") — `flex: 'none', display: 'flex', gap: 26,
   padding: '0 28px', borderBottom: 1px solid t.line`.

   The 28px gutter is the header's own (kit-tablet-shell.css §5 pads
   `10px 28px 16px`), so the first tab's word lines up under the page title
   rather than under nothing. The shell does NOT supply it — `.rd-tshell__tabs`
   is `flex: none` and nothing else — which is why it is declared here.

   `flex: none` for the same reason the header has it: this row is a fixed band
   in the shell's column and must not absorb the content region's growth. */
.rd-stabs {
  flex: none;
  display: flex;
  gap: 26px;
  padding: 0 28px;
  border-bottom: 1px solid var(--rd-line);
  box-sizing: border-box;
}

/* ---------------------------------------------------------------------------
   2. One tab — a 44px target, and an underline that lands ON the rule.
   --------------------------------------------------------------------------- */
/* rd-tablet-kit.jsx:285 ("minHeight: 44") — `minHeight: 44, display: 'flex',
   alignItems: 'center', textDecoration: 'none', borderBottom: 2px solid (acc when
   selected, transparent otherwise), marginBottom: -1`.

   ⚠️ `margin-bottom: -1px` IS LOAD-BEARING AND LOOKS LIKE A NUDGE. The row above
   carries a 1px bottom rule and the tab carries a 2px underline; without the
   -1px the underline stops one pixel short and the selected tab reads as
   floating above the line instead of breaking it. It is the drawn value, and it
   is the first declaration a tidy-up deletes.

   ⚠️ THE 2px BORDER IS DECLARED ON EVERY TAB AND COLOURED ON ONE. Declaring it
   only in the `--on` rule would move every tab 2px down the moment one lights
   up — the same trap `.rd-trail__row`'s 3px left rule records, one component
   over.

   44 IS THE TOUCH MINIMUM AND IT DOES NOT SHRINK BECAUSE A MOUSE MIGHT BE
   PRESENT — rd-tablet-kit.jsx:15 ("TOUCH SIZES DO NOT SHRINK"): "The same build
   serves an iPad and a laptop browser, and there is no way to know which is in
   front of you."

   ⚠️ --rd-acc, NEVER --rd-volt. This is a BORDER, and per substrate §1.1 a
   border takes acc, which goes deep warm brown in light theme; volt yields an
   unreadable tan rule on the light ground. Same call `.rd-trail__row--on`
   makes. */
.rd-stabs__tab {
  min-height: 44px;
  display: flex;
  align-items: center;
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  box-sizing: border-box;
}

.rd-stabs__tab--on {
  border-bottom-color: var(--rd-acc);
}

/* rd-tablet-kit.jsx:286 ("fontSize: 11") — tdisp(800, 78, '.08em') at 11px,
   `color: on ? t.fg : t.fg3`. tdisp is always uppercase; the transform lives in
   the helper (substrate §2.1), so it is written out here rather than assumed.

   fg3 unlit, fg lit — the FULL-STRENGTH half of the selection rule. An
   underline over an unchanged colour is "colour alone" inverted and the drawing
   refuses both halves separately. */
.rd-stabs__label {
  font-weight: 800;
  font-stretch: 78%;
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--rd-fg3);
  white-space: nowrap;
}

.rd-stabs__tab--on .rd-stabs__label {
  color: var(--rd-fg);
}

/* ---------------------------------------------------------------------------
   3. Focus — the kit's house ring, pulled inward.
   --------------------------------------------------------------------------- */
/* Same construction as `.rd-trail__row:focus-visible` and
   `.rd-repstep__btn:focus-visible`. The offset is NEGATIVE because the tab's own
   bottom edge IS the underline and the row's 1px rule sits directly under it: a
   +2px ring paints outside both, crossing the rule and the neighbouring tab's
   underline.

   `:focus-visible` only, never `:focus` — a pointer press on a tab must not
   leave a ring behind on the destination it just navigated to. */
.rd-stabs__tab:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* ⚠️ NO HOVER TREATMENT, AND THE ABSENCE IS DRAWN. rd-tablet-kit.jsx:145
   ("anchor inherits every style the div had"): "there is deliberately NO hover
   treatment - a hover state invented for the convenience of reading the corpus
   would end up quoted back as a design decision." If one is ever ruled in it
   goes under `@media (hover: hover)` and never under a width query — see the
   same note on the rail's part for the ruling that says why. */
/* ==== kit-tablet-activity-band · RdActivityBand, the activity chrome band · owner: spine ==== */

/* THE RULING, 19 Aug 2026 — `uploads/loading-and-rulings-reply.md` §2
   ("Activity-screen chrome band — reserve it, but never empty"):

     "Ruled: activity screens KEEP the 45px band, and the band is never blank —
      it carries the activity's own header: the back/exit control and the run's
      identity (course name, stage counter), in the same band geometry the
      section tabs occupy on destination screens. This satisfies all three prior
      rulings at once: tab rows still belong to destinations (these are not
      tabs), the column origin never moves (James's requirement), and no empty
      row ships (17 Aug). Blessing the shift and reserving a blank band are both
      rejected."

   Component: apps/web/src/components/kit/activity_band.rs.

   ⚠️ THERE IS NO STILL FRAME FOR THIS. The reply rules the band into existence
   and says nothing beyond "the same band geometry the section tabs occupy", so
   every value here is either LIFTED FROM `.rd-stabs` (the geometry, which the
   ruling names) or from the section-tab row's own type roles (the ink, which it
   does not). Nothing is invented: this part declares no size, no colour and no
   spacing that kit-tablet-section-tabs.css does not already declare, with the
   two exceptions §3 states and defends (the chevron's 9x15 box, lifted from the
   phone shell's back control, and the `min-width: 0` chain that lets a long
   course name ellipsis).

   ANATOMY — the same two-line shape kit-tablet-section-tabs.css draws:

     [ row: flex, 26px gaps, 28px gutter, 1px bottom rule ]
     [ back control | activity identity — each a 44px target pulled 1px down ]

   ⚠️ THE GEOMETRY IS A CONTRACT WITH THE OTHER PART, NOT A COINCIDENCE. The
   whole ruling is that the content region's origin does not move when a shooter
   goes from `/training/courses` to a course underway, and that origin is
   `head (76) + THIS BAND`. So the four row declarations in §1 and the two
   height-setting declarations in §2 are transcribed from `.rd-stabs` and
   `.rd-stabs__tab` and pinned AGAINST THAT FILE by
   `ruling_the_band_is_the_same_height_as_the_section_tab_row` in
   activity_band.rs, which reads both parts and compares them. Retune either
   side alone and that test goes red — which is the point, because the failure
   it guards is a 1px content jump nobody sees in review. */

/* ---------------------------------------------------------------------------
   1. The row — transcribed from `.rd-stabs`, declaration for declaration.
   --------------------------------------------------------------------------- */
/* kit-tablet-section-tabs.css §1, which cites `rd-tablet-kit.jsx:281`
   ("gap: 26"): `flex: 'none', display: 'flex', gap: 26, padding: '0 28px',
   borderBottom: 1px solid t.line`.

   The 28px gutter is the header's own (kit-tablet-shell.css §5 pads
   `10px 28px 16px`), so the back control's chevron lines up under the page
   title rather than under nothing — the same argument the tab row makes for the
   same number.

   `flex: none` for the same reason the header and the tab row both have it:
   this is a fixed band in the shell's column and must not absorb the content
   region's growth.

   ⚠️ NO `background`. The band sits in `.rd-tshell__tabs`, which is a
   reservation that paints nothing (kit-tablet-shell.css §6), and the section
   tab row it stands in for paints no ground either. A fill here would make the
   activity band the one banded strip in the app and would land exactly on the
   "reads as a fifth section" failure the 17 Aug ruling names. */
.rd-aband {
  flex: none;
  display: flex;
  gap: 26px;
  padding: 0 28px;
  border-bottom: 1px solid var(--rd-line);
  box-sizing: border-box;
}

/* ---------------------------------------------------------------------------
   2. The two slots — a 44px target each, pulled 1px down onto the rule.
   --------------------------------------------------------------------------- */
/* `.rd-stabs__tab` (kit-tablet-section-tabs.css §2) is `minHeight: 44, display:
   flex, alignItems: center, borderBottom: 2px solid …, marginBottom: -1`. Two
   of those four carry the band's height and both are here:

   ⚠️ `margin-bottom: -1px` IS LOAD-BEARING AND LOOKS LIKE A NUDGE, exactly as
   it does one part over. It is what makes a 44px target sit in a 44px band over
   a 1px rule instead of a 45px one. It is also the first declaration a tidy-up
   deletes, and deleting it here moves the content origin on every activity
   screen by one pixel while every destination screen stays put — the precise
   bounce this band exists to remove.

   ⚠️ 44 IS THE TOUCH MINIMUM AND IT DOES NOT SHRINK BECAUSE A MOUSE MIGHT BE
   PRESENT — rd-tablet-kit.jsx:15 ("TOUCH SIZES DO NOT SHRINK"): "The same build
   serves an iPad and a laptop browser, and there is no way to know which is in
   front of you." It is on the IDENTITY too, not only on the back control, so
   the band keeps its height on a screen whose identity has not loaded yet.

   ⚠️ AND NO `border-bottom: 2px solid transparent`, WHICH IS THE ONE DECLARATION
   OF THE FOUR THAT IS DELIBERATELY ABSENT. On the tab row that border is the
   selected underline, declared on every tab and coloured on one; NOTHING IN
   THIS BAND IS SELECTED — there is no set of peers to be selected among — so
   there is no underline to reserve room for. It costs zero height either way
   (`box-sizing: border-box` puts it inside the 44), so its absence changes no
   geometry and its presence would be a lit state this band cannot enter. */
.rd-aband__back {
  min-height: 44px;
  display: flex;
  align-items: center;
  gap: 9px;
  margin-bottom: -1px;
  box-sizing: border-box;

  /* It is a real <button>; strip the UA chrome without stripping the
     semantics. The same reset `.rd-shell__back` writes, minus the phone
     shell's leading bleed — this band has no screen edge to reach, it sits
     inside the shell's own 28px gutter. */
  flex: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font: inherit;
  color: inherit;
  text-align: start;
  -webkit-appearance: none;
  appearance: none;
}

/* The identity. `min-width: 0` is what lets the shrink reach the name so the
   ellipsis in §3 can fire — a flex item defaults to `min-width: auto` and
   refuses to shrink below its content, which would push the counter off the
   band instead of truncating the course name. Same declaration, same reason,
   as `.rd-tshell__titles`. */
.rd-aband__id {
  min-height: 44px;
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-bottom: -1px;
  box-sizing: border-box;
  flex: 1 1 auto;
  min-width: 0;
}

/* ---------------------------------------------------------------------------
   3. The ink — the section-tab row's own type roles, unchanged.
   --------------------------------------------------------------------------- */
/* ⚠️ THE RULING NAMES NO TYPE, so none is invented. Every rule below is
   `.rd-stabs__label`'s declaration block — tdisp(800, 78, '.08em') at 11px,
   `rd-tablet-kit.jsx:286` ("fontSize: 11") — with only the colour differing,
   and the two colours it differs by are that row's own lit/unlit pair:

     name    -> --rd-fg   (the lit tab's colour: this IS what the screen is)
     counter -> --rd-fg3  (the unlit tab's colour: supporting, not the subject)

   That pairing is the section-tab row's "full-strength text" rule applied to a
   band with one subject in it rather than four candidates, and it is why the
   band reads as the same object as the row it replaces rather than as a new
   one. If Design ever draws this band, THIS is the block to mark up.

   ⚠️ TYPE FLOOR — CHECKED, NOT ASSUMED. `01 Foundations` §05, "Two floors, one
   exemption": mixed-case text never goes below 11px on the tablet; condensed
   uppercase labels with tracking may go to 9.5px. Every size here is the 11px
   tdisp label, above both floors. Nothing is floored up. */
.rd-aband__back-label {
  font-weight: 800;
  font-stretch: 78%;
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--rd-fg3);
  white-space: nowrap;
}

.rd-aband__name {
  font-weight: 800;
  font-stretch: 78%;
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.rd-aband__counter {
  font-weight: 800;
  font-stretch: 78%;
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--rd-fg3);
  white-space: nowrap;
  flex: none;
}

/* The chevron, lifted whole from `.rd-shell__chevron` (kit-shell.css) — the
   phone shell's back control, which is the app's only other back affordance and
   the one a shooter already knows. 9x15, `flex: none` so it never shrinks, and
   the stroke colour lives here rather than in the markup because token roles
   belong in the stylesheet.

   ⚠️ --rd-acc, NEVER --rd-volt. This is a STROKE, and per substrate §1.1 a
   stroke takes acc, which goes deep warm brown in light theme; volt yields an
   unreadable tan glyph on the light ground. Same call the phone chevron and
   the tab row's underline both make. */
.rd-aband__chevron {
  flex: none;
  display: block;
  width: 9px;
  height: 15px;
  stroke: var(--rd-acc);
}

/* ---------------------------------------------------------------------------
   4. Focus — the kit's house ring, pulled inward.
   --------------------------------------------------------------------------- */
/* Same construction as `.rd-stabs__tab:focus-visible` one part over, and for
   the same reason: the row's 1px rule sits directly under the control's bottom
   edge, so a +2px ring paints across it. Negative offset keeps the ring inside
   the band.

   `:focus-visible` only, never `:focus` — a pointer press on the back control
   must not leave a ring behind on the destination it just navigated to. */
.rd-aband__back:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* ⚠️ NO HOVER TREATMENT, AND THE ABSENCE IS THE SECTION-TAB ROW'S OWN CALL,
   carried rather than re-decided. rd-tablet-kit.jsx:145: "there is deliberately
   NO hover treatment - a hover state invented for the convenience of reading
   the corpus would end up quoted back as a design decision." The phone shell's
   back control does have one, and it is marked PROVISIONAL in its own part for
   exactly that reason. If one is ever ruled in it goes under
   `@media (hover: hover)` and never under a width query. */
/* ==== kit-tablet-landing-cards · RdLandingCards, the section landing · owner: spine ==== */

/* Drawn as `TabLanding` in rd-tablet-kit.jsx:395 ("function TabLanding") over the
   section table `TAB_SECT` at rd-tablet-kit.jsx:261 ("const TAB_SECT").
   Component: apps/web/src/components/kit/landing_cards.rs, which carries the
   no-tab-row ruling, the grid threshold and the derived-state contract. Only the
   values live here.

   ANATOMY:

     [ region: flex 1, 22px/28px, its own scroll                         ]
     [   grid: 16px gaps, two-up above three cards and three-up at three ]
     [     card: 1px edge, 16px/18px, 9px stack, 118px floor             ]
     [       label · what it is · optional derived state line            ]
     [   note: the one sentence a card grid cannot say                   ]

   ⚠️ THERE IS NO TAB ROW ON THIS PAGE, AND THE ABSENCE IS THE COMPONENT'S WHOLE
   REASON FOR EXISTING. rd-tablet-kit.jsx:389 ("NO TAB ROW HERE"): "NO TAB ROW
   HERE, and that is why this is its own component rather than a flag on
   TabShell. The first version drew both: a tab row listing four labels and cards
   listing the same four, 63px apart, with no tab selected because you are above
   all of them. That is a menu stacked on an annotated menu, and the unselected
   tab row read as broken rather than as `you are above these`. On a landing page
   THE CARDS ARE THE NAVIGATION. The tab row appears the moment you pick one."

   So nothing in this file styles a tab, and the SCREEN that mounts this must
   pass `tabs: None` to RdTabletShell. Pinned by
   `ruling_a_landing_page_renders_no_tab_row` in landing_cards.rs, which also
   carries the corpus's two-answer disagreement and how it was ruled.

   ⚠️ THIS IS NOT A MENU SCREEN.
   rd-tablet-kit.jsx:384 ("THE LANDING PAGE for a section"):
   "the section's destinations as cards, each with what it is and
   where it stands. This is what makes every rail row clickable, and it is not a
   menu screen recreated - a menu screen lists names, this states what each thing
   is for and what is currently true of it." The description at §5 is therefore
   not decoration: a card with a heading and no body IS the menu item this
   component refuses to be.

   ⚠️ TYPE FLOOR — CHECKED, NOT ASSUMED, AND TWO SIZES SIT EXACTLY ON A FLOOR.
   `01 Foundations` §05, "Two floors, one exemption": "Mixed-case text never goes
   below 11px on the tablet. Condensed uppercase labels with tracking may go to
   9.5px". Four sizes here:

     16px  label (§4)        tracked uppercase — above both floors
     12px  description (§5)  mixed case        — above the 11px floor
     9.5px state line (§6)   tracked uppercase — EXACTLY ON the 9.5px floor
     11px  note (§7)         mixed case        — EXACTLY ON the 11px floor

   Nothing is floored UP: every drawn size is at or above its own floor and is
   transcribed unchanged. The two that sit ON a floor are marked at their rules,
   because the floor is what stops the next reader shaving a half-pixel off
   either one to buy a line of room.

   ⚠️ ONE DRAWN VALUE IS DEVIATED FROM, AND IT IS A WIDTH RATHER THAN A SIZE.
   The drawn label is `tdisp(800, 74, '.02em')` (rd-tablet-kit.jsx:405
   ("fontSize: 16, color: t.fg")) and 74 is not in the design system's allowed
   width set — `62, 72, 75, 76, 77, 78, 100` (ALLOWED_STRETCH, cli/src/css.rs).
   Checked rather than assumed: `74` appears EXACTLY ONCE in the whole corpus,
   on that one line, which is drift by the designer's own test — "five sites are
   drift, not a rule" (the 66 collapse, quoted in cli/src/css.rs). His band
   statement is "62 display, 75-78 labels and controls, 100 body", so 74 is
   snapped UP to 75, the nearest allowed value and the floor of the band a 16px
   card title belongs to. Recorded at §4 and pinned by
   `the_card_label_deviates_from_the_drawn_width_because_seventy_four_is_drift`. */

/* ---------------------------------------------------------------------------
   1. The region — the content pane the cards sit in.
   --------------------------------------------------------------------------- */
/* rd-tablet-kit.jsx:398 ("padding: '22px 28px'") — `flex: 1, minWidth: 0,
   padding: '22px 28px', overflow: 'hidden'`.

   The 28px gutter is the header's own (kit-tablet-shell.css §5 pads
   `10px 28px 16px`), so the first card's left edge lines up under the page
   title. `min-width: 0` is the declaration this rule exists for: a flex item
   defaults to `min-width: auto` and refuses to shrink below its content, and a
   grid of four cards has plenty of intrinsic width to push with.

   ⚠️ THE SCROLL IS A STATED DEVIATION FROM THE DRAWING, AND IT IS THE SAME ONE
   `.rd-tpanes__list` RECORDS. The drawing writes `overflow: 'hidden'`, which is
   correct for a still frame on a 1194x834 canvas holding at most four cards. In
   the shipped app nothing above this element scrolls vertically — `.rd-tshell`
   is `overflow-y: hidden` and `.rd-tshell__content` is `overflow: hidden`
   (kit-tablet-shell.css §1 and §7), on purpose, because "the REGION scrolls, not
   the document". Four cards do not overflow 834px today; a fifth destination, a
   longer description or a 320px-tall browser window all do, and the failure mode
   of `hidden` is content that is unreachable with no scrollbar anywhere on
   screen to say so.

   `overflow-x: hidden` and not `auto`: the horizontal axis is the shell's
   business (kit-tablet-shell.css §8 switches it at the breakpoint), and a second
   horizontal scroller nested in the first is two scrollbars for one gesture.

   The body family is set here rather than inherited: inside the shell it would
   arrive from `.rd-tshell`, and mounted bare on `/kit` it would not. */
.rd-tland {
  flex: 1;
  min-width: 0;
  padding: 22px 28px;
  overflow-y: auto;
  overflow-x: hidden;
  box-sizing: border-box;
  font-family: 'Archivo', sans-serif;
}

/* ---------------------------------------------------------------------------
   2. The grid — 16px gaps, and a column count DERIVED from the list.
   --------------------------------------------------------------------------- */
/* rd-tablet-kit.jsx:400 ("gridTemplateColumns: rows.length") — `display: 'grid',
   gridTemplateColumns: rows.length > 3 ? '1fr 1fr' : '1fr 1fr 1fr', gap: 16`.

   ⚠️ MORE CARDS MEANS FEWER COLUMNS, AND THAT IS NOT A TYPO. `03 (Tablet)
   Training`, the row-06 notes: "Live Fire lays out two-up because it has four
   destinations; Community three-up because it has three. Derived from the list
   rather than chosen". Four cards two-up is a 2x2 block; four cards three-up is
   a row of three with an orphan under it. The threshold is therefore a rule
   about the SHAPE the grid makes, not a width budget, which is why it reads
   backwards from the obvious direction.

   Both column counts are declared as modifiers rather than one being the base:
   neither is a default in the drawing, the ternary picks between two equals, and
   a base holding one of them would make the other look like the exception.
   Pinned by `ruling_the_grid_is_two_up_above_three_cards_and_three_up_at_three`
   in landing_cards.rs. */
.rd-tland__grid {
  display: grid;
  gap: 16px;
}

.rd-tland__grid--2up {
  grid-template-columns: 1fr 1fr;
}

.rd-tland__grid--3up {
  grid-template-columns: 1fr 1fr 1fr;
}

/* ---------------------------------------------------------------------------
   3. The card — the whole destination, not a word inside it.
   --------------------------------------------------------------------------- */
/* rd-tablet-kit.jsx:404 ("minHeight: 118") — `border: 1px solid t.line, padding:
   '16px 18px', display: 'flex', flexDirection: 'column', gap: 9, minHeight: 118,
   textDecoration: 'none'`.

   The `Link` IS the card and not a wrapper around the label, which is what makes
   the touch target the whole 118px box rather than the height of a line of type.
   That clears the kit's 44px minimum four times over — "TOUCH SIZES DO NOT
   SHRINK BECAUSE A MOUSE MIGHT BE PRESENT ... 44px minimum stands everywhere in
   this kit" (rd-tablet-kit.jsx:15 ("TOUCH SIZES DO NOT SHRINK")).

   `min-height`, never `height`: the description is the card's flexible child
   (§5) and a taller neighbour in the same grid row stretches this one anyway.
   118 is the floor a one-line description falls to.

   NO BACKGROUND. The drawn card paints none — it is an outlined box on the
   shell's ground — and the selection paint that would tempt one belongs to the
   rail and the hub, where a row is `you are here`. Nothing on a landing page is
   selected: you are above all of them. */
.rd-tland__card {
  display: flex;
  flex-direction: column;
  gap: 9px;
  min-height: 118px;
  padding: 16px 18px;
  border: 1px solid var(--rd-line);
  text-decoration: none;
  box-sizing: border-box;
}

/* ---------------------------------------------------------------------------
   4. The label — the destination's name, in the display voice.
   --------------------------------------------------------------------------- */
/* rd-tablet-kit.jsx:405 ("fontSize: 16, color: t.fg") — `tdisp(800, 74, '.02em'),
   fontSize: 16, color: t.fg`, where `tdisp` is Archivo / uppercase / the given
   weight, width and tracking (rd-train-kit.jsx:29 ("const tdisp")).

   ⚠️ `font-stretch: 75%` IS A STATED DEVIATION FROM THE DRAWN 74. See the
   banner: 74 occurs exactly once in the entire corpus and is outside the allowed
   width set, so it is drift by the designer's own five-sites test and is snapped
   up to the nearest allowed value in the band he named for labels and controls.
   Everything else on this line — weight 800, tracking .02em, 16px, `--rd-fg` —
   is the drawing's, unchanged.

   16px is above both type floors and is the largest size in this file, which is
   the point: on a page whose cards ARE the navigation, the destination's name
   has to out-rank its own description. */
.rd-tland__label {
  font-weight: 800;
  font-stretch: 75%;
  text-transform: uppercase;
  letter-spacing: .02em;
  font-size: 16px;
  color: var(--rd-fg);
}

/* ---------------------------------------------------------------------------
   5. The description — what the thing is for. Not decoration.
   --------------------------------------------------------------------------- */
/* rd-tablet-kit.jsx:406 ("lineHeight: 1.5, flex: 1") — `tbody, fontSize: 12,
   color: t.fg3, lineHeight: 1.5, flex: 1`.

   `flex: 1` is what pins the state line to the bottom of the card: the
   description absorbs the slack, so a two-line description and a one-line
   description put their state lines on the same baseline across a grid row.
   Deleting it collapses the card to its content and leaves the state line
   floating under the description.

   12px mixed case, one above the 11px floor. The text this renders is the nav
   table's `what` column (section_tabs.rs), which is the drawing's own third
   element — it is a KIT string, not the caller's copy. */
.rd-tland__what {
  flex: 1;
  font-size: 12px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   6. The state line — DERIVED BY THE CALLER, or absent.
   --------------------------------------------------------------------------- */
/* rd-tablet-kit.jsx:407 ("fontSize: 9.5, color: t.acc") — `tdisp(700, 78, '.1em'),
   fontSize: 9.5, color: t.acc`, rendered only when the state resolves
   (`{state && …}`).

   ⚠️ THE STRING IS NEVER TYPED, HERE OR ANYWHERE.
   rd-tablet-kit.jsx:209 ("EVERY COUNT THE CHROME QUOTES LIVES HERE, DERIVED"):
   "EVERY COUNT THE CHROME QUOTES
   LIVES HERE, DERIVED, and nothing outside this table is allowed to type one.
   Three rounds of review found the same defect three times - `38 in library`
   beside nine drills, `6 assessments` beside a page asserting four, `12
   favourites` inside a nine-drill library". The corpus's own answer is a table
   of guarded FUNCTIONS over fixtures; ours is an `Option<String>` per card,
   supplied by the screen that has the data. The contract, including the
   words-or-nothing rule for half-resolved data, is on `LandingStates` in
   landing_cards.rs.

   ⚠️ 9.5px SITS EXACTLY ON THE TRACKED-UPPERCASE FLOOR. It is legal only
   because this rule is condensed uppercase with .1em of tracking; drop
   `text-transform` and the same number is 1.5px under the mixed-case floor with
   nothing else in the diff to say so.

   `--rd-acc` is the accent as TEXT, which is the token substrate §1.1 splits
   from `--rd-volt` (the accent as a fill). A state line is text. */
.rd-tland__state {
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-acc);
}

/* ---------------------------------------------------------------------------
   7. The note — the one sentence a card grid cannot say.
   --------------------------------------------------------------------------- */
/* rd-tablet-kit.jsx:414 ("Pick one and these become tabs") — `tbody, fontSize:
   11, color: t.fg3, marginTop: 18, lineHeight: 1.5, maxWidth: '64ch'`, under the
   drawing's own comment: "The one sentence a card grid cannot say: that these
   four are also the tab row you get once you are inside one of them."

   It explains the navigation rather than the section, which is why the sentence
   is kit-owned and identical on both landings — see landing_cards.rs.

   ⚠️ 11px SITS EXACTLY ON THE MIXED-CASE FLOOR. `01 Foundations` §05: "Mixed-case
   text never goes below 11px on the tablet." A sentence is mixed case, so the
   9.5px exemption at §6 does not reach it and 11 is as small as this line goes.
   `64ch` is a measure, not a width — a 1194px pane would otherwise run this
   sentence to about 120 characters. */
.rd-tland__note {
  margin-top: 18px;
  max-width: 64ch;
  font-size: 11px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   8. Focus — the house ring, and the offset is POSITIVE here.
   --------------------------------------------------------------------------- */
/* `--rd-acc` ring, `:focus-visible` so a pointer press does not leave one behind
   on the destination it just navigated to.

   ⚠️ THE OFFSET IS POSITIVE, WHICH IS THE OPPOSITE OF EVERY OTHER TABLET PART,
   AND THE REASON IS GEOMETRIC RATHER THAN STYLISTIC. `.rd-trail__row` and
   `.rd-stabs__tab` both take `-2px` because they are FLUSH — rail rows abut each
   other and the tab's own bottom edge is its underline, so a ring outside them
   paints over a neighbour or over the selection rule. These cards are separated
   by the grid's 16px gap and stand 22px/28px inside the region's padding, so
   there is room outside the card's 1px edge and a ring drawn there reads as a
   ring rather than as a second border. Copying the negative offset would double
   the card's own outline instead. */
.rd-tland__card:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}
/* ==== kit-tablet-hub-rail · RdHubRail, the in-pane hub column · owner: spine ==== */

/* Drawn once, now:

     Profile, 340px, six rows under a PINNED identity block —
       rd-tablet-identity.jsx:29 ("width: 340, flex: 'none'"), inside
       rd-tablet-identity.jsx:25 ("function ScreenTabProfile")

   ⚠️ DRAWN TWICE IS NOW DRAWN ONCE — corpus, 2026-08-18. `13 (Tablet) Community`
   v0.2 redraws Community as a TabLanding and retires the hub width by name: "The
   rail hub's 320px width retires with it — the only other hub, Profile, is 340
   and stays 340." The drawing agrees; the export added a header above
   ScreenTabCommunity (rd-tablet-social.jsx:18 ("SUPERSEDED 15 Aug")) reading
   "nothing routes here, and the 320 hub rail width retires with it".

   ⚠️ THE DELETION LANDED — 2026-08-18, relay-4 §3. Community's row used to read

     Community, 320px, six rows and a count each — rd-tablet-social.jsx:34
       ("width: 320, flex: 'none'"), inside rd-tablet-social.jsx:23
       ("function ScreenTabCommunity")

   here; it is gone along with the base rule that held 320 and the
   `HubWidth::W320` variant that named it (hub_rail.rs carries the retirement
   record). The citation is NOT [gone]: the designer kept the component in
   source as the record, so rd-tablet-social.jsx:34 still holds `width: 320`.
   The LINE survived and the CLAIM did not — which is the drift a resolving
   citation cannot show; this note, not a live rule below, is now its only home
   in this part.

   Component: apps/web/src/components/kit/hub_rail.rs, which carries the width
   variants, the caller-owned rows, the pinned-header slot and the argument for
   scoping the drill wizard's step column OUT. Only the values live here.

   ANATOMY:

     [ column: fixed width, 1px right rule, its own ground is the shell's ]
     [   head (optional): flex none, pinned, NEVER selectable            ]
     [   rows: flex 1, its own scroll                                    ]
     [     row: 3px left rule + surf2 ground when selected               ]

   ⚠️ THE HUB PROBLEM, AND WHY A HUB IS A COLUMN RATHER THAN A SCREEN.
   rd-tablet-social.jsx:3 ("THE HUB PROBLEM"): "Community and Profile are both
   hubs - a short column of rows that lead somewhere. A hub is the thing a tablet
   is hardest on: five rows in 1194px is a column of buttons in a field of
   nothing, and the honest fix is not to make the rows bigger. It is to stop
   being a menu and show the destinations. So Community keeps its rows in a 320px
   rail and puts what they lead to beside them. The rail is still the hub; you
   just do not have to leave to see what is in it."

   ⚠️ THIS IS NOT `.rd-tpanes__list`, AND THE DIFFERENCE IS WHAT THE SELECTION
   MEANS. A pane's list selects a RECORD — this drill, this person, this event —
   and kit-tablet-panes.css says in as many words that its rows, their selection
   treatment and their empty state belong to the screen. A hub's rows select a
   KIND OF CONTENT, and the set of kinds is chrome the screen does not invent per
   row. The drawings separate the two mechanically as well: every hub row pads
   `0 18px` (social:31, identity:44) and every record row in a pane pads 16px
   horizontally (social:90, social:170, social:248, training:105, training:241,
   solo:217). Both draw the same 3px rule; only one of them is navigation.

   ⚠️ THE SELECTION IDIOM IS THE RAIL'S, DELIBERATELY. 3px of `--rd-acc` on the
   left edge over a `--rd-surf2` ground, exactly as `.rd-trail__row--on` paints it
   (rd-tablet-kit.jsx:182 ("padding: '9px 18px'")). "It is not the Pro rail.
   Same shape, different app: ... the shooter kit's volt selection rather than the
   console's grey. A rail is a shape, not a brand." (`03 (Tablet) Training`). So
   `--rd-acc` and `--rd-surf2` here, never the console's greys.

   ⚠️ NO HOVER TREATMENT, AND THE ABSENCE IS DRAWN.
   rd-tablet-kit.jsx:145 ("anchor inherits every style the div had"):
   "there is deliberately NO hover treatment -
   a hover state invented for the convenience of reading the corpus would end up
   quoted back as a design decision." If one is ever ruled in it goes under
   `@media (hover: hover)` and NEVER under a width query — hover keys on the
   input, not the viewport.

   ⚠️ TYPE FLOOR — CHECKED, NOT ASSUMED, AND NOTHING IS FLOORED UP. `01
   Foundations` §05, "Two floors, one exemption": "Mixed-case text never goes
   below 11px on the tablet. Condensed uppercase labels with tracking may go to
   9.5px". Five sizes here, all mixed case or numeric, all at or above 11:

     13.5px label, single-line density (§5)   social:32
     13px   label, sub-line density (§5)      identity:47
     13px   count, single-line density (§7)   social:33
     12px   count, sub-line density (§7)      identity:50
     10.5px sub-line (§6)                     identity:48   ⚠️ FLOORED UP TO 11

   The sub-line is the one deviation in this file and it is upward: the drawn
   10.5 is half a pixel under the mixed-case floor, so it ships at 11. Recorded
   at §6 and pinned by `the_sub_line_is_floored_up_to_the_mixed_case_floor`. The
   counts are numerals in the numeric role rather than sentences, and 12 and 13
   clear the floor anyway, so neither needed the argument. */

/* ---------------------------------------------------------------------------
   1. The column — a fixed width and the 1px rule beside the pane.
   --------------------------------------------------------------------------- */
/* rd-tablet-identity.jsx:29 ("width: 340, flex: 'none'") — `width: 340, flex:
   'none', borderRight: 1px solid t.line, display: 'flex', flexDirection:
   'column'` because it carries a head above its rows.

   ⚠️ NO WIDTH HERE, DELIBERATELY, SINCE 2026-08-18. This rule used to hold
   `width: 320px` as Community's base case, with §2's modifier overriding it to
   340 for Profile. Community's 320 is retired (see the banner above), and with
   one drawn value left, a base width would be a fallback nothing falls back to
   — the exact shape the `Default` note below already refuses. §2's modifier is
   now the only source of a width.

   The column is `display: flex` / `column` here — the head is optional and the
   rows must take the remaining height with or without one, and a column that
   changes its own layout mode when a slot is filled is two components sharing a
   class name.

   `flex: none` is the whole of "fixed": without it this is a flex item with
   `flex-shrink: 1` and the column crushes the moment the pane beside it holds a
   wide table. The 1px rule is a `border-right` drawn once, on this side of the
   seam; a matching `border-left` on the pane would paint two hairlines wherever
   they do not land on the same device pixel.

   NO BACKGROUND. The drawn hub column paints none — unlike `.rd-trail`, which
   sets `--rd-surf`. A hub sits INSIDE the content region on the same ground as
   the pane beside it, and the only thing that separates them is the rule.

   The body family is set here rather than inherited: inside the shell it would
   arrive from `.rd-tshell`, and mounted bare on `/kit` it would not. */
.rd-thub {
  flex: none;
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--rd-line);
  overflow: hidden;
  box-sizing: border-box;
  font-family: 'Archivo', sans-serif;
}

/* ---------------------------------------------------------------------------
   2. The width variant — one drawn value, and 300 is NOT it.
   --------------------------------------------------------------------------- */
/* 340 is the modifier (rd-tablet-identity.jsx:29) and it is the only width in
   this part — see §1's note on why the base rule above carries none. It is not
   a default in the drawing either — there is no signature default to inherit
   the way `TabPanes` has `listW = 380` — so `HubWidth` in hub_rail.rs has NO
   `Default` impl and the prop is required.

   ⚠️ 300 IS DELIBERATELY ABSENT, AND IT WAS ARGUED RATHER THAN OVERLOOKED. The
   corpus's one 300px column of this shape is the drill wizard's step rail
   (rd-tablet-authoring.jsx:138 ("width: 300, flex: 'none'")), which hub_rail.rs
   scopes OUT as a different component wearing a similar shape — ordered steps
   with a number-or-tick column, three progress states and rows that are not
   destinations. kit-tablet-panes.css reached the same verdict from the other
   side about the same element: "a stepper, not a list of records". A width
   variant whose only consumer is a component we decided not to build is a hole
   nobody decided to leave. Pinned by
   `ruling_the_wizard_step_column_is_not_this_component` in hub_rail.rs. */
.rd-thub--340 {
  width: 340px;
}

/* ---------------------------------------------------------------------------
   3. The pinned head — drawn once, by Profile, and it never scrolls away.
   --------------------------------------------------------------------------- */
/* rd-tablet-identity.jsx:34 ("padding: '18px 18px 16px'") — `flex: 'none',
   display: 'flex', alignItems: 'center', gap: 13, padding: '18px 18px 16px',
   borderBottom: 1px solid t.line`, under the drawing's own comment at
   rd-tablet-identity.jsx:30 ("The person, not a row"): "The person, not a row. It
   never drives the pane and never scrolls away - on the phone this block is the
   top of a scrolling column and here it is the one thing that stays."

   Both halves of that sentence are declarations here. `flex: none` is "never
   scrolls away" — the scroll belongs to §4, which sits below this. And there is
   NO selection idiom on this rule: no `border-left`, no ground, no `--on`
   modifier anywhere in the file that could reach it, because "it never drives
   the pane". A head that could light up would be a seventh row claiming to be a
   destination.

   Asymmetric padding is the drawing's: 18 above and 16 below, so the block's
   optical centre sits where a symmetric 18/18 would put it low. The bottom rule
   is the head's own and is drawn in addition to the first row's top edge, which
   is that row's neighbour's `border-bottom` — the rows do not draw a top border,
   so the two never double. */
.rd-thub__head {
  flex: none;
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 18px 18px 16px;
  border-bottom: 1px solid var(--rd-line);
  box-sizing: border-box;
}

/* ---------------------------------------------------------------------------
   4. The rows region — takes the rest, and owns the scroll.
   --------------------------------------------------------------------------- */
/* rd-tablet-identity.jsx:42 ("flex: 1, minHeight: 0") — `flex: 1, minHeight: 0,
   overflow: 'hidden'`.

   `min-height: 0` is the declaration this rule exists for. A flex item defaults
   to `min-height: auto` and refuses to shrink below its content, so a column of
   nine rows would push the head off the top of a short viewport instead of
   scrolling — which is precisely the thing §3 promises cannot happen.

   ⚠️ THE SCROLL IS A STATED DEVIATION FROM THE DRAWING, AND IT IS THE SAME ONE
   `.rd-tpanes__list` RECORDS. The drawing writes `overflow: 'hidden'`, correct
   for a still frame on a 1194x834 canvas holding six rows. In the shipped app
   nothing above this element scrolls vertically — `.rd-tshell` is `overflow-y:
   hidden` and `.rd-tshell__content` is `overflow: hidden` (kit-tablet-shell.css
   §1 and §7), because "the REGION scrolls, not the document" — so transcribing
   `hidden` would put a hub's seventh row below the fold with no scrollbar
   anywhere on screen to say so. Unlike the rail, whose membership is CLOSED at
   seven ratified rows and which therefore keeps `overflow: hidden` honestly,
   these rows are the caller's and their count is not fixed by anything.

   `overflow-x: hidden` and not `auto`: the horizontal axis is the shell's
   business (kit-tablet-shell.css §8), and a second horizontal scroller nested in
   the first is two scrollbars for one gesture. */
.rd-thub__rows {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
}

/* ---------------------------------------------------------------------------
   4b. The pinned foot — the counterpart of §3, and UNDRAWN.
   --------------------------------------------------------------------------- */
/* Nothing in the corpus draws a block below a hub's rows. The slot exists
   because James ruled Profile's Sign Out onto the hub itself (batch 27) and that
   control is not a destination: `HubRow.to` is required — "a row with no
   destination … would let a hub grow an inert row nobody had to defend" — so the
   act sits below the rows rather than being modelled as one of them.

   This rule is geometry only. `flex: none` so it never scrolls and never gets
   compressed by a long row list; a top hairline so it is separated from the last
   row the way the block above the rows is separated from the first. No padding,
   no colour, no font and NO SELECTION IDIOM — what goes inside belongs to the
   screen (s18-tablet.css owns Profile's), and a `border-left` here would light a
   block that leads nowhere. */
.rd-thub__foot {
  flex: none;
  border-top: 1px solid var(--rd-line);
}

/* ---------------------------------------------------------------------------
   5. The row, its two drawn densities, and the selection idiom.
   --------------------------------------------------------------------------- */
/* rd-tablet-social.jsx:36 ("minHeight: 56, display: 'flex'") — `minHeight: 56,
   display: 'flex', alignItems: 'center', gap: 12, padding: '0 18px',
   borderBottom: 1px solid t.line, borderLeft: 3px solid (sel ? t.acc :
   transparent), background: sel ? t.surf2 : transparent`.

   ⚠️ THE 3px RULE IS DECLARED TRANSPARENT ON EVERY ROW AND COLOURED ON ONE. The
   drawing does the same (`3px solid ${... : 'transparent'}`) and it is not
   decoration: declaring the border only when selected shifts every label 3px
   sideways the moment a row lights up. Same construction and same reason as
   `.rd-stabs__tab`'s 2px transparent underline.

   ⚠️ TWO DENSITIES, AND THE DISCRIMINATOR IS THE SUB-LINE. The two drawn hubs
   disagree about three numbers at once, and they disagree TOGETHER:

     row 56px, label 13.5px, count 13px   Community, rows of one line
       (rd-tablet-social.jsx:36, :32, :33)
     row 62px, label 13px,   count 12px   Profile, rows of a label and a sub-line
       (rd-tablet-identity.jsx:44, :47, :50)

   Three properties moving as a set is a density rather than drift, so the taller
   set is a `--tall` modifier keyed on whether the column's rows carry a sub-line.
   ⚠️ IT IS A PROPERTY OF THE COLUMN, NOT OF THE ROW: hub_rail.rs computes it once
   over the whole row set, because a column of 56s and 62s interleaved is drawn
   nowhere and would read as a rendering fault. Pinned by
   `the_two_drawn_densities_move_as_a_set` in hub_rail.rs.

   `min-height`, never `height`: a clamped label is one line, but the row must
   still grow rather than clip if a caller's sub-line wraps despite the clamp.

   NO `border-top` anywhere: each row's bottom rule is its successor's top edge,
   and the head at §3 draws the first one. Declaring both doubles every seam. */
.rd-thub__row {
  min-height: 56px;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 18px;
  border-bottom: 1px solid var(--rd-line);
  border-left: 3px solid transparent;
  text-decoration: none;
  box-sizing: border-box;
}

.rd-thub__row--tall {
  min-height: 62px;
}

/* The selection, both halves. The left rule is what you see across the column;
   the ground is what tells you which row it belongs to when the rule is at the
   far edge of 340px. The drawn pair is `borderLeft: 3px solid t.acc` and
   `background: t.surf2` (rd-tablet-social.jsx:36, rd-tablet-identity.jsx:44) and
   neither half is optional — the rail's own rule, one component over. */
.rd-thub__row--on {
  border-left-color: var(--rd-acc);
  background: var(--rd-surf2);
}

/* The flexible middle. Drawn as a bare `<span style={{flex: 1}}>` in Community,
   which has one line, and as a `<div style={{flex: 1, minWidth: 0}}>` in
   Profile, which has two. Always rendered here, in both densities, so the two
   drawings share one anatomy instead of one of them growing a wrapper. The
   `min-width: 0` is Profile's and is load-bearing for the clamp: without it the
   flex child refuses to shrink below its text and the ellipsis never appears. */
.rd-thub__body {
  flex: 1;
  min-width: 0;
}

/* rd-tablet-social.jsx:37 ("fontSize: 13.5") — `tbody, fontSize: 13.5, color:
   sel ? t.fg : t.fg2`. Quiet by default and full strength when selected, which
   is the same two-token pair `.rd-stabs__label` uses and the rail's `on ? t.fg :
   t.fg2`. Colour is never the only signal — the 3px rule and the ground carry it
   for a reader who cannot see the difference between fg and fg2. */
.rd-thub__label {
  font-size: 13.5px;
  color: var(--rd-fg2);
}

.rd-thub__row--tall .rd-thub__label {
  font-size: 13px;
}

.rd-thub__row--on .rd-thub__label {
  color: var(--rd-fg);
}

/* ---------------------------------------------------------------------------
   6. The sub-line — Profile's second line, FLOORED UP.
   --------------------------------------------------------------------------- */
/* rd-tablet-identity.jsx:48 ("fontSize: 10.5") — `tbody, fontSize: 10.5, color:
   t.fg3, marginTop: 3, tclamp(1)`.

   ⚠️ 11px, NOT THE DRAWN 10.5. This is a mixed-case sentence — "Tag, photo,
   display name", "Email, password, sign out" — and `01 Foundations` §05 says
   mixed-case text never goes below 11px on the tablet. The 9.5px exemption is for
   condensed uppercase with tracking and this is neither. The rail's own drawn
   subtitle records the same correction from the other direction, in the corpus:
   "11, not 10 - the tablet mixed-case floor, set with the Foundations tablet
   scale (18 Aug). Tracked uppercase may go to 9.5; sentences may not."
   (rd-tablet-kit.jsx:184 ("11, not 10 - the tablet mixed-case floor")). So this
   is the floor being applied where the drawing predates it, not a size being
   improved.

   The clamp is the drawing's `tclamp(1)`, carried at the call site as
   `rd-clamp-1` (kit-clamp.css) rather than re-declared here — the fifth site to
   use that utility instead of writing the four-line incantation again. */
.rd-thub__sub {
  margin-top: 3px;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   7. The count — the numeric role, DERIVED BY THE CALLER, or absent.
   --------------------------------------------------------------------------- */
/* rd-tablet-social.jsx:38 ("fontSize: 13, color: n === sel") — `mono, fontSize:
   13, color: sel ? t.acc : t.fg3, flex: 'none'` — and rd-tablet-identity.jsx:50
   ("fontSize: 12, color: n === sel"), the same rule one density down.

   ⚠️ ACCENT ON SELECTION IS THE DRAWN RULE AND IT HAS A STATED REASON.
   `13 (Tablet) Community` row 01, notes: "Counts stay at the end of the row and go
   accent on selection, because the number is what decides whether you tap".

   `mono` is the kit's NUMERIC ROLE, not a system mono: Archivo 800 / width 75%
   / tabular — "Numbers are the loudest thing on a scoring surface, so they speak
   in the display voice" (substrate §P2, adjudicated 2026-08-04, `.rd-num` in the
   token layer). `tabular-nums` is what stops a column of counts jittering as
   digits change.

   ⚠️ THE STRING IS NEVER TYPED HERE.
   rd-tablet-kit.jsx:209 ("EVERY COUNT THE CHROME QUOTES LIVES HERE, DERIVED"):
   "EVERY COUNT THE CHROME QUOTES LIVES HERE,
   DERIVED, and nothing outside this table is allowed to type one." The drawn hubs
   obey it — every Community count is `tn(...)` over a fixture and the two search
   rows carry none, "because they are searches, not collections: there is nothing
   to count until you type"
   (rd-tablet-social.jsx:25 ("Every count on the hub derives")). Ours is an `Option<String>` per row supplied by the screen; the
   words-or-nothing contract is on `HubRow` in hub_rail.rs. */
.rd-thub__count {
  flex: none;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  color: var(--rd-fg3);
}

.rd-thub__row--tall .rd-thub__count {
  font-size: 12px;
}

.rd-thub__row--on .rd-thub__count {
  color: var(--rd-acc);
}

/* ---------------------------------------------------------------------------
   8. Focus — the house ring, negative offset.
   --------------------------------------------------------------------------- */
/* `--rd-acc` ring, `:focus-visible` so a pointer press does not leave one behind
   on the destination it just navigated to.

   THE OFFSET IS NEGATIVE, and for `.rd-trail__row`'s reason rather than by
   imitation: these rows are flush against each other and against the column's own
   1px edge, so a `+2px` ring paints over the row above, the row below, and the
   3px selection rule it sits on top of. */
.rd-thub__row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}
/* ==== kit-tab-chip · RdTabChip, the tablet filter chip · owner: spine ==== */

/* Drawn as `TabChip` in rd-tablet-kit.jsx:37 ("function TabChip"), exported at
   rd-tablet-kit.jsx:420 ("TabChip, TabDevice").
   Component: apps/web/src/components/kit/tab_chip.rs, which carries the ruling,
   the semantics and the four drawn call sites. Only the values live here.

   ANATOMY:

     [ hit: 44x44 minimum, paints nothing                       ]
     [   chip: 34px floor (40 tall), 13px sides, 8px chamfer     ]
     [     label: the caller's children, 7px apart               ]
     [     ring:  inset, drawn on :focus-visible only            ]

   ⚠️ THIS IS A SEPARATE PART FROM `.rd-chip`, AND THE OVERLAP IS DELIBERATE.
   The phone chip's rules live in `_legacy-kit-components-and-screens.css`, whose
   own banner reads "UNOWNED · DELETE-ONLY … no lane owns it, so no lane may add
   to it". A tablet modifier bolted onto `.rd-chip` would be adding to it. So the
   two share the TOKENS and nothing else: `--rd-chip`, `--rd-chip-on-fill` and
   `--rd-chip-on-label` are the token layer's (`_legacy-tokens.css`), which every
   part may read.

   ⚠️ SELECTED IS A FILL FLIP, AND THE OUTLINE CHIP THREE TABLET FILES DREW WAS
   DRIFT. Adjudicated answer #12, restated at rd-tablet-kit.jsx:30
   ("Filter chip, tablet scale"): "Adjudicated answer #12 rules the selected
   state a FILL FLIP - volt on dark, ink on light - because an accent border +
   accent label has no structural contrast at arm's length in daylight, and that
   rationale does not weaken on a tablet; with tablet-as-media-query it is also
   one DOM, which cannot carry two selected-state grammars. Three files drew an
   accent outline until 18 Aug - drift from the kit, not a device decision."

   The relay ruling of 2026-08-18 says the same thing from the other end
   (`tmp/design/snapshot/uploads/design-reply-2026-08-18-relay3.md` §1): "One DOM
   under tablet-as-media-query cannot carry two selected-state grammars, and the
   outline also lost the chamfer that #12 names."

   So there is NO `border` and NO `border-color` anywhere in this file, and that
   absence is the rule rather than an omission. An accent outline is one
   declaration away and it is the shape three corpus files reached for. Pinned by
   `ruling_the_selected_state_is_a_fill_flip_not_a_label_flip` in tab_chip.rs.

   ⚠️ TYPE FLOOR — CHECKED, NOT ASSUMED. `01 Foundations` §05, "Two floors, one
   exemption": "Mixed-case text never goes below 11px on the tablet. Condensed
   uppercase labels with tracking may go to 9.5px". The one size in this file is
   the 10px label at §3, which is BELOW the mixed-case floor and legal only as
   the second kind of line: condensed, tracked, uppercase. The transform is what
   makes the size legal, so it is asserted beside the size in tab_chip.rs and
   counted there rather than merely looked for.

   ⚠️ NO HEX ANYWHERE, INCLUDING IN THIS BANNER. Every colour is a token and the
   fill flip is carried as token VALUES, so the component is structurally
   identical in both themes — the architect ruling the phone chip's `--on` rule
   records, applied unchanged. */

/* ---------------------------------------------------------------------------
   1. The hit area — 44x44, painting nothing.
   --------------------------------------------------------------------------- */
/* ⚠️ THE DRAWN CHIP IS 34px TALL AND THE TABLET KIT'S OWN BANNER SAYS 44 IS THE
   MINIMUM. rd-tablet-kit.jsx:15 ("TOUCH SIZES DO NOT SHRINK BECAUSE A MOUSE
   MIGHT BE PRESENT"): "The same build serves an iPad and a laptop browser, and
   there is no way to know which is in front of you. 44px minimum stands
   everywhere in this kit." Both drawn heights — 34 and 40 — are under it.

   Resolved the way the phone chip already resolved the identical conflict
   (architect ruling, 08-tchip.md §4.1): the hit area expands and THE PAINTED BOX
   IS UNCHANGED. Expansion on padding would change the drawn design, and the
   drawn design is what was approved.

   `min-width` is here for symmetry with that ruling and fires far less often
   than it does on the phone: this chip carries 13px of horizontal padding
   against the phone's 8, so the shortest label in the four drawn rows ("Mine")
   already measures well over 44. It is not free — where it does fire it lays the
   row out on a 44px pitch — and that trade-off is the phone chip's note, made
   once and not re-argued here. */
.rd-tchip-hit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* ---------------------------------------------------------------------------
   2. The painted chip — the ground, the box and the 8px cut.
   --------------------------------------------------------------------------- */
/* rd-tablet-kit.jsx:39 ("minHeight: h, display: 'flex'") — `minHeight: h,
   display: 'flex', alignItems: 'center', gap: 7, padding: '0 13px'`, and
   rd-tablet-kit.jsx:41 ("...tcham(8), whiteSpace: 'nowrap'").

   `--rd-tchip-bg` restates the chip's own fill as a variable so the focus ring's
   hole can read it without knowing which state the chip is in. Its own name, not
   the phone's `--rd-chip-bg`: reading a custom property another part sets would
   couple this file to a DELETE-ONLY one.

   SINGLE LAYER, deliberately, exactly as the phone chip is: this box has a fill
   and no stroke, so the two-layer chamfer pattern (substrate §3.3) does not
   apply and RdChamferBox is not what this is.

   CHAMFER 8, bottom-right only, from `tcham(8)`. `tcham` itself is
   rd-train-kit.jsx:23 ("const tcham = (n)"), a bottom-right polygon — one px
   larger than the phone chip's 7, which is the whole of the tablet scale-up.
   Written literally rather than as a variable because the cut is fixed at all
   four drawn call sites; there is no scale to parameterise.

   `white-space: nowrap` from the drawing: a long label makes a long chip. It
   does not truncate and it does not wrap. */
.rd-tchip {
  position: relative;
  display: inline-flex;
  align-items: center;
  box-sizing: border-box;
  min-height: 34px;
  padding: 0 13px;
  --rd-tchip-bg: var(--rd-chip);
  background: var(--rd-tchip-bg);
  color: var(--rd-fg2);
  white-space: nowrap;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%);
}

/* The second drawn height, and the ONLY other one: `h={40}` at
   rd-tablet-social.jsx:386 ("h={40}"), the Leaderboard period control. Three of
   the four drawn sites take the 34 default and emit no modifier at all, which is
   the kit's own size-variant shape (`RepValueWidth::class`, `NumericSize::class`
   — the base rule holds the drawn value and the modifier is the deviation).

   A FLOOR, NOT A HEIGHT. `minHeight` in the drawing, and the Leaderboard site
   lays its three chips out in a grid, where a taller neighbour stretches the
   row. */
.rd-tchip--tall {
  min-height: 40px;
}

/* ⚠️ THE SELECTED STATE — THE FILL FLIP, AND NOTHING ELSE CHANGES.
   rd-tablet-kit.jsx:40 ("background: on ? (t.tex ? t.volt : t.fg)") —
   `background: on ? (t.tex ? t.volt : t.fg) : t.chip`,
   `color: on ? (t.tex ? t.onVolt : t.bg) : t.fg2`.

   That conditional IS `--rd-chip-on-fill` / `--rd-chip-on-label`, which the
   token layer already carries for the phone chip and which already resolve to
   `--rd-volt` / `--rd-on-volt` in dark and `--rd-fg` / `--rd-app-bg` in light
   (`_legacy-tokens.css`). Reading the drawn conditional back out as a pair of
   theme-scoped token values is the architect ruling the phone chip records: the
   component is structurally identical in both themes.

   NEVER `--rd-acc` for the fill. The two accent tokens hold the same value in
   dark theme, so writing the stroke token here is correct BY COINCIDENCE on the
   only screenshot anybody takes and renders brown-on-tan in light. It is
   the single most likely token error in this component. */
.rd-tchip--on {
  --rd-tchip-bg: var(--rd-chip-on-fill);
  background: var(--rd-tchip-bg);
  color: var(--rd-chip-on-label);
}

/* ---------------------------------------------------------------------------
   3. The label — the caller's children, and the type role that makes 10px legal.
   --------------------------------------------------------------------------- */
/* rd-tablet-kit.jsx:41 ("...tdisp(700, 78, '.08em'), fontSize: 10") —
   `tdisp(700, 78, '.08em')` at 10px. `tdisp` is ALWAYS uppercase; the transform
   lives in the helper (rd-train-kit.jsx:29, "const tdisp = (w, s, ls)"), so it
   is written out here rather than assumed.

   ⚠️ THE `text-transform` IS LOAD-BEARING AND IT IS NOT DECORATION. 10px is
   under the tablet's 11px mixed-case floor and above the 9.5px condensed-tracked
   exemption, so this line is legal ONLY while it is condensed, tracked and
   uppercase. Deleting the transform — the shape a "let this one be sentence
   case" edit takes — puts a mixed-case line a pixel under its floor with nothing
   else in the diff to say so.

   `gap: 7px` lives HERE and not on the chip: the drawing's flex row is the chip
   itself, but the People site nests a second element inside the chip
   (rd-tablet-social.jsx:131, the request count), so the gap has to fall between
   the CALLER'S children. Putting it on the chip would space the label against
   the ring instead, which is a gap between a thing and an invisible thing.

   `z-index: 1` because the ring is a positioned sibling that comes second in DOM
   order and would otherwise paint its opaque hole straight over the words. The
   phone chip records that this was caught in a render and by no checker. */
.rd-tchip__label {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  font-size: 10px;
  letter-spacing: .08em;
  text-transform: uppercase;
}

/* ---------------------------------------------------------------------------
   4. Focus — the inset ring, because a clip-path eats an outline.
   --------------------------------------------------------------------------- */
/* ⚠️ THE HOUSE `outline` RING IS NOT AVAILABLE ON THIS SHAPE, AND THAT IS A
   MECHANICAL FACT RATHER THAN A PREFERENCE. `.rd-stabs__tab`, `.rd-trail__row`
   and `.rd-tland__card` all take `outline: 2px solid var(--rd-acc)` because none
   of them is clipped. This chip is: `clip-path` clips BOTH `outline` and
   `box-shadow`, so neither can draw a ring that follows the 8px cut, and
   `border-radius` is banned system-wide so a rounded ring is not a fallback.

   ADJUDICATED — the designer's answer for exactly this case: "for single-layer
   chips, use an inset ring: a second clip-path'ed element inside the chip at
   chamfer `n`, drawn only on focus. It costs one node on a primitive that
   already has one." Quoted in the phone chip's part; the construction below is
   that answer at chamfer 8.

   Geometry, by the substrate's cut rule n = r - w(2 - SQRT2), which lives in
   exactly one tested function (`chamfer_box::inner_chamfer_px`) and is checked
   against these two literals by `the_focus_ring_geometry_is_the_house_formula_at_eight`:
     chip cut          8.00
     ring outer cut    8.00 - 2.0(2-SQRT2) = 6.83   (inset 2px inside the chip)
     ring hole cut     6.83 - 1.5(2-SQRT2) = 5.95   (the ring is 1.5px)

   ⚠️ THE HOLE TAKES THE CHIP'S OWN FILL, AND THE FILL FLIP IS WHAT MAKES THAT
   SOUND. `--rd-tchip-bg` is re-declared by `.rd-tchip--on`, so the hole is
   `--rd-chip` when unselected and `--rd-chip-on-fill` when selected and the ring
   reads as a stroke in both. This only works because EVERY state has a ground:
   an outline-only selected state — the drift this component exists to correct —
   would have left the hole painting a fill over a chip that has none. Pinned by
   `the_focus_ring_hole_takes_the_chip_ground_in_both_states`.

   `--rd-acc`, not `--rd-volt`: it is a stroke (substrate §1.1). That is also
   what keeps it legible on a SELECTED chip in dark theme, where a volt ring on a
   volt fill would be invisible.

   `:focus-visible` only, never `:focus` — a plain `:focus` ring on a chip tapped
   with a thumb leaves a stuck outline on a touch screen. The UA outline is
   removed only inside this rule and only because the rule replaces it: the UA
   draws a rectangle around the 44px hit box, which is both the wrong shape and
   the wrong size. */
.rd-tchip-hit:focus-visible {
  outline: none;
}

/* Present but invisible at rest, so focus costs no layout and no repaint of the
   chip itself — only this node's opacity changes. */
.rd-tchip__ring {
  position: absolute;
  inset: 2px;
  background: var(--rd-acc);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 6.83px), calc(100% - 6.83px) 100%, 0 100%);
  opacity: 0;
  pointer-events: none;
}

.rd-tchip__ring::after {
  content: "";
  position: absolute;
  inset: 1.5px;
  background: var(--rd-tchip-bg);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 5.95px), calc(100% - 5.95px) 100%, 0 100%);
}

.rd-tchip-hit:focus-visible .rd-tchip__ring {
  opacity: 1;
}

/* ⚠️ NO HOVER TREATMENT, AND THE ABSENCE IS DRAWN. rd-tablet-kit.jsx:145
   ("anchor inherits every style the div had"): "there is deliberately NO hover
   treatment - a hover state invented for the convenience of reading the corpus
   would end up quoted back as a design decision." If one is ever ruled in it
   goes under `@media (hover: hover)` and never under a width query.

   No disabled rule: no drawn call site disables a filter chip, and the phone
   chip's own note records that there is no disabled chip anywhere in the corpus.
   No loading rule: a chip tap never waits on the network. No pressed treatment:
   selection is instantaneous and local, so the result IS the feedback. */
/* ==== kit-tab-chip-row · RdTabChipRow, the list-pane filter strip · owner: spine ==== */

/* The strip a tablet list pane wears above its rows, holding `RdTabChip`s.
   Component: apps/web/src/components/kit/tab_chip_row.rs, which carries the
   four drawn call sites and the reasoning. Only the values live here.

   ANATOMY:

     [ strip: flex none, 7px gap, 14px/16px, one hairline under it ]
     [   the caller's RdTabChips, side by side                     ]

   ⚠️ A SEPARATE PART FROM `kit-tab-chip.css`, AND THE SPLIT IS THE POINT.
   That part's banner reads "there is NO `border` and NO `border-color` anywhere
   in this file, and that absence is the rule rather than an omission" — the
   guard that keeps the accent-outline chip three corpus files drew from coming
   back (adjudicated answer #12, relay-3 §1). This strip's divider is a drawn
   `border-bottom` on the STRIP, not on a chip, and putting it in that file
   would have cost that guard its literal form: the test behind it scans every
   border declaration in the part and admits exactly two resets. A second part
   keeps the chip's absence absolute and gives the strip a rule of its own.

   ⚠️ ONE STRIP, FOUR SITES, AND THE ONLY DIFFERENCE IS ONE PIXEL OF VERTICAL
   PADDING. Transcribed from the corpus rather than averaged:

     rd-tablet-training.jsx:97  ScreenTabDrills   padding '14px 16px'
     rd-tablet-livefire.jsx:27  ScreenTabAssess   padding '14px 16px'
     rd-tablet-social.jsx:128   ScreenTabPeople   padding '13px 16px'

   All three: `flex:'none', display:'flex', gap:7, borderBottom: 1px t.line`.
   14 is the base and 13 is the `--tight` modifier, so the modifier in the
   markup is always the deviation a reader can see — the same shape
   `.rd-tchip--tall` and `.rd-tpanes__list--320` take.

   The fourth site, the tablet challenges list, is NOT drawn: its Active /
   Completed pair is carried (see challenges.rs), and it takes `--tight` because
   it was built as a copy of the People strip in the same drawn document. That
   is recorded here rather than smoothed into "four drawn sites".

   ⚠️ NO `flex-wrap`. The Assessments strip carried one until this extraction;
   nothing in the corpus wraps a filter strip, and a wrapping strip in a 380px
   pane changes the pane's header height under the reader. Three chips at
   tablet scale fit every drawn width on the scale. */

.rd-tchips {
  flex: none;
  display: flex;
  gap: 7px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--rd-line);
}

/* rd-tablet-social.jsx:128 — one pixel tighter, top and bottom. */
.rd-tchips--tight {
  padding: 13px 16px;
}
/* ==== kit-loading · RdSkeleton + RdSweep, the loading vocabulary · owner: 1-track3 ==== */

/* Spec: docs/design/kit/L1-loading.md.
   Drawn in `43 Loading States.html` (the document) and `rd-loading.jsx` (the
   primitives). Components: apps/web/src/components/kit/skeleton.rs,
   apps/web/src/components/kit/sweep.rs, apps/web/src/components/kit/loading.rs
   and apps/web/src/components/kit/loading_phase.rs — which carry the rulings,
   the citations and the semantics. Only the values live here.

   ANATOMY:

     skeleton (a LAYOUT claim, drawn only where the layout is known)
       [ .rd-skel                 one chamfered block of skeleton ink, pulsing ]
       [ .rd-skel-stat            key line over value block                    ]
       [ .rd-skel-row             a surf card: mark + two lines                ]
       [ .rd-skel-stack           THREE rows, opacity 1 / .55 / .25            ]
       [ .rd-skel-table           real header row + three bands of cells       ]

     sweep (claims NOTHING but that work is happening — the app's one spinner)
       [ .rd-sweep                a chamfered square, stroked twice            ]
       [ .rd-sweep-stall          the sweep with the watchdog sentence UNDER it]

     .rd-sr-only                  the honest sentence, kept for assistive tech

   ⚠️ THIS PART CARRIES THE APP'S FIRST `@keyframes`, AND THAT IS A RULING
   RATHER THAN A LIBERTY. Verified on this tree before it was written:
   `grep -rn "@keyframes" apps/web/styles/` returned nothing at all. The
   designer's reply of 19 Aug 2026, §1a: "Animation: yes. The skeleton pulses by
   opacity (0.4 -> 1.0, 1.7s ease-in-out, staggered ~140ms per element). This is
   deliberate, not drift: a static grey block on this kit reads as a disabled
   control. It is the corpus's first animation ruling; prefers-reduced-motion
   freezes it at 60% opacity - the shape carries the meaning without the motion."

   Both keyframes are transcribed from rd-loading.jsx:17, which injects them as
   one style element:

     @keyframes rd-skel{0%,100%{opacity:.4}50%{opacity:1}}
     @keyframes rd-sweep{to{stroke-dashoffset:-100}}

   ⚠️ SKELETON INK IS ITS OWN TOKEN PAIR AND IS DELIBERATELY NOT A TOKEN FROM
   THE PALETTE. rd-loading.jsx:9-12, the designer's own words: "Skeleton ink is
   deliberately not a token from TT: it sits between surf and surf2 and pulses by
   opacity, so it reads as 'ground waiting to be content' rather than as a
   control or a divider. Volt never appears in a skeleton - accent is for content
   and acts, and a skeleton is neither."

   So the two values below are DEFINITIONS, not usages. Every other colour is a
   token, with ONE exception added 20 Aug 2026 and argued at the rule that
   carries it: `.rd-sweep--on-danger .rd-sweep__dash` strokes `#fff`, the
   foreground ink of `--rd-dfill`, because that ink has no token and the two
   danger-fill sites in the sheet both spell the literal. `--rd-skel-ink` is never a
   stroke and never `--rd-volt`; an engineering proposal that suggested reusing
   `--rd-chip` as the fill was overruled by the drawing.

   ⚠️ THE TOKEN PAIR IS DECLARED HERE AND NOT IN `_legacy-tokens.css`. That file
   is UNOWNED / DELETE-ONLY - "no lane owns this file, so no lane may add to it" -
   so a new pair goes in the part that owns it. The flip mechanism is that file's,
   copied exactly: `:root, [data-theme="dark"]` then `[data-theme="light"]`, and
   NO `@media (prefers-color-scheme)`, because theme here is an explicit stored
   preference resolved in Rust.

   ⚠️ THE ONE CLASS THIS PART DECLARES IN ANOTHER BLOCK'S NAME SPACE is
   `.rd-btn__face--inflight`, at §8. `.rd-btn__face` lives in
   `_legacy-kit-chamfer-button.css`, which is DELETE-ONLY, so the in-flight
   modifier ruled by the drawing's row 06 goes here instead. This part sits after
   that one in order.txt, so the modifier wins its tie rather than losing it. */

/* ---------------------------------------------------------------------------
   1. The ink — the new token pair, and the two keyframes.
   --------------------------------------------------------------------------- */
/* rd-loading.jsx:21 ("const skelInk") — `(t) => (t.tex ? '#2A2A2A' : '#DDDDD2')`,
   where `t.tex` is the dark theme. */
:root,
[data-theme="dark"] {
  --rd-skel-ink: #2A2A2A;
}

[data-theme="light"] {
  --rd-skel-ink: #DDDDD2;
}

@keyframes rd-skel {
  0%,
  100% {
    opacity: .4;
  }

  50% {
    opacity: 1;
  }
}

@keyframes rd-sweep {
  to {
    stroke-dashoffset: -100;
  }
}

/* ---------------------------------------------------------------------------
   2. The block — every skeleton shape is made of these.
   --------------------------------------------------------------------------- */
/* rd-loading.jsx:22-24 ("function Skel") — `w = '100%', h = 10, r = 4, d = 0`,
   `background: skelInk(t)`, `...tcham(r)`,
   `animation: 'rd-skel 1.7s ease-in-out infinite'`, `animationDelay: d`.

   `tcham` is the kit's bottom-right chamfer (rd-train-kit.jsx:23,
   "const tcham = (n)"). Written out per shape rather than parameterised: the
   drawing uses four radii across the vocabulary (3, 4, 6, 8) and the row card's
   10, and every one of them belongs to a named shape rather than to a caller.

   ⚠️ `--rd-skel-d` IS THE STAGGER, AND IT IS SET BY THE CONTAINER. The drawing
   passes `d` down from the stack, the stat and the table row - so the delay is a
   property of an element's POSITION, never of the block. Reading it from a
   custom property is what lets one rule below express "this element, plus its
   container's offset" instead of enumerating every product of the two. */
.rd-skel {
  background: var(--rd-skel-ink);
  animation: rd-skel 1.7s ease-in-out infinite;
  animation-delay: var(--rd-skel-d, 0ms);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 4px), calc(100% - 4px) 100%, 0 100%);
}

/* The three standalone blocks, at the drawn geometry of the site each is drawn
   at. There is deliberately no width/height prop: a caller reasoning about a
   skeleton's pixels is a caller drawing its own placeholder.

   rd-loading.jsx:224 ("Skel t={t} w=") — the vocabulary specimen's text lines,
   `w="58%" h={9}` and `w="82%" h={9}`. The wider one is the line. */
.rd-skel--line {
  width: 82%;
  height: 9px;
}

/* rd-loading.jsx:98 ("Skel t={t} w={52}") — the section-label placeholder that
   sits above the stack on first entry: `w={52} h={8}`. */
.rd-skel--label {
  width: 52px;
  height: 8px;
}

/* rd-loading.jsx:252 ("Skel t={t} w={120}") — the Pro table's filter control:
   `w={120} h={26} r={8}`. Skeleton because its options come from data, while
   the title and the column heads beside it are code and render real. */
.rd-skel--control {
  width: 120px;
  height: 26px;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%);
}

/* ---------------------------------------------------------------------------
   3. The stat — a key line over a value block.
   --------------------------------------------------------------------------- */
/* rd-loading.jsx:62-68 ("function SkelStat") — `display: 'grid', gap: 6`, then
   `w={44} h={7} d={d}` and `w={34} h={15} r={3} d={d + 100}`.

   The first-entry screen skeletons exactly three of these (rd-loading.jsx:96,
   `SkelStat` three times at d = 0 / 120 / 240) because the loaded screen has
   exactly three stats - "that layout is code, not data, so the claim is safe"
   (43 Loading States.html:117). The COUNT of stats is the caller's; this is one. */
.rd-skel-stat {
  display: grid;
  gap: 6px;
}

.rd-skel-stat__key {
  width: 44px;
  height: 7px;
}

.rd-skel-stat__val {
  width: 34px;
  height: 15px;
  animation-delay: calc(var(--rd-skel-d, 0ms) + 100ms);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 3px), calc(100% - 3px) 100%, 0 100%);
}

/* ---------------------------------------------------------------------------
   4. The row card — a mark and two lines on a real surface.
   --------------------------------------------------------------------------- */
/* rd-loading.jsx:44-52 ("function SkelRow") — the card is
   `background: t.surf, ...tcham(10), padding: '12px 14px', display: 'flex',
   gap: 12, alignItems: 'center'`; the mark is `w={30} h={30} r={6} d={d}`; the
   two lines sit in `display: 'grid', gap: 7` at `w="62%" h={9} d={d}` and
   `w="38%" h={7} d={d + 140}`.

   ⚠️ THE CARD ITSELF IS `--rd-surf`, NOT SKELETON INK. The card is the loaded
   screen's own row geometry - it is CODE - and only what the fetch owns is
   skeleton. That is the same rule the drawing states for carried-forward facts
   at 43 Loading States.html:116. */
.rd-skel-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  background: var(--rd-surf);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%);
}

.rd-skel-row__mark {
  flex: none;
  width: 30px;
  height: 30px;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%);
}

.rd-skel-row__lines {
  flex: 1;
  display: grid;
  gap: 7px;
}

.rd-skel-row__a {
  width: 62%;
  height: 9px;
}

/* `d + 140` in the drawing: the second line trails the first by one stagger
   step, inside whatever offset the container already carries. */
.rd-skel-row__b {
  width: 38%;
  height: 7px;
  animation-delay: calc(var(--rd-skel-d, 0ms) + 140ms);
}

/* ---------------------------------------------------------------------------
   5. The stack — THREE rows, and the fade IS the cap.
   --------------------------------------------------------------------------- */
/* rd-loading.jsx:40-43, the designer's reasoning, quoted because it is the
   ruling and not a note: "A skeleton list makes a COUNT claim as well as a shape
   claim, and the count is always a lie. The stack answers that: three rows at
   descending opacity, fading toward the ground - it claims 'a list goes here',
   never 'eight rows are coming'. Fixed at three by design; passing a count is
   the anti-pattern."

   rd-loading.jsx:57-58 — `display: 'grid', gap: 8`, and
   `[1, 0.55, 0.25].map((o, i) => <div style={{ opacity: o }}><SkelRow d={i * 140} /></div>)`.

   ⚠️ THE WRAPPER'S OPACITY AND THE BLOCK'S PULSE ARE DIFFERENT ELEMENTS, which
   is what makes reduced motion safe: freezing `.rd-skel` at 60% leaves the fade
   - the part of this shape that carries the "a list goes here, not eight rows"
   claim - completely intact. Collapsing the two onto one element would trade the
   ruling for a keyframe. */
.rd-skel-stack {
  display: grid;
  gap: 8px;
}

.rd-skel-stack__item:nth-child(1) {
  opacity: 1;
  --rd-skel-d: 0ms;
}

.rd-skel-stack__item:nth-child(2) {
  opacity: .55;
  --rd-skel-d: 140ms;
}

.rd-skel-stack__item:nth-child(3) {
  opacity: .25;
  --rd-skel-d: 280ms;
}

/* ---------------------------------------------------------------------------
   6. The table — the header row is REAL and the cells are skeleton.
   --------------------------------------------------------------------------- */
/* rd-loading.jsx:241-243 ("Desktop scale"): "A data table is the strongest
   layout claim in the product - the header row is REAL (columns are code, not
   data) and the skeleton fills the geometry those columns already declare."

   rd-loading.jsx:245-246 — `const cols = '1.6fr 1fr 1fr 90px'` and
   `['Name', 'Qualifications', 'Last Attempt', 'Status']`.
   rd-loading.jsx:257 — the head band, `gap: 18, padding: '10px 20px'`, one
   `1px solid t.line` bottom rule. The 18 is rd-pro-library's, and it arrives
   here as `--rd-ptable-head-gap` rather than as a number (C-43.2, below).
   rd-loading.jsx:260-263 — three bands at `[1, 0.55, 0.25]`,
   `padding: '13px 20px'`, cells `w="70%" h={9} d={i*140}`, `w={46} h={9} d={i*140}`,
   `w={72} h={9} d={i*140 + 100}` and `w={54} h={16} r={6} d={i*140 + 100}`.

   ⚠️ THE COLUMN COUNT IS THE CALLER'S AND THE TEMPLATE GENERALISES THE DRAWN
   FOUR. `--rd-skel-cols` is the number of middle columns the component computes
   from the header labels it was given, so the drawn `1.6fr 1fr 1fr 90px` is
   exactly what four labels produce. Marked as engineering-drafted in the spec:
   the drawing draws one table and does not say what a five-column one does. */
.rd-skel-table {
  display: grid;
}

.rd-skel-table__head,
.rd-skel-table__row {
  display: grid;
  grid-template-columns: 1.6fr repeat(var(--rd-skel-cols, 2), 1fr) 90px;
  align-items: center;
}

/* ⚠️ THE HEAD BAND IS THE LOADED TABLE'S HEAD BAND — ruled 27 Aug, C-43.2:
   "The loaded header is the header … one header component so nothing shifts
   when data lands" (`uploads/design-reply-2026-08-27.md`). The drawing was
   edited the same day to match: "the header row is REAL and it is
   rd-pro-library's header verbatim … 9pt, .11em, 18px gaps"
   (`rd-loading.jsx:254-256`).

   So the gap is `.rd-ptable__head`'s own token and NOT a 18 re-typed here, and
   the head cells wear `.rd-ptable__hcell` itself rather than a `__h` of their
   own — see the markup in `components/kit/skeleton.rs`. What this file shipped
   before the ruling was the pre-edit drawing: `gap: 14`, `8.5px`, `.12em` — a
   header that grew half a point and lost tracking the instant data landed,
   which is the shift C-43.2 forbids.

   The ROWS keep 14: they are skeleton bars, not a second header, and the
   drawing draws them at 14 (`rd-loading.jsx:261`). */
.rd-skel-table__head {
  gap: var(--rd-ptable-head-gap);
  padding: 10px 20px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-skel-table__row {
  gap: 14px;
  padding: 13px 20px;
}

.rd-skel-table__row:nth-child(2) {
  opacity: 1;
  --rd-skel-d: 0ms;
}

.rd-skel-table__row:nth-child(3) {
  opacity: .55;
  --rd-skel-d: 140ms;
}

.rd-skel-table__row:nth-child(4) {
  opacity: .25;
  --rd-skel-d: 280ms;
}

/* The three drawn cell geometries. The name cell is proportional, the middle
   cells are fixed lines, and the last column is the status chip. */
.rd-skel-table__cell {
  height: 9px;
}

.rd-skel-table__cell--name {
  width: 70%;
}

.rd-skel-table__cell--mid {
  width: 46px;
}

/* `d + 100` in the drawing: everything past the first two columns trails by the
   second stagger step. */
.rd-skel-table__cell--late {
  width: 72px;
  animation-delay: calc(var(--rd-skel-d, 0ms) + 100ms);
}

.rd-skel-table__cell--chip {
  width: 54px;
  height: 16px;
  animation-delay: calc(var(--rd-skel-d, 0ms) + 100ms);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%);
}

/* ---------------------------------------------------------------------------
   7. The sweep — one chamfered square, stroked twice.
   --------------------------------------------------------------------------- */
/* rd-loading.jsx:26-29, the designer's own header, quoted whole: "THE SWEEP. One
   chamfered square, stroked twice: the track in `line`, and a 30% dash in `acc`
   travelling the perimeter. pathLength=100 so the dash math is size-independent.
   It is the app's one spinner - in-place refresh, in-flight buttons, and the
   no-claim fallback all use this same mark at different sizes."

   rd-loading.jsx:31 — the path, `M1 1 H17 V11.5 L11.5 17 H1 Z`, on an 18-unit
   viewBox: the chamfer as a PATH rather than a clip, because a stroke is what is
   being drawn.
   rd-loading.jsx:34-35 — both strokes are width 2; the dash is `30 70` at
   `pathLength="100"`, so the dash is 30% of the perimeter at every size.

   ⚠️ THE ACCENT IS `--rd-acc`, NEVER `--rd-volt`. It is a STROKE (substrate
   §1.1), and the two tokens hold the same value in dark - so `--rd-volt` here
   would be correct by coincidence on the only screenshot anybody takes and
   tan-on-white in light. 43 Loading States.html:103 says the same from the
   drawing's side: "Light theme swaps the dash to the dark olive accent, same
   rule as all accent-as-stroke in the kit."

   The size is set by a modifier, never by a prop the caller computes: the four
   ruled sizes are the LADDER - 10 / 13 / 18 / 26 (C-43.1, 27 Aug 2026) - carried
   as roles (a region label; a control in flight, which the freshness line and the
   search field's end slot now join at 13; the pane-sized rung; the no-claim
   ground), not as dimensions a caller computes. */
.rd-sweep {
  display: block;
  flex: none;
}

/* 10px — a region label inside a ready page (43 Loading States.html:142). */
.rd-sweep--region {
  width: 10px;
  height: 10px;
}

/* 13px — the ladder's second rung, and the busiest: a control in flight beside
   its unchanged label (43 Loading States.html:53), the freshness line during an
   in-place refresh (:132), and a debounced search field's end slot.

   ⚠️ THE FRESHNESS LINE AND THE SEARCH FIELD WERE 12px AND ARE 13 — ruled
   27 Aug 2026, design-reply-2026-08-27.md C-43.1: "Ladder ruled: 10 / 13 / 18 /
   26 ... Build ask: sweep sizes snap to the ladder - the shipped 12s and 14
   become 13", and C-43.4 for the field ("its size is 13 - the earlier ruling's
   12 predates the ladder"). The `--fresh` modifier that held the 12 is retired
   with it; a rule at a size the ladder does not name is the drift the ladder was
   ruled to close. */
.rd-sweep--button {
  width: 13px;
  height: 13px;
}

/* 18px — the ladder's third rung. Drawn in the specimen row
   (rd-loading.jsx:234, "The sweep - 10 / 13 / 18 / 26") and named in prose
   (43 Loading States.html:101), with no shipped site yet: it is what a mark
   bigger than an inline 13 and smaller than the page-wide 26 takes, rather than
   a screen inventing 16 or 20. */
.rd-sweep--panel {
  width: 18px;
  height: 18px;
}

/* 26px — the no-claim fallback, centred on empty ground
   (rd-loading.jsx:183). */
.rd-sweep--fallback {
  width: 26px;
  height: 26px;
}

.rd-sweep__track {
  fill: none;
  stroke: var(--rd-line);
  stroke-width: 2;
}

/* ⚠️ ONE DURATION, AND IT IS THE ONE EVERY DRAWN USAGE SITE PASSES. The drawing
   defaults `speed = 1.1` (rd-loading.jsx:30) and then passes `speed={0.9}` at
   every site that actually mounts a sweep in a screen - rd-loading.jsx:137, :168,
   :198 and :201 - leaving 1.1 to the specimen ruler alone. The reply's "~1s
   linear" covers both. Taking the applied value; recorded in the spec as an
   engineering resolution rather than a transcription, because the drawing
   genuinely holds two numbers. */
.rd-sweep__dash {
  fill: none;
  stroke: var(--rd-acc);
  stroke-width: 2;
  stroke-dasharray: 30 70;
  animation: rd-sweep .9s linear infinite;
}

/* rd-loading.jsx:198 ("Sweep t={t} size={13} color={t.onVolt} track={false}") —
   on a volt fill the sweep drops its track and draws in `onVolt` black.
   43 Loading States.html:169: "On volt the sweep drops its track and draws in
   onVolt black - same mark, legible on the fill." */
.rd-sweep--on-volt .rd-sweep__dash {
  stroke: var(--rd-on-volt);
}

/* THE DANGER FILL — ruled 20 Aug 2026, round-7-loading-rulings-reply.md §1:
   "on a danger fill the sweep draws in the danger fill's foreground ink,
   trackless, 13px, label kept. The mark is the shape, not the colour - volt on
   a danger fill would put the accent-of-acts on a destructive act mid-flight,
   which is exactly the confusion the colour rules exist to prevent. No third
   mark."

   Same rule as the volt fill above and deliberately written the same way: one
   declaration, the stroke, with the track dropped in Rust by `has_track`.

   ⚠️ #fff IS THE THIRD HEX IN THIS FILE, and the header above says there are
   two. Both statements were true when written; this is the correction. The
   other two are the skeleton ink pair - definitions of a token this part mints.
   This one is a USAGE, and it is a usage of a value the sheet has no token for:
   --rd-dfill is the ground, and its foreground ink is spelled `#fff` at every
   danger-fill site that exists (.rd-confirm__go in adm1-5-1.css, .rd-close__go
   in adm2.css). adm1-5-1.css records the reasoning and the proposal: the kit
   has the exact precedent for tokenising it - --rd-on-volt, "text on a volt
   fill" - and --rd-on-dfill would be its sibling, but minting a shared token is
   not one lane's call. Until it is minted, matching the sites is what keeps the
   mark the same white as the label beside it; a token invented here would be a
   fourth spelling of one colour. */
.rd-sweep--on-danger .rd-sweep__dash {
  stroke: #fff;
}

/* ---------------------------------------------------------------------------
   7b. The watchdog — UNDER the sweep, never instead of it.
   --------------------------------------------------------------------------- */
/* rd-loading.jsx:71-73: "The watchdog, kept from the old system but demoted: it
   appears UNDER a sweep after ten seconds, never instead of one. The sentence was
   always honest; the failure was making it the whole treatment from millisecond
   zero."

   rd-loading.jsx:76-78 — `flexDirection: 'column', alignItems: 'center', gap: 14`,
   and the sentence at `fontSize: 11, color: t.fg3, textAlign: 'center',
   lineHeight: 1.5, maxWidth: 190, textWrap: 'pretty'`. */
.rd-sweep-stall {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

.rd-sweep-stall__note {
  max-width: 190px;
  font-size: 11px;
  line-height: 1.5;
  text-align: center;
  text-wrap: pretty;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   8. The in-flight control — the label STAYS and the sweep joins it.
   --------------------------------------------------------------------------- */
/* rd-loading.jsx:189-192, the designer's header, quoted whole: "In-flight
   controls. The label NEVER changes to 'Loading...' or 'Saving...' - a label swap
   resizes the button and rewrites what the act was. The sweep joins the label,
   the control stops accepting input, and it keeps its own colour: grey is
   DISABLED ('you can't'), in-flight is 'it's going'."

   rd-loading.jsx:197 and :200 — `gap: 10` between the mark and the word, on both
   the filled primary and the outlined secondary.

   This modifier hangs off `.rd-btn__face`, declared in
   `_legacy-kit-chamfer-button.css`, which is DELETE-ONLY. Both are single-class
   selectors, so this file's position after that one is what makes the modifier
   win. */
.rd-btn__face--inflight {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
}

/* ⚠️ RdConfirmModal's in-flight rules are NOT here, and the attempt to put them
   here is worth recording. `.rd-confirm__go` gained the same treatment on
   20 Aug (round-7-loading-rulings-reply.md §1) and this part looked like its
   home, by the `.rd-btn__face--inflight` precedent above. Two things said
   otherwise, and the second is the one that decided it:

     1. ORDER. That precedent wins its tie because
        `_legacy-kit-chamfer-button.css` sits BEFORE this part in order.txt.
        `adm1-5-1.css` sits after it, so the same trick loses and the selectors
        would have had to win on specificity instead.
     2. VOLT. The primary confirm's in-flight ground is `var(--rd-volt)`, and
        two `ruling_` tests over this part assert that --rd-volt appears in NO
        declaration in it - the drawing's "Volt never appears in a skeleton"
        (rd-loading.jsx:9). Those guards were right: a button ground is not this
        part's business, and a rule that had to be argued past a ruling was a
        rule in the wrong file.

   So the confirm's rules live in adm1-5-1.css beside `.rd-confirm__go` itself,
   and only the sweep TONE the ruling minted (`.rd-sweep--on-danger`, §7) lives
   here. */

/* ---------------------------------------------------------------------------
   9. The sentence, kept for assistive technology only.
   --------------------------------------------------------------------------- */
/* The honest-sentence rule is retired as a VISUAL treatment
   (43 Loading States.html:40) and survives as an announcement: `RdLoading` keeps
   its `role="status"` and its required noun, and renders here.

   The standard clip rectangle, not `display: none` and not `visibility: hidden`
   - both of those remove the element from the accessibility tree, which would
   delete the one thing this component is now for. Not `width: 0` either: a
   zero-width live region is skipped by some screen readers. */
.rd-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------------------------------------------------------------------------
   10. Reduced motion — both animations stop, both shapes stay.
   --------------------------------------------------------------------------- */
/* rd-loading.jsx:17, transcribed: the drawing writes
   `@media(prefers-reduced-motion:reduce){.rd-skel{animation:none!important;opacity:.6!important}
   .rd-sweep-d{animation:none!important}}`.

   `!important` is not carried over and is not needed: these rules are the last
   in the file and `@media` adds no specificity, so a single-class selector here
   beats the identical single-class selector above it on source order alone.
   Carrying `!important` across would put an un-overridable declaration in the
   sheet for a tie this file already wins.

   ⚠️ 60% IS EXACT AND IT IS NOT A ROUNDING OF THE PULSE. The pulse runs .4 to 1;
   the frozen value is .6, above the trough, so a reader with motion off sees a
   block that reads as ground rather than as the dimmest frame of an animation
   they cannot see. 43 Loading States.html:104: "prefers-reduced-motion stops both
   animations: the skeleton holds at 60% opacity, the sweep holds its dash still.
   The shapes carry the meaning without the motion." */
@media (prefers-reduced-motion: reduce) {
  .rd-skel {
    animation: none;
    opacity: .6;
  }

  .rd-sweep__dash {
    animation: none;
  }
}
/* ==== kit-clamp · .rd-clamp-1 / .rd-clamp-2, the line-clamp utility · owner: spine ==== */

/* The tablet kit says where this lives, and says it as a bug report:

     "Line clamp. Lives in the KIT, not in a page file: every tablet screen file
      uses it, and the first version of this was defined in rd-tablet-training.jsx
      - which worked for the three documents that happened to load that file and
      threw for the four that did not. Same mistake as renderQs, one file later."
     rd-tablet-kit.jsx:21 ("Line clamp. Lives in the KIT, not in a page file")

   The corpus's version is a function of n — rd-tablet-kit.jsx:25
   ("const tclamp = (n)") — because JSX can take a parameter. CSS cannot, so the
   two clamp depths the drawings and this app actually use become two classes.
   One and two are the whole set: four hand-rolled sites existed in this
   stylesheet before this part and used exactly `1` and `2`.

   ⚠️ FOUR SITES WERE MIGRATED ONTO THIS, NOT LEFT BESIDE IT. `.rd-msg__excerpt`
   (profile-community.css), `.rd-tdl-name` and `.rd-tdl-meta` (s03-drills.css)
   and `.rd-par__desc` (s10-par.css) each declared the same four-line incantation
   themselves. They now declare none of it and carry `rd-clamp-1`/`rd-clamp-2` at
   their call sites instead. That is what gives these classes a consumer today,
   and it is the same argument the corpus comment above makes: a clamp defined
   next to one screen works for the screens that happen to load it.

   ⚠️ SOURCE ORDER IS A HAZARD FOR A UTILITY, AND THIS PART SITS EARLY. It is
   placed with the kit parts, which are all above every page section — so a page
   rule declaring `display` or `overflow` on the same element, at equal
   specificity, WINS and silently unclamps it. Nothing in this stylesheet does
   today (checked: the four migrated rules declare neither any more). If a later
   part needs to unclamp one of these, it should drop the class at the call site
   rather than out-declare this rule from below, because the second one is
   invisible in review. */

/* -webkit-line-clamp with no standard fallback is deliberate: the standard
   `line-clamp` shorthand still needs the `-webkit-box` display in every engine
   that ships it, so the prefixed set IS the interoperable spelling today. All
   four migrated rules used exactly this quartet. */
.rd-clamp-1,
.rd-clamp-2 {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.rd-clamp-1 { -webkit-line-clamp: 1; }
.rd-clamp-2 { -webkit-line-clamp: 2; }
/* ==== kit-switch-door · RdSwitchDoor, the shooter↔Pro door · owner: 3-doors ==== */

/* Drawn as `DoorFixture` in rd-doors.jsx:42 ("function DoorFixture"), under
   `44 The Doors.html`. Component:
   apps/web/src/components/kit/switch_door.rs, which carries the ruling, the
   untruncated-name divergence and the two floored type sizes. Only the values
   live here.

   ANATOMY:

     [ foot: hairline above, 12px 12px 14px ]
       [ link ]
         [ box: ctrl 1.5px, chamfer 10, hollow, inner 10px 12px ]
           [ lab: "Switch to" ]
           [ row: mark 30 · (name / sub) · swap mark ]

   ⚠️ THIS IS A FIXTURE, NOT A ROW. `44 The Doors.html`: "rows are flush ground,
   the door is a drawn object sitting at the rail's foot below a hairline. It is
   a fixture, not an eighth section." So the hairline and the padding below are
   not decoration — they are what separates the door from the nav above it, and
   removing either turns it back into a row.

   ⚠️ CTRL, NEVER VOLT. The outline is `--rd-ctrl` through
   `Stroke::Ctrl`/`Fill::Hollow` on `RdChamferBox`, which is also where the
   two-layer clip geometry comes from. No colour and no chamfer is re-derived in
   this file: `--rd-box-r` / `--rd-box-ri` arrive from the component, and the
   stroke and fill roles are classes on the box. What is authored below is
   layout, type and the focus layer. */

/* ---------------------------------------------------------------------------
   1. The foot — the hairline, the air, and the push to the bottom.
   --------------------------------------------------------------------------- */
/* rd-doors.jsx:44 ("borderTop: `1px solid ${t.line}`") over
   `padding: '12px 12px 14px'` on the same line.

   `margin-top: auto` IS THE DRAWN SPACER. Both drawn rails put a
   `<div style={{ flex: 1 }} />` between the last nav row and the fixture
   (rd-doors.jsx:99, and the Pro rail's own `flex: 1` list at :113) so the door
   sits at the foot however few rows are above it. An auto margin is that
   spacer with no element in it, which is the same "no empty element" call
   `.rd-trail__sub` and `.rd-tshell__rail` both make: a zero-height flex child
   is still a child, and the next `gap` rule on either rail would spend on it.

   `flex: none` so the fixture is never the thing that shrinks. Both consumers
   are flex columns; there is no drawn use outside one. */
.rd-door {
  flex: none;
  margin-top: auto;
  border-top: 1px solid var(--rd-line);
  padding: 12px 12px 14px;
  font-family: 'Archivo', sans-serif;
}

/* The whole object is one link. `display: block` so the box below fills the
   foot's content width rather than shrinking to its text. */
.rd-door__link {
  display: block;
  text-decoration: none;
  color: inherit;
}

/* No drawn focus state, so this is the house treatment: the kit's 2px
   `--rd-acc` ring, `:focus-visible` so a pointer press does not leave one
   behind.

   THE OFFSET IS POSITIVE AND THE RING IS ON THE LINK, not on `.rd-door`. The
   foot carries 12px of padding, so a ring on it would float clear of the object
   it describes and touch the nav row above; on the link it sits 2px off the
   drawn box, well inside that padding. An outline is safe here because the link
   itself is not clipped — the chamfer is on the box INSIDE it, which is exactly
   why the ring is not on the box either (substrate §1.5: clip-path clips an
   outline as readily as a box-shadow). */
.rd-door__link:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* rd-doors.jsx:43 ("padding: dense ? '9px 11px' : '10px 12px'"). */
.rd-door__inner {
  padding: 10px 12px;
}

/* ---------------------------------------------------------------------------
   1a. The dense form.
   --------------------------------------------------------------------------- */
/* ⚠️ THE `dense` VARIANT IS BUILT AS OF 2026-08-21, AND ITS OLD NOTE WAS A
   CONDITION RATHER THAN A REFUSAL. This block replaces it; it read:

     "THE `dense` VARIANT IS NOT BUILT, AND THAT IS DELIBERATE. The drawing
      declares it and then never calls it — every one of the four drawn call
      sites takes the default. A variant with no drawn instance is a value
      invented at the call site, which is the thing the kit's closed enums exist
      to prevent. If a dense foot is ever drawn, it arrives as a variant here
      with its own site."

   The drawn instance arrived: rd-pro-mobile.jsx:110 ("<DoorToShooter t={t}
   dense />"), at the foot of the Pro phone drawer, and 45 (Mobile) Pro
   Shell.html names the site in prose — "The switch fixture from 44, dense form,
   at the drawer's foot."

   ITS SITE, NAMED: `.rd-prom__foot` in adm0-mobile.css, mounted by `ProDrawer`
   in apps/web/src/components/pro/mobile.rs. That is the ONLY site. A second one
   needs a second drawing, which is the whole of what the old note asked for.

   Values, both halves of the drawn ternaries:
     foot padding  rd-doors.jsx:42 ("dense ? '10px 10px 12px'")
     inner padding rd-doors.jsx:43 ("dense ? '9px 11px'")

   Nothing else changes — the type, the marks, the hairline and the focus ring
   are the fixture's and stay identical at both feet, because "the same fixture
   renders at both ends, so the two feet teach each other". Dense is a geometry
   axis, not a second component. */
.rd-door--dense {
  padding: 10px 10px 12px;
}

.rd-door__inner--dense {
  padding: 9px 11px;
}

/* ---------------------------------------------------------------------------
   2. The label voice.
   --------------------------------------------------------------------------- */
/* rd-doors.jsx:46 ("tdisp(700, 78, '.2em')") at 8.5px, `color: t.fg3`,
   `marginBottom: 7`. tdisp is always uppercase; the transform is in the helper
   (substrate §2.1), so it is written out rather than assumed.

   ⚠️ 9.5px, NOT THE DRAWN 8.5 — FLOORED BY THE TYPE SCALE, NOT PREFERRED.
   `01 Foundations` §05 Type Scale, "Two floors, one exemption": mixed-case text
   never goes below 11px on the tablet, and "Condensed uppercase labels with
   tracking may go to 9.5px". This label is condensed uppercase with .2em of
   tracking, so 9.5 is its floor and 8.5 is below it. The same correction
   `.rd-trail__sub` took when it shipped 11 against a drawn 10 — and the corpus
   later agreed with the sheet rather than the other way round. */
.rd-door__lab {
  display: block;
  font-weight: 700;
  font-stretch: 78%;
  font-size: 9.5px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--rd-fg3);
  margin-bottom: 7px;
}

/* ---------------------------------------------------------------------------
   3. The identity row — mark, name, swap.
   --------------------------------------------------------------------------- */
/* rd-doors.jsx:47 ("gap: 10"). */
.rd-door__row {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* rd-doors.jsx:30 ("size = 30") — the mark is a 30px square in both directions.
   The drawing says so in as many words at :28: the RD monogram is "the shooter
   side's answer to OrgMark, same geometry". Only the paint differs, and the
   paint is a role on `RdChamferBox` rather than a value here. */
.rd-door__mark {
  display: block;
  flex: none;
  width: 30px;
  height: 30px;
}

.rd-door__mark-face {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* rd-doors.jsx:31 ("fontSize: size * 0.36") — 30 x 0.36 = 10.8, the same ratio
   `OrgMark` carries. Above the 9.5px tracked-uppercase floor, so the drawn size
   ships unchanged.

   `--rd-fg2` is the quiet monogram: "a destination being named, not the org you
   are in". */
.rd-door__mark-text {
  font-weight: 800;
  font-stretch: 78%;
  font-size: 10.8px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--rd-fg2);
}

/* The org mark's ink now comes from `.rd-pro-mark__text--on` (adm0-chrome.css)
   via the kit `OrgMark`, not this rule (#1402). */

.rd-door__text {
  flex: 1 1 auto;
  min-width: 0;
}

/* rd-doors.jsx:48 ("tdisp(800, 78, '.04em'), fontSize: 12, color: t.fg,
   lineHeight: 1.25").

   ⚠️ **`color: var(--rd-fg)` — AND ITS ABSENCE IS THE 2026-08-29 REHEARSAL
   DEFECT.** James reported the tablet rail's door drawing "Range Day Pro" in
   near-black on the dark rail ground. This rule declared NO `color` at all, so
   the name took whatever it inherited, and `.rd-door__link` passes inheritance
   straight through (`color: inherit`). What it inherited is not a token: the
   document's own body carries the pre-kit Tailwind utility `text-gray-900`
   (`apps/web/index.html:279`), declared at `_legacy-base.css:94` as a flat
   `color: #111827`. Nothing between the body and this span sets `color` — not
   `.rd-trail`, not `.rd-tshell`, not `.rd-box`/`.rd-box__inner` — so #111827
   reached the name in BOTH themes. It happens to read on the light ground,
   which is why it survived, and it is unreadable on the dark one.

   The drawn value has always been `t.fg`, and `adm0.css` §"the door's ink"
   already lists `title -> --rd-fg (.rd-door__name)` — the part simply never
   authored it. Declared here, in the token, so neither theme depends on an
   inherited literal. The sub's `--rd-fg3` below was authored and is unaffected.

   ⚠️ **C-44.1 — THE NAME WRAPS, AND EXPORT 30 NOW DRAWS IT THAT WAY.** This
   block used to record a divergence: the drawing's prose ruled the name
   untruncated while its own transcribed style set `whiteSpace: 'nowrap'` with
   `textOverflow: 'ellipsis'`. C-44.1 (ruled 27 Aug, shipped #1291) settled it
   for the whole Doors family — long names WRAP — and the drawing was corrected
   with it: `rd-doors.jsx:48` in export 30 carries neither declaration, and
   `rd-doors.jsx:62-64` states the ruling in the drawing's own words ("Ruled 27
   Aug:" / "that rule stands and long names WRAP — the fixture's old
   nowrap/ellipsis" / "contradicted the rule it demonstrates"). The divergence note is retired
   rather than reworded; the citation above now points at the corrected line.

   So the wrap is transcription, not deviation, and `line-height: 1.25` comes
   with it — the drawn value for the two-line case, which `profile-community.css`
   and `s18-tablet.css` already carry for the same case. It shipped at 1.2 from
   the pre-30 line, which drew none (a nowrap line needs no wrapped rhythm).

   `overflow-wrap: anywhere` rather than a bare wrap, because a single
   unbreakable token (a long one-word org name) would otherwise overflow the
   208px rail rather than break inside it. The rail this must survive is 208px
   (`kit-tablet-rail.css` §1, ratified 2026-08-17) — and export 30 now says 208
   in the drawing too (`rd-doors.jsx:61`, "the name survives the 208px rail
   untruncated"), which retires the old 220-versus-208 discrepancy this block
   used to record.

   Pinned by `ruling_the_destination_name_never_truncates`,
   `ruling_the_switch_doors_name_reads_in_fg_on_both_grounds` and
   `ruling_a_long_org_name_wraps_the_door_per_c_44_1` in switch_door.rs. */
.rd-door__name {
  display: block;
  font-weight: 800;
  font-stretch: 78%;
  font-size: 12px;
  letter-spacing: .04em;
  text-transform: uppercase;
  line-height: 1.25;
  color: var(--rd-fg);
  overflow-wrap: anywhere;
}

/* rd-doors.jsx:49 ("tbody, fontSize: 10.5, color: t.fg3, marginTop: 3") — line
   repointed at export 30, which moved the fixture's body up two lines.
   `tbody` is font-family only, so NO `font-stretch` is authored
   here and 100 arrives as the CSS initial value, which is what the body role
   means everywhere in this kit.

   ⚠️ 11px, NOT THE DRAWN 10.5 — the mixed-case half of the same floor the label
   takes above. `01 Foundations` §05: mixed-case text never goes below 11px on
   the tablet, and "the kit's own 10px rail sublines are corrected with this
   entry". This is mixed-case body copy — `tbody` sets no `text-transform` — so
   the 11px floor is the one that governs, not the 9.5px tracked-uppercase one. */
.rd-door__sub {
  display: block;
  font-size: 11px;
  line-height: 1.3;
  color: var(--rd-fg3);
  margin-top: 3px;
}

/* ---------------------------------------------------------------------------
   4. The swap mark.
   --------------------------------------------------------------------------- */
/* rd-doors.jsx:19 ("size = 15") in a 16x12 space, so the rendered height is
   15 x 0.75. The pro kit's own transcription rounds that to 11; 11.25 is the
   value that keeps the viewBox's aspect, so the arrows are not squashed by 2%.

   ⚠️ --rd-acc, AND IT IS THE ONLY ACCENT IN THE FIXTURE. "Two opposed arrows in
   accent ... Accent because the fixture is an act; the marks and names inside
   stay quiet." A stroke takes acc rather than volt per substrate §1.1. `stroke`
   is inherited in SVG, so declaring it once here paints both paths — neither
   carries a `stroke` attribute of its own, which is what lets a token reach them
   at all (a `var()` is not substituted inside an SVG presentation attribute). */
.rd-door__swap {
  color: var(--rd-fg);
  display: block;
  flex: none;
  width: 15px;
  height: 11.25px;
  stroke: var(--rd-acc);
}
/* ==== kit-keypad · RdKeypad, the digit-entry keypad · owner: 1-track3 ==== */

/* Spec: docs/design/kit/14-keypad.md. Component:
   apps/web/src/components/kit/keypad.rs, which carries the API, the ⌫-glyph
   open question and the reason the blank slot is a position rather than a node.
   Only the values live here.

   Drawn inline inside `ScreenROEntry`, `rd-ro-day.jsx:289-291`
   ("gridTemplateColumns: '1fr 1fr 1fr'"), under `47 (Mobile) RO Range Day.html`
   row 04.

   ANATOMY:

     [ pad: grid, 3 x 1fr, gap 6, NO WIDTH ]
       [ key x9 : 1..9 ]
       (        blank column 1        )
       [ key--zero : column 2 ] [ key : ⌫ ]
         each key: [ ring ] + the glyph

   ⚠️ THE PAD DECLARES NO WIDTH, and that is the rule this file exists to keep.
   It is as wide as whatever mounts it leaves it and never wider. A `width` or
   `max-width` here is a second number able to disagree with the column that
   mounts it — the same conclusion `.rd-hf-tab__pad` reached independently on
   the tablet, for its own reason ("a numeric pad spread across 600px is further
   to travel per digit"). Two surfaces, one rule, pinned by
   `ruling_the_pad_declares_no_width`.

   ⚠️ NO BORDER, AND NO `--rd-ctrl`. The substrate's "`ctrl` bounds anything you
   can tap that is not filled volt" is about HOLLOW controls that would
   otherwise have no edge. These cells are filled — `t.surf2` against the
   screen's `t.bg` — so a `ctrl` border would draw an edge the drawing does not
   have on an object that does not need one. This is where this pad and the
   tablet's hollow one genuinely differ; see spec §10.

   ⚠️ NO HOVER TREATMENT, AND THE ABSENCE IS DRAWN. A phone surface for a thumb:
   a hover highlight leaves a sticky one under the last-tapped key on a touch
   screen.

   NO @media. Both surfaces this can mount on are renderings the components
   already choose between in Rust. */

/* ---------------------------------------------------------------------------
   1. The pad — three columns, and no opinion about its own width.
   --------------------------------------------------------------------------- */
/* `display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 6` at
   rd-ro-day.jsx:289. `alignContent: 'start'` is drawn on the same line and is
   what stops four rows of 56px stretching to fill a tall flex child — without
   it the cells grow and the ruled floor silently becomes a ruled ceiling of
   whatever is left over. */
.rd-kpad {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
  align-content: start;
}

/* ---------------------------------------------------------------------------
   2. A key.
   --------------------------------------------------------------------------- */
/* 56px is the RULED PHONE TOUCH FLOOR, not a drawn coincidence:
   `47 (Mobile) RO Range Day.html:49` — "56px is the phone touch floor on
   scoring surfaces. 68 was a tablet ruling for a gloved thumb at arm's length;
   the phone number covers the steppers, the keypad and the board rows." It may
   be raised by a later ruling and may never be tidied down toward the 44px WCAG
   minimum: a floor with a reason attached is not the same object as a legal
   minimum.

   The drawn `mono` is NOT a monospace family — rd-train-kit.jsx:72
   ("const mono = ") is Archivo at 800/75% with tabular figures. Tabular is the
   whole point on a pad: eleven cells whose glyph must not shift the cell's
   optical centre between `1` and `8`.

   `position: relative` is for the ring in §3 and nothing else.

   ⚠️ The `system-ui, sans-serif` tail is load-bearing on ONE key. CSS falls back
   per CHARACTER, so if Archivo carries no U+232B the ⌫ resolves from a system
   face instead of drawing tofu. Document 11 recorded the tofu; document 47 draws
   the glyph. Whether the fallback lands is an eyeball question and that is why
   the control is mounted on `/kit` — see the component's note. */
.rd-kpad__key {
  position: relative;
  appearance: none;
  -webkit-appearance: none;
  border: none;
  height: 56px;
  padding: 0;
  cursor: pointer;
  background: var(--rd-surf2);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 9px), calc(100% - 9px) 100%, 0 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 18px;
  color: var(--rd-fg);
}

/* THE BLANK SLOT IS A POSITION, NOT AN ELEMENT. The drawing renders the empty
   string as `<div key={i} />` (rd-ro-day.jsx:290, "k === '' ?") whose only job
   is to push `0` into the middle column. Starting the zero key at column 2 is
   the same rendered grid with one fewer node for a screen reader to walk past —
   and `⌫` then falls into column 3 on its own. */
.rd-kpad__key--zero {
  grid-column-start: 2;
}

/* ---------------------------------------------------------------------------
   3. Focus — the inset ring, because a clip-path eats an outline.
   --------------------------------------------------------------------------- */
/* The house `outline: 2px solid var(--rd-acc)` is NOT available on this shape,
   and that is mechanical rather than a preference: `clip-path` clips both
   `outline` and `box-shadow`, and `border-radius` is banned system-wide, so
   neither can draw a ring that follows the 9px cut. The kit's adjudicated
   answer for a single-layer clipped control is the inset ring — "a second
   clip-path'ed element inside the chip at chamfer n, drawn only on focus"
   (quoted in kit-tab-chip.css §4); the construction below is that answer at 9.

   Geometry by the substrate's cut rule n = r - w(2 - SQRT2), which lives in
   exactly one tested function (`chamfer_box::inner_chamfer_px`) and is checked
   against these two literals by
   `the_focus_ring_geometry_is_the_house_formula_at_nine`:
     key cut          9.00
     ring outer cut   9.00 - 2.0(2-SQRT2) = 7.83   (inset 2px inside the key)
     ring hole cut    7.83 - 1.5(2-SQRT2) = 6.95   (the ring is 1.5px)

   The hole takes `--rd-surf2`, the key's OWN fill, so the ring reads as a
   stroke. That is only sound because every key has a ground in every state —
   there is no hollow variant of this cell to leave the hole painting a fill
   over something that has none.

   `--rd-acc`, not `--rd-volt`: focus in this system is an acc STROKE
   (substrate §1.1).

   `:focus-visible` only, never `:focus` — a plain `:focus` ring on a key tapped
   with a thumb leaves a stuck outline on a touch screen. The UA outline is
   removed only inside this rule and only because the rule replaces it. */
.rd-kpad__key:focus-visible {
  outline: none;
}

/* Present but invisible at rest, so focus costs no layout and no repaint of the
   key itself — only this node's opacity changes. */
.rd-kpad__ring {
  position: absolute;
  inset: 2px;
  background: var(--rd-acc);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 7.83px), calc(100% - 7.83px) 100%, 0 100%);
  opacity: 0;
  pointer-events: none;
}

.rd-kpad__ring::after {
  content: "";
  position: absolute;
  inset: 1.5px;
  background: var(--rd-surf2);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 6.95px), calc(100% - 6.95px) 100%, 0 100%);
}

.rd-kpad__key:focus-visible .rd-kpad__ring {
  opacity: 1;
}
/* ==== kit-pro-table · ProTable / ProRow, the desktop Pro console table · owner: 1-track3 ==== */

/* Spec: docs/design/kit/15-pro-table.md. Component:
   apps/web/src/components/kit/pro_table.rs, which carries the API, the four
   open questions and the reason the column list is a prop on both bands. Only
   the values live here.

   Nine drawn desktop tables across six files — rd-pro-library.jsx,
   rd-pro-locations.jsx, rd-pro-progress.jsx, rd-pro-roster.jsx,
   rd-pro-events.jsx, rd-platform.jsx.

   ANATOMY (spec §2):

     [ .rd-ptable                   flex column, no width, no padding    ]
       [ .rd-ptable__head           optional; flex, gap 18, rule under   ]
         [ .rd-ptable__hcell ] x n  one per ProColumn, in order
       [ .rd-prow ] x records
         [ .rd-prow__lead ]         OPTIONAL leading slot (spec §6.2)
         [ .rd-prow__cell ] x n     one per ProColumn, in order
       [ .rd-ptable__foot ]         OPTIONAL paragraph slot (spec §9)

   ⚠️ IT IS NOT A `<table>`, AND THAT IS A RULING RATHER THAN A CONVENIENCE.
   Arrived at twice independently — "NOT a <table> — the same flex-row idiom
   every other Pro list uses (.rd-adm-lib__head, ADM-LIB)"
   (apps/web/styles/adm-loc.css:56-58), and in prose at
   docs/design/kit/ADM1-events.md:820-821. Nine drawn tables, zero table
   elements. The primitive pays the accessibility cost the medium creates by
   emitting ARIA grid roles on the same divs; the rendered pixels are the
   drawing's and nothing here changes for them.

   ⚠️ WIDTH AND ALIGNMENT ARE NOT IN THIS FILE, ON PURPOSE. They are inline
   geometry computed once by `pro_table::column_style` and applied to BOTH
   bands, which is this file's half of the ruling at adm-lib.css:110-120 —
   "⚠️ THE HEAD REUSES THE ROW'S COLUMN CLASSES, AND THEN HAS TO UNDO THEIR
   TYPE. Sharing `__c-name` / `__c-who` / `__c-vis` / `__c-act` between the two
   is what keeps the header and its column aligned — two independent width
   declarations are how a header slides off its column three edits from now."
   The shipped part paid for the sharing with a `font: inherit` undo, because
   the width and the type rode the same class. Here they do not: the width is
   the column's, the type is the cell's own role class, so there is one
   declaration of the width and no undo rule and no specificity argument to
   lose. The designer's conclusion carries over; his mechanism does not.

   ⚠️ NO GROUND AND NO BORDER ON THE TABLE ITSELF. The drawn table sits directly
   on the frame's ground with hairlines for structure; a `--rd-surf` fill or a
   box border would draw a panel edge that nine drawings do not have.

   ⚠️ NO EMPTY COPY, NO LOADING SHIMMER, NO SORT AFFORDANCE, NO PAGER, NO TAB
   STRIP. Each is another object's job or an undrawn state — spec §7 and §12.
   `RdSkeletonTable` (L1-loading.md) is the loading state and `RdEmptyState`
   REPLACES the table rather than sitting inside it
   (rd-pro-progress.jsx:160-162, rd-pro-locations.jsx:118).

   NO @media. Every drawn site is the 1280-wide console; the phone twin is a
   different primitive with a different anatomy (16-row-taxonomy.md). */

/* ---------------------------------------------------------------------------
   1. The table — a flex column with no opinion about its own width.
   --------------------------------------------------------------------------- */
/* No width, no padding, no ground. Every drawn table takes its padding from the
   pane that mounts it — `padding: '18px 32px 32px'` at rd-pro-library.jsx:159
   and rd-pro-locations.jsx:122 is the PANE's, not the table's. A padding here
   would be a second number able to disagree with the frame. */
.rd-ptable {
  display: flex;
  flex-direction: column;
}

/* ---------------------------------------------------------------------------
   2. The head strip — identical to the literal in three drawings.
   --------------------------------------------------------------------------- */
/* ⚠️ THE GAP IS A TOKEN BECAUSE A SECOND HEADER READS IT — ruled 27 Aug,
   C-43.2 ("one header component so nothing shifts when data lands",
   `uploads/design-reply-2026-08-27.md`). `RdSkeletonTable`'s head band is the
   SAME header a moment earlier, and the drawing now says so in its own comment:
   "the header row is REAL and it is rd-pro-library's header verbatim … 9pt,
   .11em, 18px gaps" (`rd-loading.jsx:254-256`). A skeleton that re-typed 18
   would be a second copy of the number, free to disagree the first time this
   one moves — which is the whole failure C-43.2 names. The TYPE needs no token:
   the skeleton's head cells wear `.rd-ptable__hcell` itself. */
:root {
  --rd-ptable-head-gap: 18px;
}

/* `display: 'flex', gap: 18, padding: '0 0 10px', borderBottom: 1px t.line` is
   byte-identical at rd-pro-library.jsx:160, rd-pro-progress.jsx:175 and
   rd-pro-events.jsx:631. The gap lives here rather than under a density
   modifier because all three drawn heads are 18 and the one table that moves
   the gap (the events list, §5) has no head at all — see §5.

   `align-items: center` is the default because two of the three strips declare
   nothing (which computes to `stretch`) and the third, .rd-prog-head, ships
   `center` (adm-prog.css:88). Locations draws `baseline` and gets a variant
   below, not a correction. */
.rd-ptable__head {
  display: flex;
  align-items: center;
  gap: var(--rd-ptable-head-gap);
  padding: 0 0 10px;
  border-bottom: 1px solid var(--rd-line);
}

/* 🟡 A VARIANT, NOT A CORRECTION — spec §4.1, routed as Q2.
   `alignItems: 'baseline'` is the ONE property rd-pro-locations.jsx:123 adds to
   an otherwise byte-identical strip, and Locations ships it today by asking for
   it: `head: Some(HeadAlign::Baseline)` at locations.rs:1013, over the columns
   at locations.rs:1134 (`fn columns`). It used to be a hand-authored
   declaration, `.rd-loc__head` at pre-migration adm-loc.css:61; slice 10
   deleted that rule and the page now takes the variant from here.
   With every head cell at the same 9px the two render nearly
   identically, which is exactly the condition under which a silent collapse is
   invisible in review and wrong the first time a head cell carries a second
   line. Do not collapse it and do not "fix" locations to match. */
.rd-ptable__head--baseline {
  align-items: baseline;
}

/* The head cell's type is ITS OWN and it inherits nothing from a row cell.
   `pdisp(700, 78, '.11em')`, `fontSize: 9`, `color: t.fg3` at
   rd-pro-library.jsx:161, and `textTransform: 'uppercase'` arrives from pdisp
   itself (rd-pro-kit.jsx:102) — which is what the three shipped parts already
   author out longhand at adm-lib.css:104, adm-loc.css:66 and adm-prog.css:94. */
.rd-ptable__hcell {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .11em;
  font-size: 9px;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   3. The row.
   --------------------------------------------------------------------------- */
/* THE DESIGNER'S OWN FACTORING, and the primitive's charter:

     const prow = { display: 'flex', alignItems: 'center', gap: 18, padding: '13px 0' };

   (rd-platform.jsx:76). It is the one place in the corpus where the row shape
   is NAMED rather than re-typed. Note what it does not carry: no borderBottom.
   Platform draws the hairline separately so `:last-child` can drop it — §6.

   `color: inherit` and `text-decoration: none` keep an <a> row reading as a row
   rather than as four links (the precedent is .rd-loc__row, adm-loc.css:85-86).

   The padding and the gap are NOT here: they are the density's, §5. */
.rd-prow {
  display: flex;
  align-items: center;
  border-bottom: 1px solid var(--rd-line);
  color: inherit;
  text-decoration: none;
}

/* The leading slot — the Members selection box, drawn once at
   rd-pro-roster.jsx:75 as a 16x16 square with `flex: 'none'`. The primitive
   owns the slot's POSITION and the gap before column 0 and nothing else; the
   control inside is the consumer's. When the slot is absent it emits no node,
   or the row's gap would show as a leading indent on the six tables that do not
   use it. */
.rd-prow__lead {
  flex: none;
  display: flex;
  align-items: center;
}

/* ---------------------------------------------------------------------------
   4. Cells — typed by role, never inferred from content.
   --------------------------------------------------------------------------- */
/* Tabular figures on EVERY role, not only the numeric one: `2 of 3` sits in the
   body face at rd-pro-progress.jsx:146 and a column of it that shifts width
   between rows is the defect either way (spec §6.1, substrate §2). */
.rd-prow__cell {
  font-variant-numeric: tabular-nums;
}

/* Name — the row's subject. `...pbody, fontSize: 14, color: t.fg` at
   rd-pro-library.jsx:168; `pbody` is `{ fontFamily: 'Archivo, sans-serif' }`
   and nothing else (rd-pro-kit.jsx:103), so the body role authors no weight and
   no stretch and lands at 400/100.

   🟡 14 IS THE MODE, NOT A RULING — spec §6.1 lists 14 / 14.5 / 13.5 and rules
   none of them. Five drawn sites are 14 (rd-pro-library.jsx:168,
   rd-pro-progress.jsx:143 and :247, rd-pro-events.jsx:552,
   rd-pro-roster.jsx:77), two are 14.5 (rd-pro-locations.jsx:89,
   rd-pro-events.jsx:223) and two are 13.5 (rd-pro-events.jsx:640,
   rd-pro-roster.jsx:147). Routed as Q5 in the component's doc comment; a lane
   must not quietly pick a second.

   The ellipsis triple rides the two roles that carry it in the drawing
   (rd-pro-locations.jsx:89-90). `min-width: 0` — the load-bearing half — is
   inline geometry from the column, not from here. */
.rd-prow__cell--name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 14px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Meta — the quiet supporting fact: an address, a role, a date.
   `fontSize: 12.5, color: t.fg3` at rd-pro-locations.jsx:90. 12.5 is the mode
   (rd-pro-locations.jsx:90 and :91, rd-pro-progress.jsx:144 and :146,
   rd-pro-roster.jsx:148) against 12 at rd-pro-roster.jsx:149 and
   rd-pro-events.jsx:554. Same Q5. */
.rd-prow__cell--meta {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Numeric — the drawn `mono` role, which is NOT a monospace family: substrate
   §2 adjudicated it to Archivo 800 / stretch 75% / tabular. `...mono,
   fontSize: 13` at rd-pro-progress.jsx:145 and rd-pro-events.jsx:642.

   ⚠️ A NUMBER DOES NOT BECOME `mono` BY BEING A NUMBER. This role is one the
   consumer CHOOSES. The ruling, made against the drawing's own frame:
   "Declared. NOT mono, and the frame's own mono is overruled: `37` rules this
   cell into the body face because it is 'a sentence with two numbers in it',
   `2 of 3`" (apps/web/styles/adm-prom.css:115-119). An auto-mono heuristic
   would silently overrule a designer ruling on the one cell where he wrote down
   that he had made it. */
.rd-prow__cell--numeric {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 13px;
  color: var(--rd-fg);
}

/* Label — a tracked-uppercase word used as a VALUE, drawn on the event roster's
   role column: `...pdisp(700, 78, '.1em'), fontSize: 9, color: t.fg3` at
   rd-pro-events.jsx:553. `.1em`, not the head strip's `.11em`; the two are
   different drawn numbers on different objects. */
.rd-prow__cell--label {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9px;
  color: var(--rd-fg3);
}

/* Act — the trailing verb. `...pbody, fontSize: 12, color: t.fg3` at
   rd-pro-events.jsx:646. It is TEXT, not a button: every drawn action cell is a
   word rather than a control. rd-pro-roster.jsx:150 draws the same size at
   `t.fg2`; spec §6.1's role table rules fg3 and that is what ships, with the
   deviation recorded here rather than averaged away. */
.rd-prow__cell--act {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
}

/* Slot — the child decides everything. Drawn as the chip pair at
   rd-pro-locations.jsx:92-94, which sets `display: flex, gap: 6,
   justifyContent: 'flex-end'` on the cell and lets ProChip carry its own type.
   No face, no size, no colour: a declaration here would reach into a component
   the cell does not own.

   ⚠️ THE THIRD PROPERTY OF THAT LITERAL IS NOT MISSING — IT IS THE COLUMN'S.
   `justifyContent: 'flex-end'` is emitted by `pro_table::column_style` for a
   Slot cell whose column is `CellAlign::Right`, not declared here, because
   ALIGNMENT IS NOT IN THIS FILE (see the banner). It was omitted outright for
   one batch and the omission was invisible: the column already emitted
   `text-align: right`, which right-aligns TEXT and does nothing whatever to the
   children of a flex box, so a right-edge chip pair read as correctly declared
   and laid itself out from the left edge.

   Both drawn multi-child right-edge cells justify to the end —
   rd-pro-locations.jsx:92 (gap 6) and rd-pro-roster.jsx:150 (gap 14) — and no
   drawing in the corpus draws a flex-start one, so it could have been
   unconditional here. It follows CellAlign instead so there is one declaration
   of a cell's alignment and not two able to disagree. */
.rd-prow__cell--slot {
  display: flex;
  align-items: center;
  gap: 6px;
}

/* ---------------------------------------------------------------------------
   5. Density — three drawn values, and none of them collapse.
   --------------------------------------------------------------------------- */
/* Three drawn row geometries, pinned by `ruling_` tests because a failure there
   is a designer question and not a number to bump:

     13px 0 / gap 18   six tables   rd-pro-locations.jsx:88, rd-pro-roster.jsx:74
                                    and :146, rd-pro-progress.jsx:246,
                                    rd-pro-events.jsx:639, rd-platform.jsx:76
     14px 0 / gap 18   three        rd-pro-library.jsx:167, rd-pro-progress.jsx:142,
                                    rd-pro-events.jsx:551
     16px 0 / gap 20   one          rd-pro-events.jsx:221

   🟡 The obvious tidy is a named scale, and possibly a collapse of 13 and 14
   since 1px of vertical padding is not a difference anyone asked for. The
   designer answered the general form of that proposal: "treat the named scale
   as a hypothesis — some of the 300-400 spread is deliberate per-surface
   tuning; check each against its drawing's notes before collapsing"
   (~/trex-arms/uploads/design-reply-uniformity-2026-08-21.md:44-45, about pane
   widths). Nine sites read, no note found attaching a reason to 13 vs 14, so
   the spread is UNDETERMINED rather than confirmed-deliberate — which is a
   reason to ship all three and ask. Routed as Q1.

   The third value carries its gap in its name because the events list is the
   only table in the corpus that moves both numbers, and a density whose third
   arm silently also changes the gap is a trap for the next reader.

   Density is declared ONCE, on the table, and reaches the rows by descent —
   so a row cannot disagree with its own table. */
.rd-ptable--dens-13 .rd-prow {
  gap: 18px;
  padding: 13px 0;
}

.rd-ptable--dens-14 .rd-prow {
  gap: 18px;
  padding: 14px 0;
}

.rd-ptable--dens-16-gap-20 .rd-prow {
  gap: 20px;
  padding: 16px 0;
}

/* ---------------------------------------------------------------------------
   6. The last hairline — it stands unless asked to go.
   --------------------------------------------------------------------------- */
/* 🟡 THIS MODIFIER HAS NO CONSUMER, AND THAT IS A ROUTED QUESTION RATHER THAN
   DEAD CODE. Read this before deleting it.

   The flag was built for one table. The reason given was
   `.rd-plat__row:last-child { border-bottom: none; }` — pre-migration
   adm-plat.css:99 — described as "drawn on Platform alone", the one account
   list that ends at the frame edge where a trailing hairline reads as a table
   that got cut off.

   ⚠️ THE PREMISE WAS WRONG AND THE MIGRATION IS WHAT FOUND IT. Slice 12 read
   the drawing instead of the shipped rule, and the drawing hairlines EVERY row:

     <div key={em} style={{ ...prow, borderBottom: `1px solid ${t.line}` }}>

   at rd-platform.jsx:329, inside the `rows.map`, closing with the footer
   paragraph at :337. And in the shipped markup that footer paragraph was the
   last child of `.rd-plat__rows`, so `.rd-plat__row:last-child` matched
   NOTHING — the rule was inert for its whole life and Platform's last hairline
   has always painted. adm-plat.css:90-107 is where the finding is recorded.

   So slice 12 did not pass the flag: passing it would have been the first time
   that screen ever lost the hairline, against the drawing. Nine of nine drawn
   tables keep their last rule, not eight of nine.

   ⚠️ WHY THE MODIFIER STAYS ANYWAY. Deleting it would silently resolve a
   question nobody has answered — whether the designer meant Platform to lose
   that hairline and the drawing simply never said so, or whether the shipped
   `:last-child` rule was a transcription error from the start. The rule below
   is correct for whichever answer comes back; only its consumer list is empty.
   The `check-css-dead.py` sweep reports it as reachable because the component
   still emits the class behind a `false` default. Routed as spec §8.2 — do not
   delete, do not "wire it up to Platform to give it a user".

   `:last-child` rather than `:last-of-type`: a row is a <div> or an <a>
   depending on whether it navigates, and `:last-of-type` would silently mean a
   different row in a table that mixes them. The flag is only meaningful on a
   table whose last child IS a row — a foot slot under the flag would take the
   hairline off nothing. That fact is also the mechanism behind the finding
   above. */
.rd-ptable--last-bare .rd-prow:last-child {
  border-bottom: none;
}

/* ---------------------------------------------------------------------------
   7. States — hover and focus ride together, and only on rows that navigate.
   --------------------------------------------------------------------------- */
/* "A row is a LINK. The affordances are the history list's, which is the house
   pattern for a row that goes somewhere: a surf2 hover ground and an inset
   focus ring, so the outline is not clipped by the row above."
   (pre-migration apps/web/styles/adm-prog.css:107-109. ⚠️ THE SOURCE RULE IS
   GONE: slice 11 deleted `.rd-prog-row` with its hover ground and inset ring —
   adm-prog.css:110-115 is the epitaph that says so — because this primitive is
   what carries them now. The sentence is quoted here rather than cited live,
   and the two pages that inherited the ruling restate it at
   locations.rs:1200-1206 and events.rs:602-607.)

   ⚠️ `outline-offset: -2px`, NOT the house `+2px`. Rows abut with no gap, so a
   positive offset draws the ring over the neighbouring row's content and over
   the hairline between them. This is mechanical, not a preference.

   ⚠️ FOUR OF THE NINE TABLES HAVE NO HOVER AND THAT IS CORRECT. Library,
   Members, Invited, standard-detail and Platform rows do not navigate; a hover
   ground under a row nothing happens to is a promise the row does not keep. So
   both affordances hang off one modifier and cannot be had separately.

   🟡 WHICH GROUND — Q3, STILL OPEN. Two shipped parts, same affordance, two
   answers: `.rd-loc__row:hover` used `--rd-surf` (pre-migration adm-loc.css:88),
   `.rd-prog-row:hover` used `--rd-surf2` (pre-migration adm-prog.css:119) and
   called surf2 "the house pattern for a row that goes somewhere". A primitive
   cannot ship both. Interim `--rd-surf2`, on the strength of that file naming
   itself as the house pattern — flagged, not decided.

   ⚠️ BOTH SOURCE RULES ARE NOW DELETED (slices 10 and 11) AND THE QUESTION IS
   NOT. Neither line number resolves any more — this rule below is the only
   ground either screen has. The disagreement is preserved where the migrations
   put it: locations.rs:1200-1206 flags the surf → surf2 change it took, and
   events.rs:602-607 flags the same change for the events list. Do not read the
   absence of the two old rules as the question having been answered. */
.rd-prow--link:hover {
  background: var(--rd-surf2);
}

.rd-prow--link:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* ---------------------------------------------------------------------------
   8. Voided and Archived — both mean "still here, deliberately quiet".
   --------------------------------------------------------------------------- */
/* "A voided row keeps its place: shooter name goes --rd-fg3 with line-through,
   points and time drop to --rd-fg3, and the action cell reads Voided"
   (docs/design/kit/ADM1-events.md:835-838), and the drawing's own foot sentence
   is why it keeps its place: "A voided score keeps its row and leaves every
   final ranking, which is why voiding asks once. Export is where results end —
   there is no public scoreboard." (rd-pro-events.jsx:657)

   ⚠️ THE STRIKE IS ON THE NAME CELL ONLY. The other cells drop to --rd-fg3 and
   keep their faces (rd-pro-events.jsx:640-643). A blanket text-decoration on
   .rd-prow would strike the numbers too — and `text-decoration` inherits, which
   is exactly why the blanket rule below sets colour and nothing else.

   This also fixes the second half of #467: `line-through` is emitted at
   scores_tab.rs today against no declaration anywhere in our stylesheet, so
   voided scores are not struck on the one row where a strike carries meaning. */
.rd-prow--voided .rd-prow__cell {
  color: var(--rd-fg3);
}

.rd-prow--voided .rd-prow__cell--name {
  text-decoration: line-through;
}

/* ⚠️ THE TONE IS CONTESTED AND THE DRAWING WINS. ADM1-events.md:838 says
   --rd-warn. The drawing says danger and says why, in a margin comment sitting
   directly above the cell: "`danger`, not `warn` - the flag tone. A voided
   score is the one value in this table that needed somebody to act."
   (rd-pro-events.jsx:644-645, the cell at :642 rendering
   `color: void_ ? t.danger : t.fg3`). Under the contract's "when a drawing and
   an answer disagree, the drawing wins" this ships danger and routes the
   conflict as Q4 — his own spec file has drifted from his drawing, and that is
   the finding. It sits after the blanket rule so it wins on source order within
   this file; both selectors are (0,2,0). */
.rd-prow--voided .rd-prow__cell--act {
  color: var(--rd-danger);
}

/* "An archived row goes quiet, and only its NAME does: `color: dim ? t.fg2 :
   t.fg` on the name cell alone (LocRow, v0.2). The address, the count and the
   chips keep their own weights — the row is still a record somebody may need to
   correct, which is the argument for listing it at all."
   (apps/web/styles/adm-loc.css:91-94, the rule at :95)

   A strictly weaker dim than Voided, with a stated reason: fg2 not fg3, one
   cell not all of them, and no strike. The Archived CHIP is a Slot cell the
   consumer passes; the state does not draw one. */
.rd-prow--archived .rd-prow__cell--name {
  color: var(--rd-fg2);
}

/* ---------------------------------------------------------------------------
   9. Virtualization — opt-in, and the size is the table's own measurement.
   --------------------------------------------------------------------------- */
/* "An org's library lists every drill and course unpaginated, so off-screen
   rows skip layout and paint. 50px = 14+14 padding + the tallest cell, the
   .rd-prochip at 9px + 4+4 padding + its 1px border (~21px) + the row's 1px
   hairline; `auto` keeps the measured height."
   (apps/web/styles/adm-lib.css:129-132, the declarations at :133-134)

   Three things that comment settles, and each is a reason the flag is opt-in:

   1. The trigger is "unpaginated and unbounded", not "is a table". Members,
      Locations and Platform are bounded lists; turning this on everywhere buys
      nothing and costs a scrollbar that resizes as you scroll.
   2. The intrinsic size is MEASURED, per table — 50px is the library's tallest
      cell computed term by term, not a constant to copy (the history list
      arrived at 61px for its own row). So the size arrives as the consumer's
      own number through `--rd-ptable-intrinsic`, and a consumer that cannot
      state its number does not get the feature.
   3. ⚠️ `auto` in `contain-intrinsic-size: auto <px>` IS LOAD-BEARING. The
      keyword makes the browser remember the real height once a row has been
      rendered once, so the estimate only ever governs rows never yet seen.
      Dropping it leaves every row locked at the estimate and the scroll
      position drifting. */
.rd-ptable--virt .rd-prow {
  content-visibility: auto;
  contain-intrinsic-size: auto var(--rd-ptable-intrinsic);
}

/* ---------------------------------------------------------------------------
   10. The foot — a paragraph, not a caption.
   --------------------------------------------------------------------------- */
/* Six of the nine tables close with a paragraph in the same shape:
   `fontSize: 11.5, color: t.fg3, marginTop: 18, lineHeight: 1.55` and a
   maxWidth in ch — rd-pro-events.jsx:657 and :554 at 66ch,
   rd-pro-locations.jsx:130 at 76ch, rd-pro-roster.jsx:153 at 64ch.
   (⚠️ Spec §9 gives the locations foot as :129; on this tree :129 is the
   `LOCS.map` line and the paragraph is :130. Recorded, not repointed silently.)

   It is a SLOT, not a copy prop: this file owns the distance, the size, the
   tone and the fact that there is a measure at all; the consumer passes the
   sentence and its own ch width, which is why `max-width` is inline and not
   here. The phone twin records both halves of the rule: "Carries DECLARED_NOTE
   verbatim, so it is a paragraph and not a caption: measured at 70ch like its
   desktop twin, and floored to 11px." (apps/web/styles/adm-prom.css:146-148,
   the rule at :155). 11.5px on desktop clears the 11px floor. */
.rd-ptable__foot {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--rd-fg3);
  margin-top: 18px;
}
/* ==== kit-row · RdRow, the phone row family · owner: 1-track3 ==== */

/* Spec: docs/design/kit/16-row-taxonomy.md. Component:
   apps/web/src/components/kit/row.rs, which carries the API, the four intents,
   the two grounds and the open hover question. Only the values live here.

   Source: `rd-pro-mobile-pages.jsx:9-11` (the designer's own `mrow`/`msub`/
   `mname` constants, spent across Members, Org Library, Locations and Shooter
   Progress) plus the four inline restatements in `rd-pro-event-detail.jsx`
   (hub `:103`, roster `:133`, score `:170`, squad `:211`). Eight drawn rows,
   one shape.

   ANATOMY:

     [ .rd-row  — flex, center, gap 12, chamfer 10, surf ground ]
       [ .rd-row__lead   — optional; flex: none (checkbox, avatar) ]
       [ .rd-row__main   — flex: 1; min-width: 0 ]
           [ .rd-row__name — 13px / 600 / --rd-fg ]
           [ .rd-row__sub  — 11px / --rd-fg3 / margin-top 3 ]
             [ --warn      — the ONE drawn override: --rd-warn, ADDED ]
       [ .rd-row__trail  — flex: none; one RowTrailing ]
       [ .rd-row__chev   — optional; flex: none; the 7x12 chevron ]

     [ .rd-row-notice ]  — a SIBLING of the list, never a child of a row

   ⚠️ LEFT IS IDENTITY, RIGHT IS THE VERDICT, AND THE LEFT CELL IS THE ONE THAT
   GIVES WAY. `46 (Mobile) Pro Destinations.html:47` (quoted verbatim 2026-08-21
   from the e24 snapshot): "Tables become row stacks, and the row keeps the
   table's columns. Name and the second fact on the left, the scannable verdict
   on the right edge — the same reading order as the desktop columns, without a
   horizontal scroll." `.rd-row__main`'s `min-width: 0` is the entire mechanism
   for that last clause; a row that scrolls sideways has failed the rule rather
   than styled it differently.

   ⚠️ NO HOVER TREATMENT, AND THE QUESTION IS OPEN RATHER THAN ANSWERED HERE.
   Spec §10 routes it to the designer: `.rd-prom-row` (the four-destination row)
   has none, `.rd-progm-row` (the Progress row, same batch) has one, and no
   phone frame draws either — a static frame cannot. Until the answer lands this
   part ships none, matching the four-destination majority and the keypad's own
   ruling for a touch surface (`docs/design/kit/14-keypad.md:97-99`): "No hover
   treatment, and the absence is drawn. This is a phone surface for a thumb; the
   drawing gives the cells no hover state and inventing one puts a sticky
   highlight under the last-tapped key on a touch screen."
   If the answer is "keep it", the shape is `@media (hover: hover)` and never a
   bare `:hover` — a mouse-driven admin at a desk and a thumb on a phone are
   both real users of these pages.

   ⚠️ THIS PART LANDED AS AN ADDITION AND THE SWEEP HAS SINCE HAPPENED. It
   replaced nothing when it shipped — `adm-mob.css` kept every `.rd-prom-row*`
   rule it had until the strand sweep that follows all six migrations (spec §12,
   batch 15 slice 13), the same shape `order.txt` records for `.rd-hf-tab__pad`.
   That sweep has now run, and `.rd-prom-row`, `__main`, `__name`,
   `__name--past` and `__sub` are DELETED (`adm-mob.css` §2 is their epitaph).

   ⚠️ WHAT "pre-migration" MEANS IN EVERY CITATION BELOW. A citation written
   `pre-migration adm-mob.css:NN` or `pre-migration adm-prom.css:NN` names a
   line that NO LONGER RESOLVES: the rule it pointed at was deleted by a batch-15
   migration, and the quote beside it is now the only copy of that sentence. The
   line number is kept rather than dropped because it is how a reader follows the
   trail back through the history; it is marked rather than silently corrected
   because a citation that resolves to whatever moved into its slot is worse than
   one that admits it is historical. Citations WITHOUT the marker resolve today
   and were recomputed by grepping the literal, never by adding an offset.

   NO @media. Both grounds are renderings the component already chooses between
   in Rust. */

/* ---------------------------------------------------------------------------
   1. The row — the card ground, which is the base.
   --------------------------------------------------------------------------- */
/* `background: t.surf`, `...tcham(10)`, `padding: '12px 14px'`,
   `display: 'flex'`, `alignItems: 'center'`, `gap: 12` — the whole of `mrow`
   (`rd-pro-mobile-pages.jsx:9`).

   `tcham(n)` is the corpus helper at `rd-train-kit.jsx`: the bottom-right
   chamfer, transcribed here as the same `clip-path` polygon `kit-tab-chip.css`
   already ships and pre-migration `adm-mob.css:81` already transcribed.

   ⚠️ THE CHAMFER IS NOT A STYLE OPTION AND THERE IS NO PROP TO TURN IT OFF.
   `48 Kit Rules.html:33`, quoted verbatim 2026-08-21 from the e24 snapshot:
   "The kit is chamfered; radius is not a style option… The complete list of
   what may be round: circular avatars where a photo will land, marks — the
   sweep, skeletons, status dots, and step marks…, OS-owned controls…, device
   frames, and map pins. Nothing else … A new rounded control is a defect, not a
   variant." A row is on none of those five lists, and a SQUARE one is a defect
   the same way — `.rd-progm-row` lost this polygon and nobody saw it.

   `text-decoration: none; color: inherit` because the navigating form of this
   row IS the anchor: "It is a `Link`, so the UA's underline and inherited
   colour are reset the way `.rd-adm-ev__row` resets them one part family over."
   (pre-migration `apps/web/styles/adm-mob.css:73-74`). */
.rd-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  background: var(--rd-surf);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%);
  text-decoration: none;
  color: inherit;
}

/* ---------------------------------------------------------------------------
   2. Focus on the card ground — an inset ring, because a clip-path eats an
      outline.
   --------------------------------------------------------------------------- */
/* ⚠️ AN INSET RING, NOT THE HOUSE `outline`, AND IT IS MECHANICAL RATHER THAN A
   PREFERENCE: `clip-path` clips an `outline` away, so the desktop row's
   `outline: 2px solid var(--rd-acc); outline-offset: -2px` would draw nothing
   here. `kit-tab-chip.css` §4 states the fact and ships the same substitution.
   (pre-migration `apps/web/styles/adm-mob.css:86-89`, over the rule at `:90-93`.)

   This is the trap `.rd-progm-row` walked into: a chamfered row with
   `outline: 2px solid` looks FINE in review — the outline is drawn and then
   clipped, so what a reviewer sees is a row with a missing ring on the cut
   corner, or none at all.

   `:focus-visible` only, never `:focus` — the same call `14-keypad.md` §3.1
   makes, and for the same reason: a plain `:focus` ring leaves a stuck outline
   under a thumb. The UA outline is removed only inside this rule and only
   because the rule replaces it.

   `--rd-acc`, not `--rd-volt`: focus in this system is an acc STROKE
   (substrate §1.1). */
.rd-row:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--rd-acc);
}

/* ---------------------------------------------------------------------------
   3. The ruled ground — two of the five drawn rows are ruled, not carded.
   --------------------------------------------------------------------------- */
/* The hub, the destinations and the squads are LISTS OF PLACES TO GO and take
   the `surf` card ground; the roster and the scores are DENSE TABLES FOLDED and
   take a `1px solid t.line` bottom rule on the shell ground instead
   (`rd-pro-event-detail.jsx:133`, `:170`). Nothing collapses the two.

   A ruled row takes NO CHAMFER — there is no ground to cut — and that is not
   the same object as a switch: the card ground's chamfer is still not optional.
   And with no `clip-path` there is nothing to clip an outline, so this ground
   takes the house focus treatment rather than §2's substitution. The two lines
   below are one decision.

   ⚠️ **THE SIDE INSET GOES TOO, AND THE GAP IS 10 — the ruled ground is not a
   carded row with its ground removed.** The two drawn ruled rows carry NO
   `padding` key at all and a tighter gap; verbatim from the e13 snapshot:

     `rd-pro-event-detail.jsx:133`  (roster)
       `{ display: 'flex', alignItems: 'center', gap: 10, minHeight: 54,
          borderBottom: `1px solid ${t.line}` }`
     `rd-pro-event-detail.jsx:170`  (scores)
       `{ display: 'flex', alignItems: 'center', gap: 10, minHeight: 56,
          borderBottom: `1px solid ${t.line}` }`

   Compare the squad at `:211`, which DOES spell `padding: '12px 14px'` and
   `gap: 12` — the drawing states the carded inset where it wants it, so its
   absence at `:133`/`:170` is a value and not an omission. Shipping §1's
   `padding: 12px 14px` here inset the roster/score name a further 14px from the
   screen gutter and stopped the hairline 14px short of the right edge; the
   drawn rule runs edge to edge under a name that sits ON the gutter. Found by
   the round-7 representation audit of `49 (Mobile) Pro Event Detail.html`
   (`tmp/audit-r7/49-pro-event-detail.md` D1).

   THE VERTICAL 12px IS KEPT, DELIBERATELY, AND IT IS THE ONE VALUE HERE THAT IS
   OURS. The drawn rows set `min-height` only (54 / 56) and the fixture names are
   one line, so at the drawn heights the vertical padding is invisible — it is
   only reachable when a real org's name wraps, and there the choice is between
   a row that grows past 54/56 with breathing room and one whose second line
   touches the hairline. Touch comfort wins; the drawing does not rule against
   it because the drawing never draws the case. If the designer wants the row
   pinned to the drawn floor instead, the change is `padding: 0` here and
   nothing else. Only the horizontal half is a transcription.

   The two carded intents keep §1's base untouched — this is a reset on the
   modifier, never an edit to the base every carded row reads. */
.rd-row--ruled {
  background: none;
  clip-path: none;
  border-bottom: 1px solid var(--rd-line);
  padding-left: 0;
  padding-right: 0;
  gap: 10px;
}

.rd-row--ruled:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
  box-shadow: none;
}

/* ---------------------------------------------------------------------------
   4. Drawn heights, per intent. Each is a FLOOR.
   --------------------------------------------------------------------------- */
/* Destination (list) draws no height at all — the padding sets it
   (`rd-pro-mobile-pages.jsx:9`). The other four are drawn:
     hub     min-height 52 + padding '10px 14px'  (rd-pro-event-detail.jsx:103)
     person  min-height 54                        (:133)
     score   min-height 56                        (:170)
     squad   min-height 56 + padding '12px 14px'  (:211)
   The squad's padding is the base's, restated inline by the drawing; only the
   hub's differs, so only the hub restates it here. */
.rd-row--hub {
  min-height: 52px;
  padding: 10px 14px;
}

.rd-row--person {
  min-height: 54px;
}

.rd-row--score {
  min-height: 56px;
}

.rd-row--squad {
  min-height: 56px;
}

/* ---------------------------------------------------------------------------
   5. The cells.
   --------------------------------------------------------------------------- */
/* THE LEADING SLOT IS NOT DRAWN ON THE PHONE AND IT SHIPS ANYWAY — `flex: none`
   and NOTHING ELSE, so that what a leading control looks like stays the
   consumer's decision and this part never decides it. The built Members list
   carries a checkbox at the head of its desktop row and the migration needs
   somewhere to put it. An unfilled slot emits no node at all. */
.rd-row__lead {
  flex: none;
}

/* The left cell: `flex: 1, minWidth: 0` — the whole reason the row needs no
   horizontal scroll. */
.rd-row__main {
  flex: 1 1 auto;
  min-width: 0;
}

/* `mname`: `tbody` at 13px, `fontWeight: 600`, `color: t.fg`
   (`rd-pro-mobile-pages.jsx:11`). Mixed-case body copy, so it clears the 11px
   floor with room. The ellipsis is ours: the drawing's fixture names are short
   and a real org's are not. */
/* `text-decoration: none` because the name line has a LINKED form: an acting row
   is a `div` (it holds a button), so where its name is the door — the Library
   phone row's drill, `adm-lib-mob.css:163-167` — the name itself is a `Link`.
   The same reset the row's own navigating form takes at `.rd-row`, for the same
   reason the corpus already gives: "It is a `Link`, so the UA's underline and
   inherited colour are reset the way `.rd-adm-ev__row` resets them one part
   family over" (pre-migration `apps/web/styles/adm-mob.css:73-74`). The colour half is already
   here. It is declared unconditionally rather than on an `a.rd-row__name`
   variant: one name line, one set of values, and a type selector here would be
   a second declaration able to disagree with the first.

   `--struck` still wins its `line-through`: same specificity, later in the
   file. */
.rd-row__name {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--rd-fg);
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ⚠️ ONLY the name steps down for a past event — NOT an opacity on the row.
   32 Pro Events, `ScreenEvents`: an opacity multiplier dims fg2 and fg3 with it,
   and those two greys are calibrated to clear ~5:1 on small copy in sunlight; a
   blanket .62 threw that away and put a 9px chip at 2.9:1. The phone row carries
   a 9.5px chip and an 11px sub-line, so it is the same hazard with less margin.
   Do not "tidy" this into `.rd-prom-row--past { opacity: … }`.
   (pre-migration `apps/web/styles/adm-mob.css:116-121`, over `.rd-prom-row__name--past` at
   `:122-124`, carried here verbatim.) */
.rd-row__name--past {
  color: var(--rd-fg2);
}

/* A VOIDED ROW KEEPS ITS PLACE, STRUCK. `49 (Mobile) Pro Event Detail.html:133`
   (quoted verbatim 2026-08-21 from the e24 snapshot): "A voided row stays,
   struck, with the danger word — the one value in the table that needed
   somebody to act". The name goes `--rd-fg3` and takes the strike
   (`rd-pro-event-detail.jsx:176`); the danger word is the trailing slot's. */
.rd-row__name--struck {
  color: var(--rd-fg3);
  text-decoration: line-through;
}

/* `msub`: `tbody` at 10.5px, `color: t.fg3`, `marginTop: 3`
   (`rd-pro-mobile-pages.jsx:10`).

   ⚠️ 11px, NOT THE DRAWN 10.5 — the mixed-case half of the type floor, applied
   and not transcribed: "`01 Foundations` §05: mixed-case text never goes below
   11px, so the drawn 10.5px sub-line ships at 11."
   (`docs/design/kit/ADM-MOB1-events.md:76-77`.)

   It wraps rather than ellipses: it carries facts joined by middots and
   clipping it would silently drop one — the very column `46`'s rule exists to
   keep.

   ⚠️ THE DRAWING DOES NOT DECIDE THE WRAP, AND THE CORPUS DISAGREES WITH ITSELF
   ABOUT IT. `mrow`/`msub` (`rd-pro-mobile-pages.jsx:9-11`) declare no
   `white-space`, no `overflow` and no `text-overflow`, and neither does the
   Progress frame's own sub-line (`:111`) — a browser rendering the frame wraps.
   The built parts went both ways: `.rd-prom-row__sub` wraps and
   `.rd-progm-row__sub` ellipsizes (pre-migration `apps/web/styles/adm-prom.css:117-119` —
   `overflow: hidden; text-overflow: ellipsis; white-space: nowrap`). The kit
   carries the wrap, on the reason above and on the drawn default; slice 11's
   Progress migration therefore CHANGES that screen from ellipsis to wrap, which
   is a visible change and is recorded rather than slipped in
   (`docs/design/kit/16-row-taxonomy.md` §10). The name line above keeps its
   ellipsis: one name has nothing to lose to a clip, a middot list does. */
.rd-row__sub {
  display: block;
  margin-top: 3px;
  font-size: 11px;
  line-height: 1.35;
  color: var(--rd-fg3);
  overflow-wrap: anywhere;
}

/* ⚠️ THE SECOND SUB TONE IS THE FRAME'S OWN TERNARY, NOT AN ADDITION.
   `rd-pro-mobile-pages.jsx:113` ("color: cold ? t.warn : t.fg3") paints the
   Shooter Progress sub-line `t.warn` when the shooter is cold. It shipped once
   already, under a screen's name: `.rd-progm-row__sub--cold`
   (pre-migration `apps/web/styles/adm-prom.css:124`).

   QUIET IS THE BASE AND WARN IS ADDED — the shape §4.5 already rules for the
   right edge: the quiet side is the fall-through and the modifier is ADDED to a
   base rather than swapped for a second class.

   ⚠️ NAMED FOR THE TONE, NOT THE PREDICATE. The screen's class says `--cold`
   because that screen knows what cold means; the primitive does not and must
   not — the same refusal `.rd-row__tone--accent` makes for the right edge, where
   nothing here maps a word to a tone.

   TWO TONES, NOT THREE, AND THAT IS COUNTED. `msub` is spread five times in the
   corpus — its definition at `rd-pro-mobile-pages.jsx:10` and four uses at
   `:31`, `:62`, `:84` and `:111` — and exactly one overrides the colour. No
   other phone frame paints a sub-line at all. */
.rd-row__sub--warn {
  color: var(--rd-warn);
}

/* The right edge. `flex: none` so the verdict never compresses — it is the
   scannable half of the row and the left cell is the one that gives way.
   (`apps/web/styles/adm-mob.css:205-206`.) */
.rd-row__trail {
  flex: none;
  display: flex;
  align-items: center;
}

/* The 7x12 chevron the kit uses wherever a row opens something
   (`.rd-home__chevron`). Sized here because the mark sets no width or height of
   its own and takes `currentColor`. It is a slot's NEIGHBOUR, not a trailing
   variant: the hub row draws a sub-line AND a chevron
   (`rd-pro-event-detail.jsx:108`) while the destination rows draw a trailing
   word and no chevron, so making the chevron a trailing member would make the
   two mutually exclusive and the hub row unbuildable. */
.rd-row__chev {
  flex: none;
  width: 7px;
  height: 12px;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   6. The trailing slot's four fills.
   --------------------------------------------------------------------------- */
/* ⚠️ THE TRAILING NUMBER IS TYPED, AND THE DRAWN `mono` IS OVERRULED. The built
   Progress row states the ruling: "Declared. NOT mono, and the frame's own mono
   is overruled: `37` rules this cell into the body face because it is 'a
   sentence with two numbers in it', `2 of 3`, and a rule carries to phone where
   a frame carries only layout. So the face, the tabular figures and the tone
   are `.rd-prog-declared`'s; only the size follows the frame."
   (`apps/web/styles/adm-prom.css:115-119`, over `.rd-progm-row__count` at
   `:131-137`.)

   `rd-pro-mobile-pages.jsx:115` spreads `...mono` into this exact cell and is
   overruled. The consumer that genuinely wants the mono register asks for it —
   the score row's time is a MEASUREMENT, not a sentence, and takes the mono
   role through a trailing line below. Nothing here infers a face from content:
   a slot that switched to mono whenever its text parsed as a number would put
   `2 of 3` in the mono face and undo doc 37's ruling silently. */
.rd-row__count {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--rd-fg2);
}

/* The verdict word, on the tracked-uppercase display role. 8.5px in the JSX
   (`rd-pro-event-detail.jsx:140`), shipping at the built 9.5
   (`apps/web/styles/adm-mob.css:228`, `adm-lib-mob.css:154`) — a display mark
   is not mixed-case copy and is NOT floored to 11; that is the display role's
   own question and not this one. */
.rd-row__chip {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  text-transform: uppercase;
  font-size: 9.5px;
  white-space: nowrap;
  color: var(--rd-fg3);
}

/* Body-role text with no verdict reading — the score row's `Voided`. Mixed-case
   copy, so 11 is its floor. */
.rd-row__text {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  white-space: nowrap;
  color: var(--rd-fg3);
}

/* Two lines, right-aligned, 4px apart: `display: grid, gap: 4,
   textAlign: 'right'` (`rd-pro-event-detail.jsx:138`). The person row's
   fact-or-act over `Remove`; the score row's time over points; the void
   ask-once pair. */
.rd-row__stack {
  display: grid;
  gap: 4px;
  text-align: right;
  justify-items: end;
}

/* One line of a stack. The base is the quiet body register, and it carries the
   control reset because an ACT line is a real `button` — a button inside a row
   link is invalid markup, which is why an acting row is a `div` and each act is
   its own button (`rd-pro-event-detail.jsx:64-68`). */
.rd-row__line {
  background: none;
  border: 0;
  padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.2;
  text-align: right;
  white-space: nowrap;
  color: var(--rd-fg3);
}

/* ⚠️ THE FACT AND THE ACT ARE THE SAME COLOUR AND DIFFERENT ROLES, and that is
   the whole of the round-9 ruling: "31's table as rows, plus the scorer
   assignment the build shipped (James's ruling, round 9): the assigned row
   carries the FACT as a quiet accent word; every other row carries the ACT. Two
   acts stack on the right edge — the console's three-control row at phone
   width." (`rd-pro-event-detail.jsx:123-126`.)

   A fact is a DISPLAY role (`Scorer`, tracked-uppercase 8.5 → 9.5) and an act
   is a BODY role (`Make scorer`, 10.5 → the 11px mixed-case floor), and they
   are the same colour so that the row does not shout twice. */
.rd-row__line--fact {
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  text-transform: uppercase;
  font-size: 9.5px;
}

/* ⚠️ THE ACT IS THE ONLY LINE IN THE STACK THAT IS A TARGET, AND IT WAS THE
   HEIGHT OF ITS OWN TEXT — 11px over a 1.2 line box, so ~13px of tappable row
   against a 44 minimum. James, NTOA rehearsal 2026-08-29: `Remove` and
   `Make scorer` "only register a tap on the text". They did; the text was the
   whole control.

   A REAL `min-height`, not the transparent `::after` extension the horizontal
   acts take (see `kit-hit.css` for the pair). This line sits in
   `.rd-row__stack`, a GRID — its neighbour is directly above or below it at a
   4px gap, and two acts stack on the console's three-control row. A pseudo
   grown to 44 there would reach into the sibling act and hand it the tap, which
   is a worse defect than the one being fixed. So the box grows for real.

   The text does not move within its line: `justify-content: flex-end` keeps the
   right edge the grid already gave it and `align-items: center` keeps the
   baseline centred in the taller box. The ROW grows where an act is present —
   accepted, and the point: the drawn row never had room for a target this
   control needs. */
.rd-row__line--act {
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  min-height: 44px;
}

.rd-row__line--act:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* The measurement line — the score row's time, `mono` at 13px `--rd-fg`
   (`rd-pro-event-detail.jsx:187`). The drawn `mono` is NOT a monospace family:
   `rd-train-kit.jsx:72` is Archivo at 800/75% with tabular figures. This is the
   one trailing register that IS mono, and the consumer asks for it by name. */
.rd-row__line--measure {
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  color: var(--rd-fg);
}

/* The second line of the score row's numeric stack — `{pts} pts`, drawn 9.5px
   `t.fg3` (`rd-pro-event-detail.jsx:188`) and FLOORED TO 11 like every other
   body-role trailing text in the row (spec §6.3: "a row's name, sub-line,
   notice foot and any body-role trailing text are all mixed-case copy and all
   floored to 11"). It is a sentence with a number in it, not a display mark, so
   the floor applies and the 9.5 does not carry. Tabular for the same reason the
   count is: the digits must not shift the line's optical centre. */
.rd-row__line--sub {
  font-variant-numeric: tabular-nums;
}

/* ⚠️ QUIET IS THE BASE AND THE ACCENT IS ADDED — never a swapped pair. The
   drawing's own `role === 'Member' ? t.fg3 : t.acc`, written with the quiet side
   as the fall-through so the modifier is ADDED to a base rather than swapped for
   a second one (`apps/web/styles/adm-mob.css:245-249`). Every fill above
   declares its quiet tone; these two add to it, and there is deliberately no
   `--quiet` modifier for anything to swap against.

   The generalised rule, from four surfaces stating it four ways
   (`46 (Mobile) Pro Destinations.html:96`, `:108`, `:119`, `:130`): quiet is the
   base and `--rd-acc` is added for A CLAIM THAT REACHES OUTSIDE THE ROW. Staff,
   Published, Community, fully-declared — each is a statement about the world
   beyond this record. Member, Org only, Unused, a partial count are facts about
   the record itself and stay quiet.

   ⚠️ AND `Owner` NEVER TAKES IT: "ownership is a fact about a person, not a
   state they are currently in, and the drawing paints the mark `t.fg3`. The
   owner's row carries two marks and only the role half can light."
   (`apps/web/styles/adm-mob.css:251-253`.) Nothing in this part or its component
   maps a WORD to a tone — the tone is the consumer's, always — which is the
   mechanism that keeps `Owner` quiet without this file knowing the word.

   Single-class selectors like the bases they modify, so source order decides the
   tie and these come last on purpose. */
.rd-row__tone--accent {
  color: var(--rd-acc);
}

.rd-row__tone--danger {
  color: var(--rd-danger);
}

/* ---------------------------------------------------------------------------
   7. The notice foot — a SIBLING of the list, never a child of a row.
   --------------------------------------------------------------------------- */
/* Every one of the eight drawn screens ends the same way: a paragraph under the
   list, stating the rule the list cannot state itself
   (`rd-pro-mobile-pages.jsx:38`, `:90`, `:118`; `rd-pro-event-detail.jsx:147`,
   `:191`, `:220`).

   IT IS A PARAGRAPH AND NOT A CAPTION, and the built precedent ruled the
   difference: "Carries `DECLARED_NOTE` verbatim, so it is a paragraph and not a
   caption: measured at 70ch like its desktop twin, and floored to 11px. The
   frame's own `marginTop: 10` sets the distance."
   (`apps/web/styles/adm-prom.css:146-148`, over `.rd-progm__foot` at
   `:150-157`.)

   The four longer feet draw `lineHeight: 1.5`; the built precedent's 1.55 is
   kept as the one value, because two line-heights three hundredths apart is
   drift and not a design. `text-wrap: pretty` ships on all of them — it is a
   rendering hint, not a look.

   A SIBLING, for two structural reasons: a notice about the WHOLE LIST inside
   the last row's DOM is a lie to a screen reader, and a notice must survive an
   empty list — the Locations foot (`{n} archived`) is precisely the sentence
   that matters most when the live list is empty. */
.rd-row-notice {
  margin: 10px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.55;
  color: var(--rd-fg3);
  max-width: 70ch;
  text-wrap: pretty;
}
/* ==== kit-pro-filter-bar · ProFilterBar, the desktop console's filter strip · owner: 1-track3 ==== */

/* Spec: docs/design/kit/17-pro-filter-bar.md. Component:
   apps/web/src/components/kit/pro_filter_bar.rs, which carries the API, the
   field-slot boundary, the roving tabindex and the four routed open questions.
   Only the values live here.

   Drawn twice — `rd-pro-roster.jsx:53-67` (factored as `SearchBar`) and
   `rd-pro-progress.jsx:167-174` (inline) — under `34 (Desktop) Pro Members` and
   `37 (Desktop) Pro Shooter Progress`.

   ANATOMY:

     [ bar: flex row, center, gap 16, flex none, wrap ]
       [ field  : A SLOT — flex 1 1 240px, max-width 340|320, PAINTS NOTHING ]
       [ chips  : flex, gap 6 ]
           [ chip ] [ chip--on ] …
       [ count  : 12px fg3, pushed right by margin-inline-start: auto ]

   ⚠️ THE FIELD SLOT PAINTS NOTHING, and that is the rule this file exists to
   keep. It declares layout and only layout: no background, no border, no
   padding, no font and no placeholder colour. The field inside it paints
   itself — `RdSearchField` and `au5.css` own that box — and a fill or a stroke
   here would be a second opinion about a control this bar does not own. Pinned
   by `the_field_slot_paints_nothing`.

   ⚠️ TWO max-widths ARE A DEFECT, not a redundancy. The fields that ship today
   carry their own cap (adm-mem.css `max-width: 340px`, adm-prog.css
   `max-width: 320px`). When those inputs move into this slot in slices 8 and
   11, the field's own cap is DELETED and `--w320`/`--w340` below is the only
   one. Two caps able to disagree is the defect `14-keypad.md` §3 refused for
   the pad's width.

   ⚠️ `--rd-acc` ON THE LIT CHIP, NEVER `--rd-volt`. The selected chip's accent
   is a STROKE, and substrate §1.1 puts a stroke on `acc` — so the light theme
   renders that border #5C5030 and not the dark accent. This is the single most
   likely token error in the component, and it is invisible in dark theme where
   both tokens are #A89968. The light panel on `/kit` is where it is checked by
   eye. (Both hexes moved with the tan rebrand, export 28 — #3E4E10 and #CEFF51
   before it. The error they describe did not.)

   ⚠️ NO FILL ON EITHER CHIP STATE. Neither drawn chip row carries a fill, and
   the fill is what distinguishes `RdChipSelector`'s chip (filled `--rd-volt`,
   no border) from this hollow one. **Read that against §3's two layers:** the
   `background` on `.rd-pfilter__chip` and on `--on` is the OUTER layer, which is
   the stroke; the SURFACE (`::before`) is `--rd-hollow-fill` in both states, and
   that is where "no fill" now lives. `ProChip` — spec §5.1
   calls it `RdProChip`, and the shipped name is `ProChip` — differs
   from this chip by ONE pixel of vertical padding and .02em of tracking and is
   read-only — a reviewer diffing the two rules sees the same six declarations
   and stops reading. The component pins the separation structurally.

   ⚠️ NO :hover. Undrawn on either chip row, and `14-keypad.md`'s "a phone has
   no hover" argument does NOT transfer to a mouse surface. Spec §15 item 3
   routes it; nothing is built.

   NO @media. This bar is desktop-only (spec §13); the phone surface draws a
   different object and the breakpoint is asked once, in Rust. */

/* ---------------------------------------------------------------------------
   1. The bar — a row, and it paints nothing of its own.
   --------------------------------------------------------------------------- */
/* `display: 'flex', alignItems: 'center', gap: 16` at rd-pro-roster.jsx:55, and
   the same three at rd-pro-progress.jsx:167. `flex: 'none'` is drawn on the
   roster line: the strip is a band between the tab rule and the scrolling list
   and must not stretch with it.

   `flex-wrap: wrap` is an UNDRAWN addition carried from the shipped bars
   (adm-mem.css:56, adm-prog.css:37): a four-role chip row plus a 340px field
   overflows a narrow console long before it overflows the page.

   No border-bottom. The rule under the strip belongs to the table head
   (`15-pro-table.md`) or to the tab strip above it, never to this row. */
.rd-pfilter {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  flex: none;
}

/* TWO DRAWN ANSWERS FOR THE GUTTER, and both ship. The roster strip is a band
   with its own gutter — `padding: '16px 32px 14px'` (rd-pro-roster.jsx:55) —
   while the progress strip sits inside an already-padded body
   (`'20px 32px 32px'`, rd-pro-progress.jsx:166) and draws none.

   `--band` is the default because both SHIPPED bars already take it:
   adm-prog.css:36-42 declares `padding: 16px 32px 14px`, byte-identical to
   adm-mem.css:52-58, having deviated from its own drawing. That is evidence
   about the default and NOT evidence the drawing agrees — spec §15 item 2
   routes the difference rather than smoothing it over. */
.rd-pfilter--band {
  padding: 16px 32px 14px;
}

.rd-pfilter--flush {
  padding: 0;
}

/* ---------------------------------------------------------------------------
   2. The field slot — layout, and nothing else.
   --------------------------------------------------------------------------- */
/* `flex: 1, maxWidth: 340` at rd-pro-roster.jsx:56; `maxWidth: 320` at
   rd-pro-progress.jsx:168. The `240px` basis is the undrawn addition both
   shipped bars already carry, with its recorded reason: "340px is the drawn
   width; it shrinks rather than pushing the chips off the row"
   (adm-mem.css:60-62).

   `min-width: 0` so a long placeholder cannot push the flex item past its
   basis; `display: flex` so the field inside fills the slot rather than sitting
   at its content width — the same fact adm-mem.css:63-67 records about
   `RdSearchField`'s wrapper being the flex child. */
.rd-pfilter__field {
  flex: 1 1 240px;
  min-width: 0;
  display: flex;
}

/* ⚠️ THE `display: flex` ABOVE IS NOT ENOUGH ON ITS OWN, AND THE SHORTFALL IS
   INVISIBLE IN THE FLEX DIRECTION IT DOES NOT CONTROL.

   `display: flex` stretches a child on the CROSS axis by default; on the MAIN
   axis a child is `flex: 0 1 auto`, which is its content width. `RdSearchField`'s
   wrapper is `.rd-search { display: block; }` (`apps/web/styles/au5.css:168`) —
   as a block child of a block it filled its parent, which is why the two shipped
   bars work: they wrap the field in a plain block (`.rd-mem-searchwrap`,
   `.rd-prog-searchwrap`) and the field fills it. Dropped into this slot the same
   wrapper becomes a FLEX ITEM, and a block flex item does not fill — it shrinks
   to its input's intrinsic width and stops short of the 320/340 cap the slot
   above is here to set. Full height, wrong width: the failure looks like a
   correctly-drawn field on a bar that forgot its cap.

   So the slot sizes whatever it is handed, once, here — not a class the page
   adds to the field, which would put the slot's own job outside the slot and
   let one bar disagree with another about whether the cap applies. The `*` is
   deliberate: §2 rules that this component does not define a field, so it cannot
   name the child it is sizing. `min-width: 0` repeats the parent's for the child,
   or a long placeholder pushes the field past the cap from the inside. */
.rd-pfilter__field > * {
  flex: 1 1 auto;
  min-width: 0;
}

/* 320 is the DEFAULT, on a count rather than a preference: it is the number the
   Library search arrived at independently (rd-pro-library.jsx:111), by the same
   hand for the same job on the same console, which makes 340 the outlier across
   three drawn desktop search fields. Members passes 340 explicitly so the
   outlier is visible at its call site instead of hiding in a default.

   COLLAPSE NEITHER. 340 is not tidied to 320 and 320 is not widened to 340;
   spec §15 item 1 asks the designer whether the 20px is deliberate. These are
   pins and not rulings — they are yours to update when he answers. */
.rd-pfilter__field--w320 {
  max-width: 320px;
}

.rd-pfilter__field--w340 {
  max-width: 340px;
}

/* ---------------------------------------------------------------------------
   3. The chips — FILTERS, not status.
   --------------------------------------------------------------------------- */
/* The designer's reasoning, in the drawing, in capitals (rd-pro-roster.jsx:57-59):

     "These are FILTERS, not status. Selection legitimately takes the accent -
      'the one you are on' is the same fact `live` names on a status axis - so
      the two uses do not compete, they agree."

   `display: 'flex', gap: 6` at rd-pro-roster.jsx:60. `flex-wrap: wrap` is the
   shipped undrawn addition (adm-mem.css:76). */
.rd-pfilter__chips {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

/* `pdisp(700, 78, '.08em')`, `fontSize: 9`, `padding: '5px 9px'`, a 1px stroke
   and NO fill — all five off rd-pro-roster.jsx:62, and the same line at
   rd-pro-progress.jsx:171.

   ⚠️ **THE CHAMFER, AND THE 27 AUG STAGING DEFECT THIS BLOCK USED TO BE.** This
   comment previously read *"No chamfer: `tcham` is on neither chip row"*, and
   the rule below spelled a square `border: 1px solid`. James, staging, at TABLET
   width (`/admin/members`, 1024 landscape — `TABLET_MIN_WIDTH_PX`,
   `apps/web/src/device.rs`): the ALL / ADMIN / INSTRUCTOR row drew SQUARE
   corners while the phone strip beside it (`.rd-prom-chip`, adm-mob.css §3.3)
   drew the cut, because #1363 fixed the phone families and this desktop one is
   a different class family that nobody had reached.

   The absence was read off the drawing correctly and is still wrong: `tcham` is
   the DRAWING's helper, and the drawing predates `01 Foundations` §03 as
   re-ruled 26 Aug 2026 — **13 on controls, 14 on containers, house-wide**. A
   filter chip is a control. The house rule outranks a helper the frame did not
   call, and one console cannot cut its chips two ways at two widths.

   ⚠️ **IT IS TWO LAYERS, AND ONE LAYER IS THE OTHER HALF OF THE SAME DEFECT.**
   `48 Kit Rules` — *"a stroked chamfer must be two layers"*; `01 Foundations`:85;
   `chamfer_box.rs`'s header derives it. A `border` UNDER a `clip-path` renders
   the diagonal BARE: the clip removes the corner and takes the border drawn on
   it. So:

     the ELEMENT   the outer layer, which IS the stroke — a box in the stroke
                   colour, clipped at `--rd-chamfer-control`, its padding
                   carrying the 1px the border used to occupy so the BOX is the
                   same box and the drawn `5px 9px` hit area is unchanged.
     ::before      the surface, inset by the stroke width and clipped at
                   `inner_chamfer_px(13, 1)` = 12.41, which is what keeps the
                   stroke's thickness UNIFORM along the diagonal.

   A pseudo-element rather than a face span (which is what the read-only
   `ProChip` mounts): the label is the button's own text child, and `z-index: -1`
   under `isolation: isolate` paints the surface at step 2 of the chip's own
   stacking context — above the element's background and below the inline label.
   The same construction adm-mob.css §3.2/§3.3 and `adm-lib-mob.css` §2 ship, and
   the reason this stays a CSS family rather than becoming `ProChip`: `ProChip`
   is a `<span>` that must never be pressed, and its `Live` tone lights the
   LABEL `--rd-acc` where a lit filter lights it `--rd-fg`. One geometry, pinned
   once in `components/pro/chip.rs`; two components, because they are two
   objects.

   ⚠️ **NO WIDTH QUERY MAY RE-CUT THIS CHIP** — that divergence IS the tablet
   symptom, and `the_chip_corner_is_width_independent` (`chip.rs`) fails the
   assembled sheet if one appears.

   The 44x44 hit expansion `08-tchip.md` §4.1 rules for interactive chips does
   NOT apply: that ruling was argued from a phone "used outdoors, in gloves, on
   a live line", this is a mouse console, and a 44px box around a 9px chip in a
   16px-gap row would make neighbouring targets overlap. The chip's own
   `5px 9px` is the hit area. Whether it clears WCAG 2.2 AA's 24x24 is a
   MEASUREMENT to take on the built `/kit` surface — spec §15 item 4 — and if it
   measures under, the fix is a transparent hit box at 24 and never more
   padding, which would change the drawn design. */
.rd-pfilter__chip {
  position: relative;
  isolation: isolate;
  border: 0;
  background: var(--rd-line);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--rd-chamfer-control)), calc(100% - var(--rd-chamfer-control)) 100%, 0 100%);
  color: var(--rd-fg3);
  /* The drawn `5px 9px` plus the 1px the border used to occupy. */
  padding: 6px 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9px;
  cursor: pointer;
}

/* The surface. `--rd-hollow-fill` is the kit's outlined-control ground
   (`Fill::Hollow`), and it must be OPAQUE: a transparent surface lets the outer
   layer's stroke colour flood the whole chip, which is the one way this
   construction fails loudly. */
.rd-pfilter__chip::before {
  content: "";
  position: absolute;
  inset: 1px;
  z-index: -1;
  background: var(--rd-hollow-fill);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 12.41px), calc(100% - 12.41px) 100%, 0 100%);
}

/* "i === 0 ? t.acc : t.line" and "i === 0 ? t.fg : t.fg3" — one line,
   rd-pro-roster.jsx:62, carrying both axes. A STROKE and a LABEL, never a fill:
   a lit filter paints its label `--rd-fg`, where `ProTone::Live` paints the
   label `--rd-acc` as well as the border (rd-pro-kit.jsx:274). Building this row
   out of `ProChip` would light the label the wrong colour and hand a <span> to
   somebody's keyboard.

   ⚠️ **THE TREATMENT IS UNCHANGED AND ONLY ITS SPELLING MOVED**: what was
   `border-color` is the OUTER layer's `background` now, because the outer layer
   IS the stroke. The face stays `--rd-hollow-fill`, so the lit chip is still
   hollow with an accent stroke and is still not a fill. */
.rd-pfilter__chip--on {
  background: var(--rd-acc);
  color: var(--rd-fg);
}

/* ---------------------------------------------------------------------------
   4. Focus — an inset ring, because the chip is clipped.
   --------------------------------------------------------------------------- */
/* ⚠️ **THIS WAS THE HOUSE `outline` AND IT CANNOT BE ANY MORE.** `clip-path`
   clips an `outline` away, so the ring this block used to declare would draw
   NOTHING once §3 gained its chamfer — the same substitution `14-keypad.md`
   §3.1 argued, `kit-tab-chip.css` §4 states, and adm-mob.css §3.2 ships.

   ⚠️ IT RIDES THE SURFACE LAYER, NOT THE ELEMENT. An `inset` `box-shadow` paints
   with the element's own background — step 1 — which here is the STROKE layer,
   underneath the surface. Left on the element the ring would be painted over and
   focus would show nothing at all.

   `:focus-visible` only, never `:focus` — a plain `:focus` leaves a stuck ring
   under the last chip a mouse clicked. */
/* ⚠️ THE FILTER CHIP WAS ~23px OF TARGET — a 9px display line inside 6px of
   padding. James, NTOA rehearsal 2026-08-29: the Pro strip is used at a tablet
   on the range, where the phone chip's answer (a 44px `.rd-chip-hit` wrapper
   around a small painted chip) is not available without repainting this one.

   So the target is grown by a transparent `::after` instead. It hit-tests as
   the chip — a pseudo-element belongs to its originating element — and it
   paints nothing, so the drawn chip and the strip's rhythm are byte-identical.

   VERTICAL ONLY, and that is the whole reason this shape is allowed here:
   `.rd-pfilter__chips` is a horizontal flex row, so the chip's neighbours are
   beside it and a 44px band above and below it overlaps nothing. Growing
   sideways would put each chip's target under the next one's glyph, which is
   the trade-off `.rd-chip-hit` already refused by name. `z-index` is left alone
   deliberately: `::before` is the face at -1 and this sits in the default flow
   above the label, which is the same element, so nothing is covered that is not
   already the chip. Shared note: `kit-hit.css`. */
.rd-pfilter__chip::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 44px;
  /* `margin-top`, not a `transform`: a transform would make the pseudo a
     containing block for fixed descendants, which is a hazard the pool already
     polices by name in three places. Half of 44. */
  margin-top: -22px;
}

.rd-pfilter__chip:focus-visible {
  outline: none;
}

.rd-pfilter__chip:focus-visible::before {
  box-shadow: inset 0 0 0 2px var(--rd-acc);
}

/* ---------------------------------------------------------------------------
   5. The count line.
   --------------------------------------------------------------------------- */
/* `fontSize: 12, color: t.fg3` at rd-pro-roster.jsx:66. Absent from the
   progress drawing, which is why the component takes it as `Option`.

   THE DRAWN SPACER IS NOT AN ELEMENT. The roster pushes its count right with an
   empty flex child — `<div style={{ flex: 1 }} />` at rd-pro-roster.jsx:65 —
   whose only job is to eat the gap. `margin-inline-start: auto` on the count
   itself renders identically with one fewer node and nothing for a screen
   reader to walk past, and both shipped bars already do it this way
   (adm-mem.css:109). Pinned by
   `the_count_is_pushed_by_an_auto_margin_not_a_spacer_node`.

   `tabular-nums` because the number changes on every keystroke: "tabular so it
   does not jitter as the search narrows it" (adm-mem.css:95-96).

   ⚠️ THIS IS NOT "Showing 6 of 318". That foot line is a different object at a
   different size in a different place — 11.5px, under the last row, capped at
   70ch on Progress, and it carries that page's honesty caveat. It belongs to
   the table's notice paragraph (`16-row-taxonomy.md` item 9), and pulling it in
   here would put a 70ch paragraph on a flex row. */
.rd-pfilter__count {
  margin-inline-start: auto;
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--rd-fg3);
}
/* ==== kit-zone-dial · RdZoneDial / RdZoneDialStack, the 84px scoring dial · owner: 1-track3 ==== */

/* Spec: docs/design/kit/18-zone-dial.md, which carries the anatomy, the two
   rulings and the six routed questions. Only the values live here.
   Component: apps/web/src/components/kit/zone_dial.rs.

   The drawing is `59 (Mobile) Pro Scoring` frames E1/E2, drawn as
   `ScreenPscGloveE` at rd-pro-scoring-lab.jsx:173 ("function ScreenPscGloveE"),
   under the direction rd-pro-scoring-lab.jsx:169 ("THE GLOVE DIRECTION"):
   "one giant -/+ row per zone, the zone's letter between the targets; the count
   appears beside the letter once tallied."

   ⚠️ THIS IS NOT `.rd-repstep`, AND THE TWO MAY NOT BE MERGED. kit-rep-stepper.css
   keeps every rule it has. The rep stepper is 44x44 flush boxes with the value
   AFTER them and a ruled floor of ONE — see that file's
   `ruling_one_rep_is_the_floor_because_zero_is_a_removal`,
   from rd-rangeday.jsx:69 ("Minus goes quiet at one"). This control
   is 84x84 with a 14px gap, the value BETWEEN the targets, asymmetric fills, and a
   floor of ZERO on the opposite ruling (spec §5.2). Under the kit's `ruling_`
   convention a collision between those two is a designer question, not a number
   to reconcile.

   ⚠️ NO `overflow` AND NO VIEWPORT UNIT IN THIS FILE. The stack is `flex: 1`
   inside `.rd-prom-bare`, which owns the one scroll model on this surface
   (adm0-mobile.css §1b). rd-pro-scoring-lab.jsx:196 draws the spacer as
   `flex: 1, minHeight: 0` and no scroller at all — five dials fit the 676px
   device. A six-zone drill is routed as spec §9.2. */

/* ---------------------------------------------------------------------------
   1. One dial.
   --------------------------------------------------------------------------- */

/* rd-pro-scoring-lab.jsx:178 ("gap: 14, marginTop: 6"):
   `flex: 'none', display: 'flex', alignItems: 'center', justifyContent:
   'center', gap: 14, marginTop: 6`. The row does NOT stretch — the stack's
   spacer below the dials is what absorbs the slack, so a dial keeps its 84px
   targets at any device height. */
.rd-zdial {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin-top: 6px;
}

/* One target. 84 x 84 on the control itself, not on padding around it —
   rd-pro-scoring-lab.jsx:179 has `width: 84, height: 84` and so does
   rd-pro-scoring-lab.jsx:184. That is 1.5x the ruled 56px scoring floor
   (`59 (Mobile) Pro Scoring.html:27`), which is the whole point of the glove
   direction.

   Everything below the two size lines is either the drawn type role or a reset:
   a `<button>` arrives with a UA background, border and font, and each of those
   would otherwise paint over a drawn value.

   Type is the display voice at `tdisp(800, 78, 0)` — Archivo 800, 78% stretch,
   28px, NO tracking. `text-transform` is inert on the two glyphs and is kept
   anyway, because it is what the role IS: a glyph that later becomes a word
   should not silently change voice.

   The MINUS is the base rule and the PLUS is the modifier, matching the drawing's
   own asymmetry — the additive target is the filled one. */
.rd-zdial__btn {
  width: 84px;
  height: 84px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: transparent;
  border: 1.5px solid var(--rd-ctrl);
  color: var(--rd-fg2);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: 0;
  font-size: 28px;
  line-height: 1;
  cursor: pointer;
}

/* The plus — rd-pro-scoring-lab.jsx:184 ("background: t.volt"):
   `background: t.volt, color: t.onVolt`, and NO border at all IN THE DRAWING
   (the one below is the theme-conditional edge, third block down, and it is
   `transparent` in the theme this frame is drawn in). It is a fill, not a
   stroke, so the `ctrl` hairline above is removed rather than recoloured;
   leaving it would put a 1.5px `ctrl` line around a volt block, which the
   drawing does not have and which reads as a seam at 84px.

   ⚠️ FIVE OF THESE PLUS A VOLT `Add Time` IS SIX VOLT FILLS ON ONE SCREEN,
   against `48 Kit Rules.html:38` ("One volt-filled role per screen"). Built as
   drawn and ROUTED — spec §9.1. Do not step it down to an accent outline here;
   that is the designer's call and it would be invisible in a diff.

   ⚠️ THE BORDER IS `--rd-btn-edge` AND NOT `none` — THE LIGHT PASS (S12).
   The SAME cell, `48 Kit Rules.html:38`, ends: "Light theme always hairlines a
   volt fill with `acc`." rd-train-kit.jsx:42 ("acc vs volt") says why:
   `volt` is the SAME hex in both themes, so on the near-white
   light ground (#A89968 on #F1F1EC) the fill has no edge of its own, and "any
   accent surface bigger than a badge needs that hairline". `TBtn` is named
   there as the reference — in light it is an `acc` STROKE around a `volt` FILL.

   ⚠️ CARRIED AS A TOKEN VALUE, NOT AS A `[data-theme]` RULE, and that is the
   kit's standing answer for this exact treatment rather than a preference.
   `--rd-btn-edge` is `transparent` in dark (`_legacy-tokens.css:188`) and
   `var(--rd-acc)` in light (`_legacy-tokens.css:227`), under architect ruling
   ② recorded on `chamfer_box.rs` (`Stroke::PrimaryEdge`): "theme-conditional
   treatments are expressed as token values wherever a value can carry them —
   and `transparent` is a value." Dark therefore renders exactly what it
   rendered before — a transparent border paints nothing — and the 84px target
   does not move in either theme, because `_legacy-base.css:22` sets
   `box-sizing: border-box` on everything.

   ⚠️ THE TWO CHAMFERED VOLT ACTS DO NOT GET THIS AND IT IS NOT AN OVERSIGHT.
   `.rd-zdial-stack__act` below and `.rd-runsh__save` (adm-run-sheet.css) are
   `clip-path` fills, and `48 Kit Rules.html:33` rules that "every stroked
   chamfer routes through `TOut` (a CSS border under a clip-path renders the
   diagonal bare — recurred three times)". HELD as designer/architect question
   220 — see the note on that rule. This target is a plain square with no
   `clip-path`, so the border route is legal here and only here. */
.rd-zdial__btn--plus {
  background: var(--rd-volt);
  border: 1.5px solid var(--rd-btn-edge);
  color: var(--rd-on-volt);
}

/* No drawn focus state, so this is the house treatment: the kit's 2px
   `--rd-acc` ring, `:focus-visible` so a pointer press does not leave one
   behind.

   ⚠️ POSITIVE OFFSET, unlike `.rd-repstep__btn:focus-visible`, and the
   difference is geometric rather than stylistic: the rep stepper's three boxes
   are FLUSH, so a +2px ring there paints over its neighbours. These three sit
   14px apart (§1 above) and there is nothing to paint over. */
.rd-zdial__btn:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* The centre — rd-pro-scoring-lab.jsx:180 ("minWidth: 58, textAlign"):
   `minWidth: 58, textAlign: 'center'`. It is ONE SLOT WITH TWO OCCUPANTS, never
   two slots one of which is empty: at rest the 30px line carries the letter,
   and once tallied the 30px line carries the count with the letter beneath it.
   The 58px minimum is what stops the two targets sliding inward as a count
   grows from `2` to `22`. */
.rd-zdial__centre {
  flex: none;
  min-width: 58px;
  text-align: center;
}

/* The 30px line — rd-pro-scoring-lab.jsx:181 ("fontSize: 30"):
   `tdisp(900, 70, '.02em'), fontSize: 30`. Its CONTENT changes with the tally
   (letter, then count) and its type does not; that is what makes the count read
   as the same object the letter was, rather than as a badge appearing beside it. */
.rd-zdial__value {
  display: block;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 900;
  font-stretch: 70%;
  letter-spacing: .02em;
  font-size: 30px;
  line-height: 1;
  color: var(--rd-fg);
}

/* The letter, demoted beneath the count once tallied —
   rd-pro-scoring-lab.jsx:182 ("fontSize: 18"):
   `tdisp(900, 70, '.02em'), fontSize: 18, marginTop: 2`, and `t.fg3` where the
   value is `t.fg`. Same type role, two sizes and two tones: the number is the
   fact and the letter is now the caption on it.

   ⚠️ It exists ONLY when a count exists. There is no reserved 18px band under an
   untallied dial — the drawn ternary renders `null`. */
.rd-zdial__letter {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 900;
  font-stretch: 70%;
  letter-spacing: .02em;
  font-size: 18px;
  line-height: 1;
  margin-top: 2px;
  color: var(--rd-fg3);
}

/* Penalty tone — Miss and No-Shoot. rd-pro-scoring-lab.jsx:195 mounts the last
   two dials as `dial('M', 1)` and `dial('NS', 1)`, and that flag reaches exactly
   two ternaries: rd-pro-scoring-lab.jsx:181 ("danger ? t.danger : t.fg") on the
   value and rd-pro-scoring-lab.jsx:182 ("danger ? t.danger : t.fg3") on the
   letter.

   ⚠️ THE CENTRE ONLY. The minus keeps `ctrl`/`fg2` and the plus keeps its volt
   fill on a penalty dial — neither :179 nor :184 takes the flag. A penalty dial
   whose TARGETS also went red would be a different drawing, and it is the
   obvious thing to add by eye.

   ⚠️ `--rd-danger`, NOT `--rd-dfill`. `dfill` is the destructive FILL and is its
   own key; the drawn value is the figure colour on text. */
.rd-zdial__value--danger {
  color: var(--rd-danger);
}

.rd-zdial__letter--danger {
  color: var(--rd-danger);
}

/* ---------------------------------------------------------------------------
   2. The stack the dials stand in — rd-pro-scoring-lab.jsx:188 ("const base").
   --------------------------------------------------------------------------- */

/* WHY THE FRAME IS IN THE KIT PART AND NOT ON THE PAGE: every class in this
   section is emitted by `RdZoneDialStack` in the same module as the dial. A part
   that declares vocabulary its own component never renders is the drift
   order.txt's filing notes refuse; a page that hand-rolls a frame around a kit
   control is the drift the row taxonomy refuses. One name space, one renderer.

   The stack is a flex child of `.rd-prom-bare`, the chromeless console root
   (adm0-mobile.css §1b) — so it claims no viewport height of its own and
   declares no `overflow`. `min-height: 0` is here because a flex child's default
   `auto` minimum would let the dials push the spacer past the device foot. */
.rd-zdial-stack {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* The header row — rd-pro-scoring-lab.jsx:190 ("gap: 7, marginBottom: 2"):
   `flex: 'none', display: 'flex', alignItems: 'center', gap: 7, marginBottom: 2`.

   ⚠️ NO ORG NAME AND NO MENU CONTROL, and that is the ruled exception rather
   than an omission: `59 (Mobile) Pro Scoring.html:37` takes the Pro chrome off
   every screen in this lab — "mid-squad, identity is overhead and the full
   device height goes to the work; the back row is the only way out." The chrome
   is dropped by `admin/layout.rs`, never by a page; this row is the way out it
   leaves behind. */
.rd-zdial-stack__head {
  flex: none;
  display: flex;
  align-items: center;
  gap: 7px;
  margin-bottom: 2px;
}

/* The back chevron — rd-pro-scoring-lab.jsx:191 ("color: t.fg3"): body face,
   13px, `t.fg3`. It is drawn as a glyph and built as a real `<button>`: it is
   the only exit from the screen, and an exit that is not in the tab order is not
   an exit. The reset lines are the UA's, as on the dial's targets above. */
/* ⚠️ THE BACK LINK'S TARGET WAS ITS 13px OF TEXT — James, NTOA rehearsal
   2026-08-29. The transparent `::after` below is the whole fix; this rule keeps
   its drawn geometry exactly, and gains only the `position` the pseudo needs as
   a containing block. */
.rd-zdial-stack__back {
  flex: none;
  display: flex;
  align-items: center;
  position: relative;
  padding: 0;
  background: transparent;
  border: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  line-height: 1;
  color: var(--rd-fg3);
  cursor: pointer;
}

/* 44px of target, no paint and no layout. A pseudo-element hit-tests as its
   originating element, so a tap anywhere in this band is a tap on the button.
   VERTICAL ONLY: `.rd-zdial-stack__head` is a horizontal flex row, so growing
   sideways would reach under `.rd-zdial-stack__who` beside it — growing up and
   down reaches into the row's own leading, which is nobody's target. The shared
   note for this shape is in `kit-hit.css`. */
.rd-zdial-stack__back::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 44px;
  /* `margin-top`, not a `transform`: a transform would make the pseudo a
     containing block for fixed descendants, which is a hazard the pool already
     polices by name in three places. Half of 44. */
  margin-top: -22px;
}

.rd-zdial-stack__back:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* The shooter — rd-pro-scoring-lab.jsx:192 ("Priya Raghunathan"):
   `tdisp(800, 78, '.03em'), fontSize: 13.5, color: t.fg`. The one thing on this
   screen that says who the tally belongs to. */
.rd-zdial-stack__who {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .03em;
  font-size: 13.5px;
  color: var(--rd-fg);
}

/* The drill — rd-pro-scoring-lab.jsx:193 ("· Bill Drill"): body face, 10.5px,
   `t.fg3`. The interpunct is drawn INTO the string; it is a separator between
   two facts, not a list bullet, so it ships as copy from the caller. */
.rd-zdial-stack__drill {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
}

/* rd-pro-scoring-lab.jsx:196 ("flex: 1, minHeight: 0"). An empty box whose only
   job is to push the act to the foot, so the tally sits under the reader's eye
   and the act sits under the reader's thumb. Not `margin-top: auto` on the act:
   the drawing puts a real element here, and a spacer with a name is what makes
   "the stack does not scroll" reviewable. */
.rd-zdial-stack__spacer {
  flex: 1;
  min-height: 0;
}

/* Add Time — rd-pro-scoring-lab.jsx:197 ("height: 68, background: t.volt"):
   `flex: 'none', height: 68, background: t.volt, color: t.onVolt`, centred,
   `tdisp(800, 78, '.08em'), fontSize: 12.5`, `tcham(9)`.

   ⚠️ 68, NOT 56. `59 (Mobile) Pro Scoring.html:27` states the phone floor for
   scoring surfaces as 47's 56px and says it "holds on every control here"; this
   one control is drawn above it. A floor is a minimum so this is not a
   violation, but it is the only number on the screen that matches nothing else.
   ROUTED — spec §3.2.

   ⚠️ `margin-bottom` IS ADDED AND THE DRAWING DOES NOT DRAW IT.
   `48 Kit Rules.html:47`: "Nothing tappable sits in the home-indicator zone" —
   ruled by James on 25 Aug from first-hand misfires. The drawn frame is a
   312x676 mock with no home indicator, and `.rd-prom-bare` pads `18px 0`, so an
   act ending at the bottom edge of this nav-less phone screen sits under iOS's
   swipe-away gesture. The value arrives as the kit's shared token and never as a
   28: `kit-shell.css` owns it so the screens that carry it cannot drift apart.
   ROUTED — spec §3.3.

   `clip-path` is `tcham(9)` transcribed exactly — rd-train-kit.jsx:23
   ("const tcham") — the bottom-right corner cut, not a radius.

   ⚠️ NO LIGHT HAIRLINE ON THIS ACT — HELD, QUESTION 220 (slice S12).
   `48 Kit Rules.html:38` says "light theme always hairlines a volt fill with
   `acc`", and `.rd-zdial__btn--plus` above takes that as `--rd-btn-edge`. This
   act cannot: `48 Kit Rules.html:33` rules that "every stroked chamfer routes
   through `TOut` (a CSS border under a clip-path renders the diagonal bare —
   recurred three times)", and an inset `box-shadow` ring is the same rectangle
   under the same clip — it would leave the one drawn diagonal bare, which is
   the named recurring defect and not a hairline.

   The in-kit fix is STRUCTURAL, not a value: rebuild this act on
   `RdChamferBox` with `Fill::Volt` + `Stroke::PrimaryEdge` (the pair
   `button.rs:149` gives `Kind::Primary` and `shoot_no_shoot.rs` pins), which
   draws the stroke as the outer chamfered layer and follows the diagonal. That
   is markup on S9's and S10's surfaces at drawn heights of 68 and 56 which
   `RdButton` does not offer, so it is a slice and not a light pass. Do not add
   a border or an inset ring here to close the gap by eye. */
.rd-zdial-stack__act {
  flex: none;
  height: 68px;
  margin-bottom: var(--rd-home-clear);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  background: var(--rd-volt);
  color: var(--rd-on-volt);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 12.5px;
  line-height: 1;
  cursor: pointer;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 9px), calc(100% - 9px) 100%, 0 100%);
}

/* `clip-path` clips an `outline` away, so the ring is drawn as an inset shadow
   instead — the same construction, and the same reason, adm-lib-mob.css records
   for its chamfered chip. */
.rd-zdial-stack__act:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--rd-acc);
}
/* ==== AU5 · docs/design/kit/AU5-text-field.md · owner: lane4 ==== */

/* RdTextField — the labelled text input, one primitive for Auth and Profile.

   NUMBERS ARE AUTH'S COLUMN (spec §5.2). The two drawings disagree on eleven
   values and §9.2 declines to pick; Profile is uniformly one step tighter, which
   is either a later generation or a fit to a denser screen (routed as Q2). Auth
   wins here because 7 of 11 call sites are Auth's and `dense` is Auth's axis —
   so Profile's four sites render one step looser than drawn until Q2 lands.

   The box itself is NOT here: it is RdChamferBox at r=12, w=1, Fill::Field, so
   the clip-path, the two layers and the inner cut all come from one tested
   implementation. There is no `border` and no `border-radius` in this block on
   purpose — a stroke on a clipped box loses its diagonal (substrate §3.3). */

.rd-field { margin-bottom: 16px; }
.rd-field--dense { margin-bottom: 11px; }

/* Auth draws 10 / .16em, which is character-for-character the TShell *title*
   role at a different colour. Neither drawing uses the bound TLab role (8.5 /
   .2em) — spec Q1 asks whether the kit gains a field-label role or collapses
   onto TLab. The caps are PRESENTATIONAL: the DOM holds the authored string, so
   a caller that pre-uppercases ships a screamed accessible name (§7.3). */
.rd-field__label {
  display: block;
  margin-bottom: 6px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .16em;
  font-size: 10px; color: var(--rd-fg3);
}
/* Label recolours with the stroke, and error outranks focus (§6.1). */
.rd-field__label--focus { color: var(--rd-acc); }
.rd-field__label--error { color: var(--rd-danger); }

/* ⚠️ THE HIT AREA — a `label` wrapping the box, and the ONE rule it needs.

   THE DEFECT IT FIXES (reported from a tablet, and it reproduces at every
   width): tapping a field near its edge did not focus it; only a tap on the
   middle, over the value or the placeholder, did. The anatomy says why —
   `.rd-field__box` carries `padding: 15px 16px` and `min-height: 52px`, and
   `.rd-field__input` is `flex: 1` with `padding: 0`, so the input's own rect is
   the ~19px text run centred in a 52px box. Everything outside it was a `div`,
   and a `div` forwards nothing. `cursor: text` on the box announced an
   affordance the box did not have, which is why this read as a bug rather than
   as a layout.

   The markup is a `label` (`text_field.rs`, `search_field.rs`), so the browser
   forwards an activation anywhere in the box to the input — full rect, no JS,
   no listener to keep in sync, and it fixes clicking the caption at the same
   time. `display: block` is the whole rule: the wrapper must lay out exactly as
   the `div` it replaced, and a `label` is inline by default.

   `min-width: 0` is for the one mount that puts it in a flex row
   (`.rd-search--submit` below) — a flex item's `auto` minimum would refuse to
   shrink below the input's intrinsic width and push the button off the row.

   NO PADDING, NO HEIGHT AND NO CURSOR HERE. All three belong to the box inside
   it, which already draws them; restating any of them would move the render. */
.rd-field__hit { display: block; min-width: 0; }

/* The inner layer of the chamfered box: padding, min-height and the row live
   here, as they do at 84/84 sites. `gap: 1` is PROFILE's number (§5.2) — Auth
   draws no prefix and therefore specifies no gap. Mixed provenance, stated.

   ⚠️ THE TWO `min-height` VALUES ARE THE TAP TARGET and a test asserts both
   clear 44px. They were drawn as a type-and-padding budget, and since the
   `.rd-field__hit` label above they are also the height of what a thumb can
   hit. Anything below 44 is a target smaller than a fingertip. */
.rd-field__box {
  display: flex; align-items: center; gap: 1px;
  padding: 15px 16px;
  min-height: 52px;
  cursor: text;
}
.rd-field--dense .rd-field__box {
  padding: 13px 15px;
  min-height: 48px;
}

/* Outside the value, never part of it — aria-hidden in the markup (§7.3). */
.rd-field__prefix {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 16px; color: var(--rd-fg3);
}

/* The body face: weight 400 and NO font-stretch, because absent is 100 and
   absent is correct (substrate §2.2). That is the second big divergence from
   RdNumericField, which speaks in the display voice.

   `caret-color` is the drawn 2px accent caret bar, ported. The browser owns the
   caret on web, so this puts the design's colour at the TRUE insertion point
   rather than drawing a static bar that would be right only at end-of-line.

   The outline is removed and NOT replaced: `focus` here is DOM focus, so the
   accent stroke on the box already is the keyboard-focus indicator, and a ring
   would double-draw it (§6.2). clip-path would cut an outline anyway. */
.rd-field__input {
  flex: 1; min-width: 0;
  /* ⚠️ THE VALUE'S OWN RECT, and it is the hit-area fix's second half. The box
     is `align-items: center`, so the input was as tall as one line of text and
     a tap on the row's upper or lower third missed it. Stretching it to the
     box's content height costs nothing visually — a single-line input centres
     its text in whatever height it is given — and it means a tap anywhere in
     the row lands ON the input and places the caret at the tapped column,
     rather than being forwarded by the label with no position. */
  align-self: stretch;
  border: 0; background: none; outline: none; padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 400;
  font-size: 16px; color: var(--rd-fg);
  caret-color: var(--rd-acc);
}
/* ⚠️ SPECIFICITY DEFENCE, not decoration — do not flatten this to one class.

   The pre-kit Tailwind backfill styles bare inputs by ATTRIBUTE:
     input[type="text"], input[type="email"], input[type="password"]
       { border: 1px solid #d1d5db; outline: none; }      style.css:98, :1482
     input:focus { box-shadow: 0 0 0 2px #3b82f6; ... }   style.css:103, :1483

   Those selectors are (0,0,1,1) and `.rd-field__input` is (0,0,1,0), so THEY
   WIN. Undefended, this primitive draws a 1px #d1d5db rectangle inside the
   chamfer and a 2px blue ring on focus — a raw-hex border in a component whose
   colours are all token roles, and exactly the double-drawn focus indicator
   §6.2 rules out, in a colour the design does not contain.

   The compound below is (0,0,2,0) and outranks both. It is scoped to this
   component and edits nothing in the backfill block, which is not this lane's
   to touch. It stays correct if that block is ever removed.

   THIS WILL BITE EVERY KIT PRIMITIVE THAT BINDS A REAL <input>. Reported. */
.rd-field__box .rd-field__input {
  border: 0;
  box-shadow: none;
}

/* Chrome autofill paints its own light blue-grey slab behind a pre-filled
   value, ignoring `background` entirely — it can only be overridden with an
   absurdly long `-webkit-box-shadow` transition, the standard trick. Design
   rule: `02 (Mobile) Auth.html` row 18 "Field Treatment" — one flat field
   face (`--rd-field`), no inner surface, so the autofill slab is exactly the
   surface row 18 argues against. Same ancestor compound as the specificity
   defence above, for the same reason: the pre-kit backfill's `input:focus`
   rule at (0,0,1,1) would otherwise still win the box-shadow contest against
   a bare `.rd-field__input:-webkit-autofill` at (0,0,2,0)-ish. */
.rd-field__box .rd-field__input:-webkit-autofill,
.rd-field__box .rd-field__input:-webkit-autofill:hover,
.rd-field__box .rd-field__input:-webkit-autofill:focus,
.rd-field__box .rd-field__input:-webkit-autofill:active {
  -webkit-box-shadow: 0 0 0 1000px var(--rd-field) inset;
  -webkit-text-fill-color: var(--rd-fg);
  caret-color: var(--rd-acc);
  transition: background-color 9999s ease-in-out 0s;
}

/* The value/placeholder colour step is the ENTIRE filled/empty signal (§1). */
.rd-field__input::placeholder { color: var(--rd-fg3); opacity: 1; }
/* Tracking on the masked string only — never a real value, never a placeholder
   (§7.4). The ten-bullet mask is deliberately NOT reproduced. */
.rd-field__input--masked { letter-spacing: .22em; }

/* One line, four roles, at most one showing (§6.1). The container is always in
   the DOM: a polite live region that appears with its first message is never
   announced, and `Checking… → Available` is the case §7.3 asks for.

   ⚠️ THE MARGIN IS ON THE ROLE MODIFIERS, NOT ON THE BASE CLASS. The base class
   is what a status-less field renders, and it must occupy nothing.

   This replaced a rule that zeroed the margin on the empty-element pseudo-class,
   which was a bet on engine behaviour: the element holds a zero-length text node,
   and whether that pseudo-class still matches one differs between Selectors 3
   (no) and Selectors 4 (yes). Lost, every status-less field carries a stray 7px.
   The modifiers are only ever present when there IS a message, so the margin
   arrives with the line it belongs to and no selector has to decide.

   The pseudo-class is deliberately not spelled here: a test asserts it is absent
   from this section, and naming it in a comment would defeat that test the same
   way spelling a dead class in a comment makes it look used (kit/mod.rs). */
.rd-field__status {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px; line-height: 1.4;
  color: var(--rd-fg3);
}
.rd-field__status--error,
.rd-field__status--ok,
.rd-field__status--checking,
.rd-field__status--note { margin-top: 7px; }
.rd-field__status--error { color: var(--rd-danger); }
/* ⚠️ `ok` is the ONLY one of the four that leaves the body face. §4: it is
   tdisp(800, 78, .14em) at 9px — character-for-character PVerifyLine's
   *Verified* label (`rd-profile-kit.jsx:119`), *"same role, same surface family
   — worth binding to one class rather than two."* So it is a display role,
   uppercase by the helper, and the size drops 12 → 9. At 9px caps it is 11.6pt
   on device, which clears the 11pt caps floor (§5.3).
   The other three stay on the body face.

   ⚠️ THE COLOUR IS `--rd-ok` AND WAS `--rd-acc`, AND THE BINDING ABOVE IS WHY.
   This rule follows PVerifyLine, and export 28 moved PVerifyLine's verified
   branch from `color: t.acc` to `color: t.ok` (`rd-profile-kit.jsx:119`,
   "state === 'verified'") — the only line in that component that changed. It is
   the same re-assignment `/verify`'s mark took: success had been borrowing the
   brand accent because the accent was green, and when the accent went tan the
   success signal kept the green under a key of its own. The other three status
   roles are untouched — `error` is still `--rd-danger`, `checking` and `note`
   are still `--rd-fg3`.

   That the two are ONE class is now load-bearing rather than tidy: had they
   been two rules, this export would have moved one and left the other reading
   the brand accent, which is exactly the drift the binding was written to
   prevent. `--rd-ok` is declared in kit-ok.css. */
.rd-field__status--ok {
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .14em;
  font-size: 9px; color: var(--rd-ok);
}
.rd-field__status--checking { color: var(--rd-fg3); }
.rd-field__status--note { color: var(--rd-fg3); }

/* ─────────────────────────────────────────────────────────────────────────
   A6 · RdSearchField — docs/design/kit/A6-search-field.md

   The search box, in the same part as RdTextField because the two share
   `.rd-field__label` and `.rd-field__box` outright. Splitting them would put a
   selector in one part and its only consumer in another, which is the exact
   pair `order.txt` exists to keep together (spec §8.1).

   The BOX is not here, for AU5's reason above: it is RdChamferBox at r=12 (or
   10 where the drawing draws 10), w=1, Fill::Field, so the clip-path, the two
   layers and the inner cut all come from one tested implementation.

   ⚠️ This part sits at 317 in order.txt, BEFORE every site part that mounts a
   search field. That is load-bearing: a site's own input class
   (`.rd-find__input` at 322, `.rd-plat__input` at 361, and the rest) is a
   single class like these, so on a tie the SITE wins and keeps its drawn type
   size. The kit supplies the floor; the drawing supplies the deviations.
   --------------------------------------------------------------------------- */

/* The wrapper. It carries `aria-busy` in the markup and no box of its own —
   every visible edge belongs to the chamfered box inside it. */
.rd-search { display: block; }

/* AU5's layout-budget axis, same two numbers. The dense padding is restated
   rather than inherited: `.rd-field--dense .rd-field__box` is bound to the
   OTHER component's wrapper class, which a search field never carries. */
.rd-search--dense .rd-field__box {
  padding: 13px 15px;
  min-height: 48px;
}

/* The body face: weight 400 and NO font-stretch, because absent is 100 and
   absent is correct (substrate §2.2) — the same choice `.rd-field__input`
   makes above, for the same reason.

   The input is transparent and borderless: the BOX is the control's boundary,
   and a second border inside it would draw the affordance twice.

   `caret-color` is the drawn 2px accent caret, ported. The browser owns the
   caret on web, so this puts the design's colour at the TRUE insertion point
   rather than drawing a static bar that would be right only at end-of-line.

   The outline is removed and NOT replaced: focus here is DOM focus, so the
   accent stroke on the box already IS the keyboard-focus indicator, and a ring
   would double-draw it. clip-path would cut an outline anyway. */
.rd-search__input {
  flex: 1; min-width: 0;
  /* Same as `.rd-field__input`'s, for the same reason and in the same box: the
     value's rect is one line of text in a centred row, so the row's upper and
     lower thirds were not the input. Stretching it makes the tap land on the
     input itself and put the caret where the finger went. */
  align-self: stretch;
  border: 0; background: none; outline: none; padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 400;
  font-size: 16px; color: var(--rd-fg);
  caret-color: var(--rd-acc);
}

/* ⚠️ SPECIFICITY DEFENCE, not decoration — do not flatten this to one class.

   The pre-kit Tailwind backfill styles bare inputs by ATTRIBUTE:
     input[type="text"] { border: 1px solid #d1d5db; outline: none; }
     input:focus { box-shadow: 0 0 0 2px #3b82f6; border-color: #3b82f6; }
   at (0,0,1,1), which beats a bare class at (0,0,1,0). Undefended, focusing a
   kit search field paints a 2px BLUE ring inside a volt-accented box: two
   design systems on one control, in a colour the design does not contain.

   The compound below is (0,0,2,0) and outranks both. It is anchored on the
   WRAPPER rather than on the inner box, because three sites pass an inner class
   of their own — the wrapper is the one ancestor present at every mount. This
   replaces the per-site copies of the same defence that `.rd-find__box` and
   `.rd-plat__search` each carried. */
.rd-search .rd-search__input {
  border: 0;
  box-shadow: none;
  outline: none;
}

/* Chrome autofill's blue-grey slab, same fix as `.rd-field__input` above, same
   citation: `02 (Mobile) Auth.html` row 18 "Field Treatment" — one flat field
   face (`--rd-field`), no inner surface. Anchored on the wrapper for the same
   reason the defence block above it is: the wrapper is the one ancestor
   present at every mount. */
.rd-search .rd-search__input:-webkit-autofill,
.rd-search .rd-search__input:-webkit-autofill:hover,
.rd-search .rd-search__input:-webkit-autofill:focus,
.rd-search .rd-search__input:-webkit-autofill:active {
  -webkit-box-shadow: 0 0 0 1000px var(--rd-field) inset;
  -webkit-text-fill-color: var(--rd-fg);
  caret-color: var(--rd-acc);
  transition: background-color 9999s ease-in-out 0s;
}

/* The colour STEP is the CONTENT axis and needs no signal — the browser keys it
   off emptiness. The box's stroke and border are the FOCUS axis, resolved in
   Rust. Two axes, deliberately not collapsed: driving both from "is there a
   query" makes an unfocused-but-filled field wear the accent. */
.rd-search__input::placeholder { color: var(--rd-fg3); opacity: 1; }

/* ⚠️ THE FIELD'S END SLOT — round 7 item 4 (2026-08-20), the designer verbatim:
   "The field carries the 12px sweep in its end slot — request-in-flight only,
   not during the debounce timer."

   ⚠️ THE SIZE IN THAT QUOTE IS SUPERSEDED — C-43.4 (27 Aug 2026): "its size is
   13 - the earlier ruling's 12 predates the ladder; sweeps snap to ladder
   sizes". The behaviour is ratified as shipped, and the mark appears
   immediately, for the request only.

   The last flex child of the chamfered box's inner layer, which is already a
   centred flex row, so the mark needs no positioning of its own. `flex: none`
   is the whole rule's job: the value's `flex: 1` would otherwise take the 13px
   back, and the square itself is SweepSize::Button rather than anything set
   here.

   10px, the same gap the in-flight button puts between its sweep and its label
   (rd-loading.jsx:197).

   ONE rule for every mount. It replaces `.rd-af__search-sweep` (s14-add),
   `.rd-find__sweep` (s16) and `.rd-plat__sweep` (adm-plat) — the same four
   declarations written three times, which is the whole argument for the
   component. */
.rd-search__sweep {
  flex: none;
  display: flex;
  align-items: center;
  margin-left: 10px;
}

/* ⚠️ THE EXPLICIT SEARCH CONTROL — James, 2026-08-25, CONSOLE ONLY.

   `submit_label: Some(..)` draws an RdButton beside the box. The batch-11
   ruling ("one search behavior, not two") is untouched and is still the
   default: no shooter mount sets the prop, and a `ruling_` sweep in
   `search_field.rs` pins that mechanically. The Org Library takes the deviation
   because it is Enter-commit for a reason no debounce can pay for (its reset
   rewinds the server cursor, A6 §9.2), which leaves Enter as the ONLY
   affordance — the exact thing the ruling's own parenthesis forbids.

   A MODIFIER, not the base rule. `.rd-search` is `display: block` at the other
   twelve mounts and a permanent flex row would relayout all of them for a
   control they do not draw.

   ⚠️ THE TWO CHILD RULES ARE SPECIFICITY ARITHMETIC, NOT DECORATION — do not
   flatten them. `adm-lib.css` caps the field's one child with
   `.rd-adm-lib__search > *` at (0,0,1,0): `flex: 1` and `max-width: 340px`,
   written when the box WAS the only child. Undefended, the button inherits that
   cap and the two children split the row down the middle. These compounds are
   (0,0,2,0) and outrank it from either side of the cascade. The box keeps the
   340 cap on purpose — nothing here sets `max-width` on it, so the drawn width
   survives and only the button escapes.

   ⚠️ A DRAWN LABEL BESIDE A DRAWN CONTROL IS UNDRAWN (A6 Q5): the flex row
   would put the label beside the box rather than above it. No mount does both,
   and no layout is invented here for a combination nobody has asked for. */
.rd-search--submit {
  display: flex;
  align-items: center;
  gap: 10px;
}
/* ⚠️ THE CHILD IS `.rd-field__hit`, NOT `.rd-box` — the hit-area label now sits
   between the wrapper and the box, so the old `> .rd-box` matched nothing and
   the field lost its `flex: 1`. Named here rather than loosened to a descendant
   selector: the point of the compound is to outrank `.rd-adm-lib__search > *`
   at (0,0,2,0), and a descendant selector would still be (0,0,2,0) but would
   also reach a `.rd-box` nested anywhere below. */
.rd-search--submit > .rd-field__hit { flex: 1; min-width: 0; }
.rd-search--submit > .rd-btn { flex: none; max-width: none; }
/* ==== ADM2 · 33 Pro Org Settings (design-corpus-2026-08-10) · owner: lane-2 ==== */

/* ---------------------------------------------------------------------------
   The settings page: four sections ordered harmless -> irreversible.
   ⚠️ 2026-08-10 export: `SetSection` moved to rd-pro-settings.jsx:32 and its
   signature changed (`ownerOnly, viewerIsOwner`); the owner chip at :31 is gone.
   That surface belongs to another lane — flagged for the head's realignment
   routing rather than repointed here.

   Drawn: rd-pro-settings.jsx:26-104 (SetSection, SetRow, DiscoverRow,
   ScreenOrgSettings). No ADM2 spec exists — the drawing is the source.

   Rhythm is tight on purpose, and the drawing says why: the sections have to
   fit 800px of laptop, and the first draft pushed "Close this org" 42px past the
   bottom edge — the one control the document argues hardest about was the one
   nobody could reach.

   ⚠️ The count is FOUR and the pinned snapshot above still draws FIVE. The Plan
   block was deleted by ruling; the corpus moved upstream and our citations stay
   pinned until a re-sync, which is its own process. Do not "correct" this back
   from the drawing.
   --------------------------------------------------------------------------- */
.rd-set { padding: 20px 32px 24px; max-width: 860px; }

/* Banners own their padding and their bottom gap (`p1.css`, THE SLOT RULING
   2026-08-28), so nothing is spaced here any more. The rule survives as `0`
   rather than being deleted: it is the slot these three screens name, and a
   deleted class is a class the next author re-invents with a number in it. */
.rd-set__banner { margin: 0; }

.rd-set-sec { margin-bottom: 22px; }

.rd-set-sec__head { display: flex; align-items: baseline; gap: 12px; margin-bottom: 11px; }

.rd-set-sec__title {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 75%; letter-spacing: .15em;
  font-size: 10.5px; color: var(--rd-fg3);
}

/* The owner mark. Accent TEXT and an accent BORDER — never a fill, so --rd-acc
   is correct in both themes (ADM0 §3's volt/acc split, pointed the other way
   from ProTop's action). */
.rd-set-sec__owner {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .11em;
  font-size: 8.5px; color: var(--rd-acc);
  border: 1px solid var(--rd-acc);
  padding: 3px 7px;
}

/* A row. The 168px key column is what makes the sections read as one table
   rather than several; it does not flex. */
.rd-set-row {
  display: flex; align-items: flex-start; gap: 20px;
  padding: 11px 0;
  border-bottom: 1px solid var(--rd-line);
}
.rd-set-row--last { border-bottom: none; }

.rd-set-row__key {
  width: 168px; flex: none; padding-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px; color: var(--rd-fg3);
}

.rd-set-row__val { flex: 1; min-width: 0; }

.rd-set-row__v {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px; color: var(--rd-fg);
}

.rd-set-row__sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px; color: var(--rd-fg3);
  margin-top: 5px; line-height: 1.5;
}

.rd-set-row__act { flex: none; }

/* ---------------------------------------------------------------------------
   The Logo row — UNDRAWN. `33 (Desktop) Pro Org Settings` draws no logo row,
   and its Decided Here section refused one by name on the grounds that no such
   column existed anywhere in the schema. James supplied the column on
   2026-08-28, which removes that premise rather than disagreeing with the
   reasoning; the row is built at full fidelity in Identity and its copy is
   flagged for a ruling.

   ⚠️ NO NEW PART. The MARK's own rules live in `adm0-chrome.css` and belong to
   the OrgMark slice; these are the settings ROW's, so they live with the rest
   of this screen. The part count is a hand-updated pin in `cli/src/css.rs`, and
   a new file would move it.

   Geometry is the drawn mark's, quoted rather than invented: a 34px box
   (`org_mark.rs`'s DEFAULT_SIZE; rd-pro-kit.jsx:108).

   ⚠️ NO CHAMFER, and no ground. James ruled on 2026-08-28 that an image REPLACES
   the chamfered box rather than fitting within it, so the rail's OrgMark drops
   the box on its logo branch — and this slot previews what the rail will draw.
   A chamfered, accent-stroked preview of an unchamfered mark would be a preview
   of something that does not exist. `.rd-set-logo__face` and the `.rd-box` sizing
   rule that went with it are DELETED for that reason, not orphaned.
   --------------------------------------------------------------------------- */
.rd-set-logo { flex: none; width: 34px; height: 34px; background: none; border: 0; }

/* `contain`, not `cover`, which is the other half of the same ruling: whatever
   the operator uploads is what we use. The client no longer crops either — it
   scales the whole picture to fit inside 144² and keeps its aspect ratio — so a
   letterbox here is the picture's real shape, drawn against the row's own
   ground rather than baked into the stored bytes. */
.rd-set-logo__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* Two acts in a slot the drawn rows give one. Baseline-aligned rather than
   centred: both are the accent-underlined word treatment, and centring would
   float them off the row's first line. */
.rd-set-logo-acts { display: flex; align-items: baseline; gap: 16px; }

/* A `<label>` takes no `:focus` — the file input inside it does, and the ring
   has to land on the thing a sighted keyboard user sees. The same mechanism
   `.rd-avup__btn` carries on the shooter's photo control, for the same
   structural reason: a `<button>` cannot open a file dialog for an input it
   does not contain, so the act that picks a logo is a label and not a button.

   ⚠️ TYPE-SCOPED TO THE LABEL, and the bare selector was a bug. `.rd-set-row__edit`
   is EVERY action on this screen — Edit, Preview, Transfer, Remove — and those
   are `<button>`s, which take the `:focus-visible` ring at the foot of this part.
   `:focus-within` matches an element that merely CONTAINS focus and, on a button,
   is satisfied by the button itself: a mouse click drew a ring the drawing does
   not give a pointer user. Only the file-picker label needs this arm, so only the
   label gets it. Offset 3px to match the `:focus-visible` rule below — one ring on
   one screen, at one distance. */
label.rd-set-row__edit:focus-within {
  outline: 2px solid var(--rd-acc);
  outline-offset: 3px;
}

/* The inert action slot — a sentence where a control would go. Deliberately NOT
   the drawn accent-underlined action treatment: this text is not a control and
   must not read as one. Quiet body copy in the tertiary, no underline, no
   pointer. See `inert_note` for why a disabled button is the wrong lie. */
.rd-set-row__inert {
  display: inline-block; max-width: 34ch;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px; color: var(--rd-fg3);
  line-height: 1.5; text-align: right;
}

/* ---------------------------------------------------------------------------
   The discoverability toggle.

   ⚠️ border-radius is drawn here and nowhere else in the kit. The substrate
   rules chamfers over radii for every box; this is a switch track and a knob,
   drawn round at :61-62, and a chamfered switch would be a different control.
   Followed as drawn, and flagged so the next reader does not "correct" it.
   --------------------------------------------------------------------------- */
.rd-set-toggle-row { display: flex; align-items: center; gap: 11px; }

.rd-set-toggle {
  width: 38px; height: 21px; flex: none;
  box-sizing: border-box;
  display: flex; align-items: center;
  padding: 2px;
  border-radius: 11px;
  cursor: pointer;
}

/* OFF, for both viewers: hollow track, control-boundary stroke, knob in the
   same grey. The knob sits left. */
.rd-set-toggle--off {
  background: transparent;
  border: 1.5px solid var(--rd-ctrl);
  justify-content: flex-start;
}
.rd-set-toggle--off .rd-set-toggle__knob { background: var(--rd-ctrl); }

/* ON, owner: the live state, in the accent, knob right. */
.rd-set-toggle--on-owner {
  background: var(--rd-acc);
  border: 1.5px solid var(--rd-acc);
  justify-content: flex-end;
}
.rd-set-toggle--on-owner .rd-set-toggle__knob { background: var(--rd-on-volt); }

/* ON, admin — filled but unreachable. ⚠️ **A disabled control that is ON must
   still LOOK on** (ADM0 §5.2): the filled track is the state, knob position
   alone is not. Quieter than the owner's — a chip fill and a control-boundary
   stroke — but never hollow, because the setting is ONE fact and both viewers
   must read it identically. Drawing the admin's copy as OFF was the first
   version of this frame, and it quietly claimed two different orgs. */
.rd-set-toggle--on-admin {
  background: var(--rd-chip);
  border: 1.5px solid var(--rd-ctrl);
  justify-content: flex-end;
}
.rd-set-toggle--on-admin .rd-set-toggle__knob { background: var(--rd-fg3); }

.rd-set-toggle__knob {
  width: 15px; height: 15px; flex: none;
  border-radius: 50%;
}

.rd-set-toggle:disabled { cursor: default; }
.rd-set-toggle:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 3px; }

.rd-set-toggle__state {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px; color: var(--rd-fg);
}

/* THE FACT IS ON THE WIRE. `43 Loading States.html:53` keeps the sentence and
   puts the 13px sweep beside it — the same `SweepTone::Accent` treatment
   `.rd-set-row__save--inflight` above draws, for the same reason: accent TEXT
   on a surface, not a fill. No colour change here (unlike the save button):
   this span is never greyed by `disabled`, so there is no "you can't" state to
   guard against — only the row layout, which needs the inline-flex to seat the
   mark on the same baseline as the sentence it does not resize. */
.rd-set-toggle__state--inflight {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
}

/* ---------------------------------------------------------------------------
   Ending it. The danger-underlined text affordance — a div with a bottom border
   in the drawing.

   ⚠️ IT IS A <button> NOW AND THE TREATMENT IS UNCHANGED. It was a <span>
   because there was no Close RPC to give it; slice C built one, so the element
   follows what it does. The resets below (background/border/padding/cursor) are
   what keep a real button looking like the drawn text — everything a user agent
   adds to a <button> has to come off, and `border-bottom` has to go back on
   after `border: 0` or the underline goes with it.

   The --inert form is the ADMIN's, and the owner-only mark above it is the rest
   of that sentence: the word without the door, because a row that vanishes
   teaches nobody where the permission lives. */
.rd-set-end__act {
  display: inline-block;
  background: none;
  border: 0;
  padding: 0 0 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px; color: var(--rd-danger);
  border-bottom: 1px solid var(--rd-danger);
  cursor: pointer;
}
.rd-set-end__act:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 3px; }

/* Not a control: no pointer, and the underline goes. The word stays in the
   danger colour because it names the same act — it is the block's subject, and
   greying it would read as a control that is off rather than as a fact about
   where the permission lives. */
.rd-set-end__act--inert { cursor: default; border-bottom: 0; padding-bottom: 0; }

.rd-set-end__note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px; color: var(--rd-fg3);
  line-height: 1.5; max-width: 62ch; margin-top: 10px;
}


/* ⚠️ THE MEMBERS AND INVITES RULES LEFT WITH THEIR SCREENS, and the classes are
   deliberately NOT named here. The card, table, select, inline-confirm and
   invite-form blocks that stood between here and the editing state below served
   two tabs this screen no longer has: `34 Pro Members` gave the org's people
   their own destination, and their CSS went with them to `adm-mem.css` under
   its own names. Spelling the retired class names in this comment would put
   them back inside a source-text scan — the mechanism `set_section`'s doc
   describes one file over, where a dead rule stayed green because its class was
   still written in a branch no caller could reach. A tombstone that hides the
   grave is worse than no tombstone. */

/* ---------------------------------------------------------------------------
   Identity's editing state — the drawn `SetRowEdit`.

   The field is accent-bordered on the input fill; Cancel is quiet and Save
   carries the accent underline. That RANKING is the drawing's, not a taste: one
   of the two is the act and the other is the way out, and equal weight would
   make the way out look like an equal choice.

   The key column shifts to `padding-top: 10px` while editing (from 2) so the
   label sits on the field's first line rather than above it — the drawn value.

   ⚠️ `--editing` does NOT touch the hairline: the drawn row takes the same
   `last` prop at rest and while editing, so it composes with `--last` instead of
   overriding it. A row that suppressed its own border here would lose the rule
   under an open Name editor; one that forced it would draw a rule under an open
   Description editor that the resting row does not have.
   --------------------------------------------------------------------------- */
.rd-set-row--editing { align-items: flex-start; }
.rd-set-row__key--editing { padding-top: 10px; }

.rd-set-row__field {
  width: 100%;
  box-sizing: border-box;
  background: var(--rd-field);
  border: 1px solid var(--rd-acc);
  color: var(--rd-fg);
  padding: 9px 12px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
}

/* ⚠️ The FRAME is drawn now and the ELEMENT still is not. `33 Pro Org Settings`
   carries an *editing a row* frame, generic to either Identity row, so the box
   above is no longer a pattern borrowed from a sibling — it is the drawn box.
   What no drawing carries is a multi-line field, and `RdTextField` has no
   multiline variant by ruling, so these three declarations remain the one
   departure: everything else here is inherited, and only the element changed. */
.rd-set-row__field--area { line-height: 1.55; resize: vertical; min-height: 76px; }

/* Same pre-kit backfill defence the other two fields needed: attribute
   selectors at (0,0,1,1) beat a bare class at (0,0,1,0) and would paint a blue
   focus ring inside an accent-bordered box. This compound is (0,0,2,0).
   ⚠️ Do NOT strip when the retokenisation lands — a defence outranking a
   now-correct default is harmless; removing it early leaves a gap. */
.rd-set-row__val .rd-set-row__field {
  box-shadow: none;
  outline: none;
}

.rd-set-row__field:focus { border-color: var(--rd-acc); }

.rd-set-row__editacts {
  flex: none;
  display: flex;
  align-items: center;
  gap: 16px;
  padding-top: 9px;
}

.rd-set-row__cancel,
.rd-set-row__save,
.rd-set-row__edit {
  background: none;
  border: 0;
  padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  cursor: pointer;
}

.rd-set-row__cancel { color: var(--rd-fg3); }

.rd-set-row__save,
.rd-set-row__edit {
  color: var(--rd-acc);
  border-bottom: 1px solid var(--rd-acc);
  padding-bottom: 2px;
}

/* Nothing typed yet, so nothing to send: the underline goes and the word greys.
   ⚠️ NOT the in-flight state — see the modifier below, which narrows this. */
.rd-set-row__save:disabled {
  color: var(--rd-fg3);
  border-bottom-color: transparent;
  cursor: default;
}

/* THE SAVE IS ON THE WIRE. `43 Loading States.html:53` keeps the label and puts
   the 13px sweep beside it; rd-loading.jsx:191 says why the colour stays: "grey
   is DISABLED ('you can't'), in-flight is 'it's going'." A greyed word with an
   accent dash next to it would say both at once — and the sweep here draws in
   `--rd-acc`, so the ink has to match the type it joins.

   `pending` still sets the native `disabled` attribute (input stops, as ruled),
   so the rule above fires and this restores the accent over it. SPECIFICITY,
   NOT ORDER: 0,3,0 against 0,2,0. `cursor` is deliberately not restated — a
   save already sent is still not pressable.

   The row is what seats the mark: `inline-flex` because the button is
   shrink-to-fit inside `.rd-set-row__editacts`, and `baseline` rather than
   `center` so the 13px square sits on the same line the underline draws under.
   No padding or size change — §5.5's invariant. */
.rd-set-row__save--inflight {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
}

.rd-set-row__save.rd-set-row__save--inflight:disabled {
  color: var(--rd-acc);
  border-bottom-color: var(--rd-acc);
}

.rd-set-row__cancel:focus-visible,
.rd-set-row__save:focus-visible,
.rd-set-row__edit:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 3px; }

/* ---------------------------------------------------------------------------
   The Public Page preview — rd-pro-settings.jsx (ScreenPublicPage).

   Same part file as the settings screen above, deliberately: one drawing
   (`33 Pro Org Settings`), one owner, one section. A second part for a second
   screen out of the same document would put two banners and two order.txt lines
   between rules that share tokens, a rhythm and a reviewer.

   ⚠️ THE PREVIEW BOX PAINTS NO CARD OF ITS OWN, and that is the argument
   rather than an omission. What sits inside it is `components/org_public.rs`,
   the SAME renderer the shooter meets at /orgs/:id — the drawing's own rule is
   "not a second renderer, which is where a contract stops being enforced in one
   of them." That renderer has not been re-skinned yet, so the frame below is a
   window onto the page as it actually ships, not a Pro-side restaging of it.
   Styling its interior from here would be exactly the second copy the rule
   forbids, one layer down.

   ⚠️ THE DRAWN CARD ALSO CARRIES AN ORG MARK AND A `Since <year>` LINE, and
   neither is here. Both are page content, and the year is not on the wire this
   crate reads (`PublicOrgDto` does not model `created_at`, though the proto
   declares it). Flagged rather than improvised — see public_page.rs.
   --------------------------------------------------------------------------- */
/* ⚠️ NO `flex: 1; min-height: 0`, and the drawing has both. Those exist to fit
   a mock to a fixed 800px canvas; here the phone frame's height is DATA — an
   org with a long drill list makes it taller than the viewport — and a flex
   item that fills its parent with `overflow: visible` puts the tail of that
   list past the bottom edge with nothing to scroll. `.rd-set` one screen up
   flows and lets the document scroll, on this same chrome, and it is right.
   Same class of defect the drawing itself records for the settings screen,
   where the first draft pushed "Close this org" 42px past the bottom. */
.rd-pub {
  display: flex; gap: 34px;
  padding: 26px 32px 32px;
}

/* The left column: what the page carries, and the one door onto it. Capped at
   460px because the copy under it is measured in ch and a full-width column
   would run the contract sentence past a comfortable line. */
.rd-pub__col { flex: 1; min-width: 0; max-width: 460px; }

.rd-pub__lab {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .11em;
  font-size: 9px; color: var(--rd-fg3);
  margin-bottom: 8px;
}

.rd-pub__name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 14px; color: var(--rd-fg);
  margin-bottom: 20px;
}

.rd-pub__desc {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px; color: var(--rd-fg2);
  line-height: 1.55; max-width: 52ch;
}

/* The one door onto Name and Description. Accent text with an accent underline
   — the drawn action treatment, and the same one `.rd-set-row__edit` carries on
   the settings screen, because it is the same act reached from the other side.
   A real button, so it takes the button reset. */
.rd-pub__edit {
  display: inline-block;
  margin-top: 20px;
  background: none; border: 0; padding: 0 0 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px; color: var(--rd-acc);
  border-bottom: 1px solid var(--rd-acc);
  cursor: pointer;
}
.rd-pub__edit:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 3px; }

.rd-pub__note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px; color: var(--rd-fg3);
  margin-top: 24px; line-height: 1.6; max-width: 58ch;
}

.rd-pub__side { flex: none; }

.rd-pub__plab {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .11em;
  font-size: 9px; color: var(--rd-fg3);
  margin-bottom: 10px;
}

/* The phone. 320px is drawn and is the point — a shooter meets this page on a
   phone, and previewing it at desktop width would preview a layout nobody sees.
   The drawn HEIGHT is a fixed 470px; it is a min-height here because the real
   page's length is data, and a fixed box would crop an org with a full drill
   list rather than show it. `position: relative` is load-bearing: it makes this
   the containing block for the veil below. */
/* ⚠️ A FLEX COLUMN, and that is what makes the shared card behave in here.
   The shooter shell gives `.rd-org` a flex parent with a height, which is what
   its `flex: 1` grows into and what makes the locked door's `margin-top: auto`
   pin to the foot rather than trail the drill list. A block parent here would
   leave both inert: the door would sit wherever the list happened to end, and
   the preview would show a layout no shooter gets — a preview lying about
   spacing is a quieter lie than one lying about content, and still a lie.
   Nothing about the card knows it is in a preview; the FRAME supplies the
   context the shell supplies on the other surface. */
.rd-pub-frame {
  position: relative;
  box-sizing: border-box;
  width: 320px; min-height: 470px;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--rd-line);
  background: var(--rd-bg);
  padding: 20px;
}

/* The frame, waiting. `43 Loading States.html:52` puts the 26px no-claim sweep
   "centred on empty ground", and `.rd-pub-frame` above is a flex COLUMN with no
   alignment of its own — so a bare 26px svg would sit in its top-left corner,
   which is the one place the ruling does not put it. `margin: auto` on this
   wrapper is what centres it on both axes inside the 320 x 470 box; the frame's
   `min-height` is what gives the auto margins room to work.

   `RdEmptyState` next door needs no wrapper because it centres its own text;
   the sweep is a fixed-size mark and carries no such rule. */
.rd-pub-frame__wait {
  margin: auto;
}

/* The not-findable veil. ⚠️ Absolute inside the frame, not fixed to the
   viewport — the same decision `.rd-ev-confirm` records in ADM1-PR2: the rail,
   the page head and the left column all stay lit, because this covers ONE
   object on the screen and says something about that object.

   The literal scrim value is the drawing's (`rgba(6,6,6,.86)`) and matches the
   .82 scrims ADM1 already ships. Not a token: it is a scrim over an arbitrary
   ground rather than a surface, and a token that only ever appears under an
   overlay would be a colour pretending to be a role. */
.rd-pub-veil {
  position: absolute; inset: 0; z-index: 20;
  background: rgba(6, 6, 6, .86);
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
}

.rd-pub-veil__mid { text-align: center; }

.rd-pub-veil__title {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 75%; letter-spacing: .14em;
  font-size: 10px; color: var(--rd-warn);
  margin-bottom: 12px;
}

/* ⚠️ THE VEIL CARRIES `data-theme="dark"` IN THE MARKUP, AND WITHOUT IT THIS
   TEXT IS UNREADABLE IN THE LIGHT THEME. The scrim is a fixed near-black in
   both themes, matching the .82 scrims ADM1 already ships — so the two rules
   above resolve --rd-warn and --rd-fg2 against whichever theme is in force, and
   in light those are #7A4E00 and #3F4039: dark type on near-black.

   The fix is the mechanism `RdThemeSwatch` already documents rather than a new
   one: an element carrying `data-theme` rebinds every --rd-* below it, and each
   token block defines the COMPLETE set precisely so a re-scoped subtree cannot
   leak half its colours in from outside. The veil is a dark surface wherever it
   appears, so it stands in the dark scope and its two lines take dark-theme
   values. Same shape as the swatch's miniature: two colour systems on screen at
   once, each correct for the ground it is painted on.

   White-on-white's twin, and no unit test can see it — a computed ground is not
   in any snapshot. Do not drop the attribute. */
.rd-pub-veil__body {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px; color: var(--rd-fg2);
  line-height: 1.6;
}

/* ---------------------------------------------------------------------------
   The two owner-only acts — rd-pro-settings.jsx (ScreenTransfer, ScreenCloseOrg).

   Same part file as the settings screen and the preview above, for the reason
   the preview's own block states: ONE DRAWING (`33 Pro Org Settings`), one
   owner, one section. A separate part per screen out of one document would put
   two banners and two order.txt lines between rules that share tokens, a rhythm
   and a reviewer — and these two are the pair the document argues hardest about
   keeping apart, which is an argument for reading them side by side rather than
   in different files.

   ⚠️ TWO PREFIXES, NOT ONE, AND THAT IS THE POINT. `rd-own-*` and `rd-close-*`
   share not one rule. The drawn screens differ in every measure that matters —
   720px against 680px, a volt act against a dfill one, a picker against a
   ceremony — and a shared base class would be the first step back towards the
   single "ending it" surface the drawing split in two.
   --------------------------------------------------------------------------- */

/* ── Transfer ─────────────────────────────────────────────────────────────
   ⚠️ **CORRECTED 2026-08-25.** This read: "`position: relative` is load-bearing:
   `RdConfirmModal`'s scrim is absolutely positioned and resolves against the
   nearest positioned ancestor, so without it the scrim escapes to the viewport
   and covers the rail too. The drawing puts the confirm over the picker and
   leaves the rail and the head lit."

   `.rd-confirm` is `position: fixed` now — see adm1-5-1.css for the mechanism
   (an absolute scrim centred its panel on the console's one scrolling region,
   not on the viewport) and for the drawn-scrim-scope deviation that costs. The
   declaration is kept as the reversal path, not as a live contract. */
.rd-own {
  position: relative;
  padding: 26px 32px 32px;
  max-width: 720px;
}

.rd-own__banner { margin: 0; }

.rd-own__lede {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px; color: var(--rd-fg2);
  line-height: 1.65; max-width: 64ch;
  margin: 0 0 26px;
}

.rd-own-list { display: block; }

/* One admin. A real <button> in a radiogroup rather than an <input type=radio>:
   the whole row is the target at this size, and the drawn dot is a 16px circle
   with a 1.5px edge that no user-agent radio renders. */
.rd-own-row {
  display: flex; align-items: center; gap: 14px;
  width: 100%; box-sizing: border-box;
  padding: 14px 0;
  background: none; border: 0;
  border-bottom: 1px solid var(--rd-line);
  text-align: left; cursor: pointer;
}
.rd-own-row:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* ⚠️ THE CHOSEN ROW IS MARKED BY ITS DOT, NOT BY A FILL. The drawing changes
   only the dot; a highlighted row would compete with the accent it uses for the
   dot itself, and on a list of two admins the pair would read as two states of
   the same control rather than one choice between them. Declared empty-handed
   on purpose — see the dot below. */
.rd-own-row--on { background: none; }

.rd-own-dot {
  width: 16px; height: 16px; flex: none;
  border-radius: 50%;
  border: 1.5px solid var(--rd-ctrl);
  background: transparent;
}
.rd-own-dot--on { border-color: var(--rd-acc); background: var(--rd-acc); }

.rd-own-who { flex: 1; min-width: 0; display: block; }

.rd-own-name {
  display: block;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 14px; color: var(--rd-fg);
}

.rd-own-since {
  display: block;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px; color: var(--rd-fg3);
  margin-top: 4px;
}

.rd-own-note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px; color: var(--rd-fg3);
  line-height: 1.55; max-width: 62ch;
  margin: 18px 0 0;
}

/* The act. Volt fill, chamfer 7 — the drawn primary control, and the same
   treatment `.rd-mem-send` carries one screen over. */
.rd-own-go {
  margin-top: 26px;
  background: var(--rd-volt);
  color: var(--rd-on-volt);
  border: 0;
  padding: 11px 20px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 11px;
  cursor: pointer;
  clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
}
.rd-own-go:disabled { background: var(--rd-surf2); color: var(--rd-fg3); cursor: default; }
.rd-own-go:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 3px; }

/* The one door this screen grows that the drawing does not, with two
   destinations: out to Members when nobody can receive the org, and back to Org
   Settings once somebody has. ONE CLASS, because it is one affordance and the
   word is the parameter — the argument `row_action` makes for Edit and Preview
   on the settings screen, where a second near-identical rule would have been
   the same treatment wearing a different name. They never appear together.

   The accent-underline treatment the settings rows use for an action, not the
   primary fill: neither destination is the act of this screen. */
.rd-own__door {
  display: inline-block;
  margin-top: 22px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px; color: var(--rd-acc);
  border-bottom: 1px solid var(--rd-acc);
  padding-bottom: 2px;
  text-decoration: none;
}
.rd-own__door:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 3px; }

/* ── Close Org ────────────────────────────────────────────────────────────
   680px against Transfer's 720: the drawing's own measures. This screen is
   narrower because every line on it is a sentence to be read rather than a row
   to be scanned. */
.rd-close {
  padding: 26px 32px 32px;
  max-width: 680px;
}

.rd-close__banner { margin: 0; }

.rd-close__lede {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px; color: var(--rd-fg2);
  line-height: 1.65; max-width: 62ch;
  margin: 0 0 24px;
}

/* The impact block. A bordered box, because the counts are the one part of this
   screen that is data rather than argument. */
.rd-close-impact {
  border: 1px solid var(--rd-line);
  padding: 20px 22px;
  margin-bottom: 22px;
}

.rd-close-impact__row { display: flex; gap: 14px; align-items: baseline; padding: 9px 0; }

/* 200px is the drawing's, and it is a fixed column rather than content width so
   the two consequence sentences start on the same axis. `318 members` and
   `46 events` are different lengths and a shrink-to-fit column would stagger
   them. */
.rd-close-impact__n {
  width: 200px; flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px; color: var(--rd-fg);
}

.rd-close-impact__what {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px; color: var(--rd-fg3);
  line-height: 1.5;
}

/* UNDRAWN: the counts did not load. Inside the box rather than replacing it —
   an impact block that vanishes reads as "nothing is affected". */
.rd-close-impact__unknown {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px; color: var(--rd-fg3);
  line-height: 1.55; margin: 0;
}

.rd-close__keeps {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px; color: var(--rd-fg2);
  line-height: 1.5; margin: 0 0 22px;
}

.rd-close__lab {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .11em;
  font-size: 9px; color: var(--rd-fg3);
  margin-bottom: 8px;
}

.rd-close__field {
  display: block;
  box-sizing: border-box;
  max-width: 320px; width: 100%;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  color: var(--rd-fg);
  padding: 11px 13px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
}
/* The same pre-kit backfill defence the Identity fields needed: attribute
   selectors at (0,0,1,1) beat a bare class at (0,0,1,0) and would paint a blue
   focus ring inside this box. This compound is (0,0,2,0). */
.rd-close .rd-close__field { box-shadow: none; outline: none; }
.rd-close__field:focus { border-color: var(--rd-acc); }

/* ⚠️ NO CHAMFER, AND THAT IS DRAWN. Every other primary act in Pro is chamfered
   — `.rd-own-go` above, `.rd-mem-send`, `ProTop`'s action. This one is a plain
   rectangle on the destroy fill, and the difference is the last thing between
   an owner and the end of their org. */
.rd-close__go {
  /* The in-flight sweep sits BESIDE the label, which does not change
     (43 Loading States.html:53) — so the row needs an alignment and a gap it
     never needed while the word was swapped for another word. `inline-flex`
     rather than `flex`: the control is shrink-to-fit and must stay that way. */
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 22px;
  background: var(--rd-dfill);
  color: #fff;
  border: 0;
  padding: 11px 20px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 11px;
  cursor: pointer;
}
/* UNARMED only — the name is not typed, so there is nothing to send.
   ⚠️ NOT the in-flight state; the modifier below narrows this rule. */
.rd-close__go:disabled { background: var(--rd-surf2); color: var(--rd-fg3); cursor: default; }

/* THE CLOSE IS ON THE WIRE, and the fill stays.
   round-7-loading-rulings-reply.md §1: "on a danger fill the sweep draws in the
   danger fill's foreground ink, trackless, 13px, label kept" — a statement that
   the fill is still there while the request is open. This site already draws the
   OnDanger mark; without this rule the mark sat on a --rd-surf2 grey, which is
   the "you can't" paint over an act that is going. rd-loading.jsx:191: "grey is
   DISABLED ('you can't'), in-flight is 'it's going'."

   `closing` still sets the native `disabled` attribute — input stops, and a
   close already sent must not be sendable twice — so the rule above fires and
   this restores the ground over it. SPECIFICITY, NOT ORDER: 0,3,0 against 0,2,0.
   `cursor` is deliberately not restated. Colour only: the base rule is already
   the inline-flex row with the gap that seats the mark, so nothing here changes
   a box (§5.5). #fff is the literal .rd-sweep--on-danger strokes, so the mark
   matches the type beside it. */
.rd-close__go.rd-close__go--inflight:disabled { background: var(--rd-dfill); color: #fff; }
.rd-close__go:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 3px; }
/* ==== ADM-MEM · 34 Pro Members (design-corpus-2026-08-13) · owner: lane-2 ==== */

/* ---------------------------------------------------------------------------
   The org's people: one list, one composer.

   Drawn in `34 Pro Members` — Pro · Members, Pro · Members · changing a role,
   Pro · Members · selection, Pro · Members · Invited, Pro · Members · empty,
   Pro · Members · remove, Pro · Invite People.

   ⚠️ A NEW PART RATHER THAN MORE OF ADM2, and the split follows the drawings.
   These rules used to be `.rd-set-*` inside the org-settings section, because
   the members table was a tab on that screen. Two documents now draw two
   destinations, and a section file that held both would be the one place in the
   system where the CSS still said they were one page.

   ⚠️ NO COLUMN HEADERS, WHICH IS WHY THERE IS NO TABLE. The drawn rows are
   self-describing — the role word, `Joined Mar 2024`, `Remove` — so there is no
   header for a `<th>` to associate a cell with, and flex rows are what the
   markup uses. The predecessor was a real `<table>` for the opposite and
   equally good reason: it HAD headers. The element follows the drawing.

   The column widths are the drawing's: 170px for the role, 150px for the date,
   and the name takes what is left.
   --------------------------------------------------------------------------- */
.rd-mem { padding: 0 0 24px; }

/* ⚠️ THIS RULE IS THE BUG JAMES REPORTED, AND ONLY HALF OF IT WAS WRONG.
   Reported on staging `/admin/members/invite`: the *"12 invitations sent."*
   strip touched the `EMAIL ADDRESSES` label under it, and sat ~32px inside the
   left edge every other block on that screen aligns to.

   The two halves, and they have different causes:

     - NO GAP BELOW — the `0`. That is fixed in the primitive, not here:
       `.rd-banner` carries `--rd-banner-gap` itself now (THE SLOT RULING,
       `p1.css`). The bottom slot is deliberately left at `0` in this rule so
       there is exactly one declaration of the gap on this screen.
     - THE 32px INSET — this rule is mounted on TWO screens with two different
       columns, and it was written for one of them. On the LIST
       (`.rd-mem { padding: 0 0 24px }`) there is no horizontal page padding, so
       the 32 IS the console gutter and the banner lands on the same left edge
       as `.rd-pfilter--band` and the rows. On the COMPOSER
       (`.rd-mem-compose { padding: 24px 32px 32px }`) the column ALREADY
       carries that gutter, so the 32 is applied twice and the strip sits 32px
       inside everything around it. Corrected below, at the composer's own
       column rather than by re-spelling the gutter here.

   The gap the banner does NOT own is the one ABOVE it, because that is a
   property of what precedes it on each screen. It names the token so the two
   ends of the slot cannot drift apart. */
.rd-mem__banner { margin: var(--rd-banner-gap) 32px 0; }

/* The head's action. `ProTop` renders whatever element it is handed, and this
   one is a `Link`: volt fill, chamfer 7, matching every other primary action in
   Pro. Volt and not `--rd-acc`, because in light mode `acc` is the deep warm brown
   reserved for accent TEXT and puts black on 2.3:1 as a fill. */
.rd-mem-invite {
  display: inline-block;
  background: var(--rd-volt);
  color: var(--rd-on-volt);
  padding: 10px 18px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 11.5px;
  text-decoration: none;
  clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
}
.rd-mem-invite:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 3px; }

/* ---------------------------------------------------------------------------
   Search, chips, count — the toolbar the drawing leads with.

   ⚠️ THE BAR IS `ProFilterBar` NOW (batch 15 slice 8), so four rules left this
   file and nothing replaced them here:

     .rd-mem-bar        → .rd-pfilter --band       (the same padding: 16px 32px 14px)
     .rd-mem-searchwrap → .rd-pfilter__field --w340 (the same flex: 1 1 240px / 340 cap)
     .rd-mem-filters    → .rd-pfilter__chips
     .rd-mem-filter(--on) → .rd-pfilter__chip(--on)
     .rd-mem-count      → .rd-pfilter__count

   ⚠️ THE 340 CAP MOVED — IT WAS NOT COPIED. `17-pro-filter-bar.md` §2 names this
   exact trap: the field's own cap is DELETED when the input moves into the slot,
   or the sheet carries two max-widths able to disagree, which is the defect
   `14-keypad.md` §3 refused for the pad's width. `.rd-mem-search` below is what
   survives, and it is a FONT SIZE and not a width.
   --------------------------------------------------------------------------- */

/* ⚠️ **THE BOX IS NOT HERE ANY MORE — Q2, `A6-search-field.md` §10.** This rule
   used to draw a plain 1px bordered rectangle with its own fill, padding and
   focus colour. That shape is NOT drawn: `34 (Desktop) Pro Members` describes
   the search in prose. It is the kit's chamfered box now, on the grounds that
   an undrawn field should look like every other field rather than like itself.
   The placeholder colour and the focus stroke went with it (`au5.css`).

   What survives is the one fact this console still owns: 13px type, a step down
   from the kit field's 16. `au5.css` sits at 317 in order.txt and this part at
   319, so a single class here beats a single class there on a tie. */
.rd-mem-search {
  font-size: 13px;
}

/* ---------------------------------------------------------------------------
   The list.

   ⚠️ THE ROWS ARE `ProTable` / `ProRow` NOW (batch 15 slice 8):

     .rd-mem-list → .rd-ptable                 (display: flex; flex-direction: column)
     .rd-mem-row  → .rd-prow + .rd-ptable--dens-13
                    (gap 18, padding 13px 0, the 1px --rd-line hairline)

   The four column widths went with them, into `member_columns()` /
   `invite_columns()` in `members.rs`: 170 for the role, 150 for the date, 220
   for the act, and the name takes what is left. THEY ARE NOT DECLARED TWICE —
   `adm-lib.css:110-120`'s ruling — so the four wrappers below keep their
   treatment and no longer carry a width.

   ⚠️ **CORRECTED 2026-08-25.** This read: "`position: relative` IS LOAD-BEARING.
   `RdConfirmModal`'s scrim is `position: absolute` and resolves against the
   nearest positioned ancestor, so without this the removal confirm escapes to
   the viewport and covers the rail too. The drawing puts it over the list and
   leaves the rail and the head lit."

   `.rd-confirm` is `position: fixed` now — under the fixed-rail scroll model an
   absolute scrim centred its panel on the page region rather than on the
   viewport (adm1-5-1.css carries the mechanism and the deviation it costs). So
   this declaration no longer decides anything about the confirm. Kept, not
   deleted: it costs nothing and it is what would restore the old behaviour if
   the ruling were reversed. Do not re-derive the retired contract from it.
   --------------------------------------------------------------------------- */
.rd-mem-body { position: relative; padding: 0 32px; }

/* The selection box, mounted in `ProRow`'s LEADING slot — `.rd-prow__lead` owns
   its position and the gap before column 0 and nothing else, so the control
   itself is still this screen's. A real control with a real hit target — the
   drawing's argument for shipping it now is that adding one later moves every
   row height and every hit target on the screen. */
.rd-mem-check {
  width: 16px; height: 16px;
  flex: none;
  padding: 0;
  border: 1.5px solid var(--rd-ctrl);
  background: none;
  cursor: pointer;
}
.rd-mem-check--on { border-color: var(--rd-acc); background: var(--rd-acc); }
.rd-mem-check:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* The name cell's contents. `flex: 1; min-width: 0` is the COLUMN's now
   (`ColumnWidth::Flex`, emitted inline by `pro_table::column_style`); what is
   left here is the drawn `alignItems: 'baseline', gap: 10`
   (`rd-pro-roster.jsx:76`) that no kit cell role carries, which is why the
   column is a `Slot` and this wrapper survives. `min-width: 0` stays because
   the wrapper is itself a flex parent and the name inside it must be allowed to
   shrink. */
.rd-mem-who {
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
}

/* ⚠️ THIS IS A LINK NOW, and the base reset already strips the underline and
   the blue — so a name renders exactly as it did as a span, which is the point:
   the row is unchanged. What it needed was an affordance and a focus ring. The
   destination is `/admin/progress/:id`, the shooter screen that 37 Pro Shooter
   Progress rules to BE Member Detail; the two declarations below are the only
   consequence that reached this section file, and they are additive.

   A row whose name failed to resolve is still a span — see members.rs. */
.rd-mem-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 14px; color: var(--rd-fg);
}
.rd-mem-name:hover { color: var(--rd-acc); }
.rd-mem-name:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* An invited address is not a name: quieter, and one step down in size, because
   the person behind it does not exist here yet. */
.rd-mem-email {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px; color: var(--rd-fg2);
  overflow-wrap: anywhere;
}

.rd-mem-you {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .1em;
  font-size: 9px; color: var(--rd-fg3);
}

/* The role cell's contents. `position: relative` anchors the menu to the row it
   belongs to rather than to the list.

   ⚠️ THAT ONE DECLARATION IS WHY THIS WRAPPER SURVIVED THE MIGRATION.
   `.rd-prow__cell` declares no `position`, so folding these three children
   straight into the cell would resolve `.rd-mem-menu` against `.rd-mem-body` and
   drop the open menu at the top left of the whole list. The `width: 170px;
   flex: none` that used to sit here is `member_columns()`'s. `gap: 7` is the
   drawn number (`rd-pro-roster.jsx:85`) where `.rd-prow__cell--slot` gaps at 6. */
.rd-mem-role {
  position: relative;
  display: flex;
  align-items: center;
  gap: 7px;
}

.rd-mem-rolebtn {
  background: none;
  border: 0;
  padding: 0;
  display: flex;
  align-items: center;
  gap: 7px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px; color: var(--rd-fg2);
  cursor: pointer;
}
.rd-mem-rolebtn:hover { color: var(--rd-fg); }
.rd-mem-rolebtn:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 3px; }
.rd-mem-rolebtn__chev { font-size: 9px; color: var(--rd-fg3); }

/* The menu: four roles and the sentence that says there are only four. */
.rd-mem-menu {
  position: absolute;
  top: 26px;
  left: 0;
  z-index: 4;
  width: 190px;
  background: var(--rd-surf2);
  border: 1px solid var(--rd-line);
  box-shadow: 0 16px 36px rgba(0, 0, 0, .55);
}

.rd-mem-menu__item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  border-left: 2px solid transparent;
  padding: 10px 14px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px; color: var(--rd-fg2);
  cursor: pointer;
}
.rd-mem-menu__item--on { border-left-color: var(--rd-acc); color: var(--rd-fg); }
.rd-mem-menu__item:hover { background: var(--rd-chip); }
.rd-mem-menu__item:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: -2px; }

.rd-mem-menu__note {
  border-top: 1px solid var(--rd-line);
  padding: 10px 14px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px; color: var(--rd-fg3);
  line-height: 1.45;
}

/* The date cell's contents. The `width: 150px; flex: none` is
   `member_columns()`'s now.

   ⚠️ 12px, WHERE `.rd-prow__cell--meta` IS 12.5 — the drawn number
   (`rd-pro-roster.jsx:88`) against the kit's mode. Half a pixel is exactly the
   size of difference a migration absorbs without anyone noticing, and spec 15's
   Q5 is the routed question of who gets to round it. Until it is answered the
   column stays a `Slot` and the drawn face stays here. */
.rd-mem-joined {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 12px; color: var(--rd-fg3);
}

/* The act cell's contents. The column is 220px and right-aligned — wider than
   the drawn 70px because one row in the org carries a sentence instead of a
   control, the owner's, and a 70px column would set that sentence in a
   four-word-tall stack.

   ⚠️ THIS WRAPPER SURVIVES FOR `gap: 10` AND `text-align: left` ON THE INERT
   SENTENCE. `.rd-prow__cell--slot` gaps at 6, and the Invited row puts three
   controls side by side. `width: 100%` is DERIVED from the cell rather than a
   second number: a shrink-to-fit wrapper inside the 220px column would rewrap
   the owner's sentence at a width the drawing never chose. */
.rd-mem-act {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  text-align: right;
}

.rd-mem-remove,
.rd-mem-resend,
.rd-mem-confirm__yes,
.rd-mem-confirm__no {
  background: none;
  border: 0;
  padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  cursor: pointer;
}
.rd-mem-remove { color: var(--rd-fg3); }
.rd-mem-remove:hover { color: var(--rd-danger); }
.rd-mem-confirm__yes { color: var(--rd-danger); }
.rd-mem-confirm__no { color: var(--rd-fg3); }

/* Resend sits beside Revoke and must not read like it. Same quiet resting
   state, and the hover goes ACCENT rather than danger: pressing it sends an
   email, which is the act this screen exists for, not a step towards
   destroying something. */
.rd-mem-resend { color: var(--rd-fg3); }
.rd-mem-resend:hover { color: var(--rd-acc); }
.rd-mem-resend:disabled { color: var(--rd-fg3); cursor: default; }

/* In flight: the label STAYS "Resend" and a 13px `RdSweep` joins it, instead of
   the retired "Sending…" swap — `43 Loading States.html:53`, restated in
   `48 Kit Rules.html` (Action Tiers). Modifier-only, never the base rule: a
   permanent flex row would change every render of this button for a mark that
   is usually absent, the same restraint `.rd-btn__face--inflight` takes in
   kit-loading.css and `.rd-ev-roster__remove--inflight` takes one screen over.
   10px gap is that primitive's own drawn number (`rd-loading.jsx:197`).

   NO COLOUR HERE. `.rd-mem-resend:disabled` already holds the resting ink, and
   this control has no fill to lose — the volt-fill problem is `.rd-mem-send`'s,
   below. §5.5's invariant rides along: construction and height never change
   with state, and an inline-flex row of the same two boxes changes neither. */
.rd-mem-resend--inflight {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

/* Revoke on a CLOSED org: disabled, and reading *Revoked* rather than *Revoke*.
   Ruled by James 2026-08-15 — a deliberate special case to the
   controls-stay-live precedent (#755), because the word there states a fact the
   close already made true instead of offering an act. Same treatment as the
   disabled Resend, and it must also cancel the danger hover above: a dead
   control that still reddens under the pointer is a promise it can be pressed.
   Placed after `.rd-mem-remove:hover` on purpose — equal specificity, decided by
   source order, which is what `styles/order.txt` exists to keep true. */
.rd-mem-remove:disabled { color: var(--rd-fg3); cursor: default; }

.rd-mem-remove:focus-visible,
.rd-mem-resend:focus-visible,
.rd-mem-confirm__yes:focus-visible,
.rd-mem-confirm__no:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

.rd-mem-confirm__ask {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px; color: var(--rd-danger);
}

/* Where a control would go, a sentence — the house treatment for an act that
   nobody can perform, on the owner's row and under the Invited list. Quiet,
   left-aligned even inside the right-aligned act column, because it is prose
   and prose does not rag left. */
.rd-mem-inert {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px; color: var(--rd-fg3);
  line-height: 1.45;
  text-align: left;
  max-width: 64ch;
}

.rd-mem-note {
  margin-top: 16px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px; color: var(--rd-fg3);
  line-height: 1.55;
  max-width: 64ch;
}

/* ---------------------------------------------------------------------------
   The composer — Pro · Invite People.
   --------------------------------------------------------------------------- */
.rd-mem-compose { padding: 24px 32px 32px; max-width: 720px; }

/* THE SLOT, ON A COLUMN THAT ALREADY CARRIES THE GUTTER. A banner mounted as a
   block of this column takes the column's own edges — the primitive declares no
   horizontal margin, so all this has to do is cancel the LIST screen's gutter
   above. `margin-inline`, not a re-spelled shorthand: the top gap is the rule
   above's and must not be restated here.

   ⚠️ NOT A `margin: 0` ON THE COMPOSER'S BANNER. That would also delete the top
   gap, and the strip would then touch the `ProTop` header instead of the label
   — the same defect one edge over. */
.rd-mem-compose > .rd-mem__banner { margin-inline: 0; }

.rd-mem-lab {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .11em;
  font-size: 9px; color: var(--rd-fg3);
  margin-bottom: 8px;
}
.rd-mem-roles + .rd-mem-note { margin-top: 12px; }
.rd-mem-box + .rd-mem-lab { margin-top: 22px; }

/* The address box: chips for what is in the batch, the field for the next one.
   Drawn as one bordered area at a minimum height, so an empty box still reads
   as somewhere to type rather than as a stray input.

   ⚠️ THE BOX IS A COLUMN OF TWO ROWS, NOT ONE WRAPPING BAG. It used to be a
   single `flex-wrap` container holding the chips AND `.rd-mem-addfield`, which
   made the field a wrap item like any chip: past the first line's worth of
   addresses it landed INLINE beside the last chip in whatever width was left
   (staging, 26 Aug). The chips wrap among themselves in `.rd-mem-chips`; the
   field is a full-width row beneath them at every batch size, zero included.
   The frame (34 ScreenInvite, `rd-pro-roster.jsx:169`) draws the box exactly
   this way — a bordered area holding one `flex-wrap` chip row — and stops
   there, because it draws `Add another` as a placeholder span rather than the
   real label + input + button. */
.rd-mem-box {
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  padding: 12px 13px;
  min-height: 104px;
}

.rd-mem-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  align-items: flex-start;
}
/* The separation is on the CHIP LIST and conditional, not a `gap` on the box:
   an empty list is still an element, and a gap would push the field down by a
   row that is not there. `:not(:empty)` spends the 12px only when a chip
   exists.

   ⚠️ AND `:empty` REALLY IS EMPTY HERE. Dioxus leaves a node behind for a
   dynamic child that rendered nothing, so this depends on WHAT that node is —
   it is a COMMENT (`document.createComment('placeholder')`,
   dioxus-interpreter-js 0.6.2 `unified_bindings.rs:74`), and `:empty` ignores
   comments. A placeholder `<pre hidden>`, which older Dioxus generations used,
   would have defeated this selector AND drawn a stray element inside the box. */
.rd-mem-chips:not(:empty) { margin-bottom: 12px; }

.rd-mem-addr {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--rd-chip);
  border: 1px solid var(--rd-line);
  padding: 5px 9px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px; color: var(--rd-fg);
}

.rd-mem-addr__x {
  background: none;
  border: 0;
  padding: 0;
  color: var(--rd-fg3);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}
.rd-mem-addr__x:hover { color: var(--rd-danger); }
.rd-mem-addr__x:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* Its own row, always. `width: 100%` rather than a flex basis: this element is
   no longer a flex item of the box, so a `flex: 1 1 260px` here would be inert
   and would read as if the field could still share a line. It cannot. */
.rd-mem-addfield {
  width: 100%;
  display: flex;
  align-items: flex-start;
  gap: 10px;
}
/* The field takes the row and the Add button keeps its own width. Without the
   grow, `.rd-field` is a plain block flex item and sizes to max-content — the
   input would draw at its intrinsic ~20ch no matter how wide the row is, which
   is the shape the old `flex: 1 1 260px` on the wrapper was standing in for.
   `min-width: 0` is the same one `.rd-field__hit` documents: an `auto` minimum
   refuses to shrink past the input and pushes the button off the end. */
.rd-mem-addfield > .rd-field { flex: 1 1 auto; min-width: 0; }

.rd-mem-add {
  flex: none;
  margin-top: 22px;
  background: none;
  border: 1.5px solid var(--rd-ctrl);
  color: var(--rd-fg);
  padding: 7px 14px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 10.5px;
  cursor: pointer;
}
.rd-mem-add:disabled { color: var(--rd-fg3); border-color: var(--rd-line); cursor: default; }
.rd-mem-add:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 3px; }

.rd-mem-roles { display: flex; gap: 8px; flex-wrap: wrap; }

/* The role picker on the Invite pane. Bigger than the filter chips on the list,
   because this one is a choice being made rather than a view being narrowed —
   and the drawing sizes them that way.

   ⚠️ **A CHIP, SO TWO LAYERS AND THE 13px CONTROL CUT.** It drew a square box
   for the same reason the list's filter row did (`kit-pro-filter-bar.css` §3,
   the 27 Aug tablet defect): `01 Foundations` §03 as re-ruled 26 Aug 2026 cuts
   every CONTROL at 13, and `48 Kit Rules` — *"a stroked chamfer must be two
   layers"* — is why the stroke cannot stay a `border` under the clip. Same
   construction as `.rd-prom-chip` (adm-mob.css §3.3) at the same 1.5px stroke,
   so the inner cut is `inner_chamfer_px(13, 1.5)` = 12.12.

   ⚠️ IT IS A CHIP AND NOT A SEGMENTED CONTROL. The four roles are separate
   boxes with their own gap (`.rd-mem-roles` gaps at 8px), not one welded strip
   — which is what makes the chip geometry the right one here and the wrong one
   for `.rd-ev-form__seg-opt` and its family. */
.rd-mem-rolepick {
  position: relative;
  isolation: isolate;
  border: 0;
  background: var(--rd-ctrl);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--rd-chamfer-control)), calc(100% - var(--rd-chamfer-control)) 100%, 0 100%);
  color: var(--rd-fg2);
  /* The drawn `8px 14px` plus the 1.5px the border used to occupy. */
  padding: 9.5px 15.5px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  cursor: pointer;
}
.rd-mem-rolepick::before {
  content: "";
  position: absolute;
  inset: 1.5px;
  z-index: -1;
  background: var(--rd-hollow-fill);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 12.12px), calc(100% - 12.12px) 100%, 0 100%);
}
.rd-mem-rolepick--on { background: var(--rd-acc); color: var(--rd-fg); }
/* An inset ring on the SURFACE layer — `clip-path` clips an `outline` away, and
   an element-level inset shadow paints under the surface. */
.rd-mem-rolepick:focus-visible { outline: none; }
.rd-mem-rolepick:focus-visible::before { box-shadow: inset 0 0 0 2px var(--rd-acc); }

.rd-mem-send {
  margin-top: 26px;
  background: var(--rd-volt);
  color: var(--rd-on-volt);
  border: 0;
  padding: 11px 20px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 11px;
  cursor: pointer;
  clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
}
/* NOTHING TO SEND — no address in the box. ⚠️ NOT the in-flight state; the two
   rules below narrow this one. */
.rd-mem-send:disabled { background: var(--rd-surf2); color: var(--rd-fg3); cursor: default; }
.rd-mem-send:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 3px; }

/* THE BATCH IS ON THE WIRE, and the counted label stays — `43 Loading
   States.html:53` / `48 Kit Rules.html`, Action Tiers. The row is what seats
   the 13px sweep beside `Send 2 Invitations`; it changes no box, which is
   02-tbtn §5.5's invariant (construction and height never change with state).
   `inline-flex` and not `flex`: the control is shrink-to-fit and must stay so. */
.rd-mem-send--inflight {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

/* AND THE VOLT GROUND SURVIVES THE PRESS. `sending` still sets the native
   `disabled` attribute — a batch already going must not be sendable twice — so
   the grey rule above fires and this restores the fill over it. Without it the
   `SweepTone::OnVolt` mark, which draws in `--rd-on-volt`, would sit on a
   `--rd-surf2` grey: `rd-loading.jsx:191`, *"grey is DISABLED ('you can't'),
   in-flight is 'it's going'."* The same pair `.rd-close__go--inflight` writes
   over its danger fill and `.rd-confirm__go--volt.rd-confirm__go--inflight`
   writes over this very token.

   SPECIFICITY, NOT ORDER: 0,3,0 against the `:disabled` rule's 0,2,0, so moving
   either rule cannot silently swap the paint back. Colour only — `cursor` is
   deliberately not restated, and the rule above is still correct for a button
   disabled because the box is empty. */
.rd-mem-send.rd-mem-send--inflight:disabled {
  background: var(--rd-volt);
  color: var(--rd-on-volt);
}
/* ==== ADM1-5.1 · docs/design/kit/ADM1-events.md §5.1 · owner: lane4 ==== */

/* RdConfirmModal — the destructive-act confirmation.

   §5.1's geometry was already correct in the signature-stage block and is
   carried over unchanged: the scrim value, 460px, --rd-surf, the --rd-line
   border, padding 26, --rd-dfill under white type, and Keep outlined 1.5px
   --rd-ctrl. What follows notes only what changed and why.

   NOT RULED HERE, ROUTED: the buttons' square corners and 11px size. ADM0 §3
   draws the Pro action button at 11.5px with chamfer 7, which looks like a
   reason to chamfer these — but ADM1 §2.1 draws StatusChip as "a square-cornered
   outline", so the arc mixes square and chamfered deliberately and §5.1 is
   silent on both. Left exactly as shipped.

   ALSO ROUTED: the heading's 800/75%/.02em/18px has no source in §5.1, §2.1 or
   ADM0. The sibling modal carries the identical role at 17px, one pixel apart.
   Kept as shipped rather than picked. */

/* ⚠️ **FIXED, NOT ABSOLUTE — CHANGED 2026-08-25, AND IT IS A RULING, NOT A
   TIDY-UP.** James, NTOA rehearsal, Pro TABLET, `/admin/drills`: pressing
   `Publish` on a drill opened a confirm that sat neither vertically nor
   horizontally centred in the viewport.

   THE MECHANISM, WHICH IS NOT A TRANSFORM AND NOT A PORTAL.

   This rule was `position: absolute; inset: 0`, so the scrim's containing block
   was the nearest POSITIONED ancestor. On the Org Library that is `.rd-adm-lib`
   (adm-lib.css) — `flex: 1; min-height: 0; overflow: auto; padding: 18px 32px
   32px; position: relative`, i.e. the LIST REGION, which since the fixed-rail
   scroll model (`.rd-pro` `height: 100dvh; overflow: hidden` +
   `.rd-pro-page` `overflow: auto`, adm0-chrome.css) is a real scroller sitting
   BELOW the page head, the search field and the tab row, and to the RIGHT of
   the 208px rail. `inset: 0` therefore sized the scrim to that region and the
   flex centring below centred the 460px panel on THAT box:

     - horizontally, on the content column's centre, ~104px right of the
       viewport's (half the rail width);
     - vertically, on the list region's centre, which on James's 1185x886 glass
       is ~552 against a viewport centre of ~443 — the panel sits about 110px
       low, which is exactly what the screenshot shows;
     - and, because an absolutely-positioned child of a SCROLL container is laid
       out against the scrollable overflow area, it scrolled away with the list
       once the list was scrolled at all. A question you have to scroll back up
       to answer.

   No ancestor declares `transform`, `filter`, `contain`, `will-change` or
   `perspective` — that hypothesis was checked and is not what happened. The
   containing block was doing exactly what `position: absolute` asks for. What
   changed underneath it was the scroll model: the drawing's list region is
   `overflow: 'hidden'` and never scrolls (`rd-pro-library.jsx:215`), so in the
   drawing "the region" and "the visible page below the head" are the same box.
   In the build they stopped being the same box, and the rule that had been
   correct against a static mock became wrong against a scroller.

   WHAT THIS COST, STATED RATHER THAN BURIED, WHEN THIS WAS WRITTEN. §5.1 said
   the scrim goes "over the tab body", and `rd-pro-library.jsx:227` /
   `rd-pro-events.jsx:438,523,560` all drew it `position: 'absolute', inset: 0`
   inside the content region, so the DRAWN scrim left the rail and the page
   head lit. A viewport-fixed scrim dims them too. That was a real deviation
   from the drawing and it was the designer's own finding that forced it: the
   two properties — "centred in the viewport" and "dims only the content pane"
   — could not both hold once the pane was a scroller that starts a third of
   the way down the glass. Centring won, because it is what was reported.
   Flagged for James on the PR.

   RESOLVED, EXPORT 27. James ruled the other way, but not the fallback this
   comment offered (a fixed panel inside an absolute scrim) — the designer
   changed the drawing instead. `rd-pro-kit.jsx:326` moves `position:
   'relative'` off every Pro content pane and onto `ProShell` itself, and the
   Library's confirm now says so in words (`rd-pro-library.jsx:225-226`):

     "The dim + modal cover the WHOLE page — rail included — not just the
     list pane: a modal fences the entire surface, so it centers on the
     surface."

   So the drawn scrim now covers the whole console surface, rail included, on
   every Pro dialog — exactly what `.rd-confirm` below already does. There is
   no deviation left to flag.

   `display: flex` KEPT, not swapped to `grid`/`place-items` — the drawing is a
   flex box with `alignItems: 'center', justifyContent: 'center'` and for a
   fixed-width panel the two are identical. Changing it would be a second,
   invisible edit riding along with this one. */
.rd-confirm {
  position: fixed; inset: 0; z-index: 40;
  background: rgba(6, 6, 6, .82);
  display: flex; align-items: center; justify-content: center;
}

.rd-confirm__panel {
  width: 460px;
  max-width: calc(100vw - 32px);
  background: var(--rd-surf);
  border: 1px solid var(--rd-line);
  padding: 26px;
}
/* Script-focused on open; never a Tab stop. No ring — the panel is a focus
   destination, and a highlighted box around a whole dialog reads as an error. */
.rd-confirm__panel:focus { outline: none; }

/* THE RESET CARD'S OWN STROKE — the half of the outline ruling that is not the
   button. `21 Range Bag`: "the destructive action is a red outline rather than a
   red fill — it is a reset, not a deletion", and `rd-bag.jsx`'s ScreenBagReset
   draws the card itself stroked in `t.danger` at `w={1.5}`, where every other
   confirm in the product takes the neutral hairline above.

   COLOUR AND THE STROKE'S WEIGHT ONLY. No radius, no padding, no width, no
   ground: the panel is the same 460px box it always was, and the 1.5px is the
   drawn figure rather than a hairline rounded up. §5.5's invariant is about a
   control's STATES; this is a kind, and the drawing gives the kind a heavier
   stroke on purpose — a card you can walk away from, ringed in the colour of the
   thing it is about to do. */
.rd-confirm__panel--danger {
  border-color: var(--rd-danger);
  border-width: 1.5px;
}

/* ⚠️ THE TRANSFORM IS LOAD-BEARING AND AN EARLIER REVISION OF THIS PR DELETED IT.
   Recorded rather than silently restored, because the reasoning that removed it
   was plausible and would be reached again.

   The wrong version, which reviewed and nearly merged:

       .rd-confirm__head {
         font-family: 'Archivo', system-ui, sans-serif;
         [text-transform: uppercase DELETED HERE]
         font-weight: 800; font-stretch: 75%; letter-spacing: .02em;

   The argument for deleting it: §5.1 renders the heading `Delete {name}?` in
   sentence case, and ADM1 §6.1 says of the sibling modal's header "sentence
   case, unlike the button" — so uppercasing appeared to scream a user's own
   event name as DELETE STEEL NIGHT?.

   Why that was wrong: CASE IS A PROPERTY OF THE TYPE HELPER, NOT OF THE
   HEADING'S APPARENT ROLE. rd-pro-kit.jsx:102 defines pdisp with
   textTransform: 'uppercase' in the helper itself, and rd-pro-events.jsx:475
   draws this heading as pdisp(800, 75, '.02em') at 18. It uppercases BY
   CONSTRUCTION. 00-substrate.md §2.1:454 already recorded the same mechanism
   for the shooter-side kit: "the transform is in the helper, so every display
   role is uppercase and no spec needs to restate it."

   So §5.1's silence about case was that rule working, not a gap. And §6.1 was
   never in conflict: rd-pro-events.jsx:513 draws its header "Add members" while
   :512 draws the button beside it "Add 2 Members" — a contrast in AUTHORED
   capitalisation, both rendered uppercase. Reading it as rendered case made
   §6.1 true and the substrate false.

   A spec's silence about a visual property is not an absence of a rule. Check
   which helper the drawing reaches for first. */
.rd-confirm__head {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 75%; letter-spacing: .02em;
  font-size: 18px; color: var(--rd-fg); margin-bottom: 12px;
}

.rd-confirm__body {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px; color: var(--rd-fg2); line-height: 1.6;
}

/* The drawn quieter line under the body — §5.1's third line (the who-can-act
   sentence at the delete confirm, rd-pro-events.jsx:477): a step down from the
   body in both size and grey, 12px apart. A statement about authority, not a
   warning — no alarm colour, same doctrine as the note below. OPTIONAL; with it
   absent the modal is byte-identical to before the slot existed. */
.rd-confirm__quiet {
  margin-top: 12px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px; color: var(--rd-fg3); line-height: 1.55;
}

/* The hairline note under the body — OPTIONAL, and absent at the drawn delete
   confirm. Added for the Org Library's publish confirm
   (rd-pro-library.jsx:234-237), whose geometry is `borderLeft: 2px solid t.line`,
   `paddingLeft: 14`, the body's own `marginBottom: 16` above it and `22` below.

   The 22 below is already spent by .rd-confirm__acts' margin-top, so only the 16
   above is declared here — .rd-confirm__body is left exactly as it was, which
   keeps the note-less modal byte-identical in layout.

   ⚠️ --rd-line, AND THERE IS NO WARNING VARIANT OF THIS RULE, deliberately.
   rd-pro-library.jsx:231-233: "This is no longer a warning - it is the one fact
   about publishing an org will not guess, and a warning rule beside a true
   statement makes the statement look like a risk." An earlier draft of the
   drawing DID use the warning colour and the designer took it out; a `--warn`
   modifier here would be that draft returning through the back door. */
.rd-confirm__note {
  margin-top: 16px;
  border-left: 2px solid var(--rd-line);
  padding-left: 14px;
}
.rd-confirm__note-head {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px; color: var(--rd-fg); line-height: 1.6;
}
.rd-confirm__note-detail {
  margin-top: 6px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px; color: var(--rd-fg3); line-height: 1.6;
}

/* The refusal slot — OPTIONAL, undrawn, and it declares SPACING ONLY.

   §5.1 draws no error inside the panel because the corpus has no interactive
   modal at all; this exists because `admin/platform_admin.rs`'s delete can be
   refused by design (open payment, ownership transfer, a seed guard) and the
   refusal is the answer to the button inside this panel.

   ⚠️ NO COLOUR, NO BORDER, NO TYPE HERE. The strip itself is `RdBanner` in its
   error tone (P1), which already owns the ground, the rule and the assertive
   role — restating any of it here would be a second definition of one thing,
   drifting from the primitive the moment P1 moves. The 16 matches the note's own
   margin-top above it, for the same reason: the 22 below is already spent by
   .rd-confirm__acts, so only the gap above is declared. */
.rd-confirm__err { margin-top: var(--rd-banner-gap); }

.rd-confirm__acts { display: flex; gap: 10px; margin-top: 22px; }

/* dfill, NOT danger — the ground a destructive button sits on under white type.
   The two are equal in light and differ in dark, which is the default.

   ⚠️ #fff is the one raw hex in this block, and it is deliberate. §5.1 spells
   the literal, so it is the spec-declared value; --rd-dfill is a dark red in
   both themes (#A82A12 / #B32D0C), so white is theme-safe here and this is not
   the dark-theme bug the no-hex rule aims at. The kit has the exact precedent
   for tokenising it — --rd-on-volt, "text on a volt fill" — and --rd-on-dfill
   would be its sibling, but minting a shared token is not this lane's call.
   Proposal on record; the hex ships as drawn. */
/* ⚠️ 36px OF DRAWN CONTROL, 44 OF TARGET — James, NTOA rehearsal 2026-08-29.
   10px of padding over an 11px display line is a 36px pill; the confirm pair is
   the last thing a hand in gloves touches before something is destroyed, and it
   was the tightest target on the screen. The `::after` on both acts below is
   the whole of the fix: the pill is drawn at exactly the size it was drawn at.
   `position: relative` here is the containing block that pseudo needs. */
.rd-confirm__go {
  flex: none; background: var(--rd-dfill); color: #fff;
  position: relative;
  border: 0; padding: 10px 18px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 11px; cursor: pointer;
}

/* The PRIMARY ground, for a confirm that destroys nothing. Publishing is not a
   destructive act, and dressing it in the destructive fill would say it is:
   rd-pro-library.jsx:239 draws it on `t.volt` with `onVolt` type and tcham(7) —
   the same pair .rd-pro-top__btn carries, for the reason stated there (acc is
   the accent as TEXT; as a fill it lands at 2.3:1 in light).

   Colour and clip only. §5.5's invariant — construction and height never change
   with state — extends to kind: this must not restate padding or size, or the
   two confirms would differ in geometry as well as in meaning. The :disabled
   rule below is shared by both kinds and needs no variant, since it repaints the
   ground either way.

   ⚠️ A SECOND DRAWING ASKED FOR THIS GROUND AND ARRIVED IN PARALLEL (merged
   2026-08-13). `33 Pro Org Settings` (ScreenTransfer) stages the same component
   exactly — same scrim, same 26px panel, same heading and body type, same two
   buttons in the same order — and fills the act with volt as well. That branch
   had built it as `.rd-confirm__go--primary`; the merge keeps ONE modifier, this
   one, because two class names for one ground is how a sheet grows a second way
   to say the same thing. Its argument belongs here: Transfer and Close Org sit
   on one screen whose own stated problem is that the two are easy to confuse,
   and the red fill is one of the two things that keeps them apart — a transfer
   destroys nothing (no account, no run, no enrolment, no drill), so the destroy
   ground would rank handing an org on with ending it.

   ⚠️ THE TWO BRANCHES DREW THE CHAMFER ON DIFFERENT CORNERS — this rule cuts
   top-right and bottom-left, the retired one cut top-left and bottom-right, and
   both cited `tcham(7)`. Neither is substrate §3.1's literal `tcham`, which cuts
   the bottom-right corner ALONE; the two-corner form is a Pro convention the
   substrate does not spell. This kept master's geometry rather than re-cutting a
   shipped button during a merge. If the drawings really do differ, that is a
   design ruling and not a merge decision. */
.rd-confirm__go--volt {
  background: var(--rd-volt);
  color: var(--rd-on-volt);
  clip-path: polygon(0 0, calc(100% - 7px) 0, 100% 7px, 100% 100%, 7px 100%, 0 calc(100% - 7px));
}

/* THE OUTLINE GROUND — HOLLOW, AND THE ANSWER TO THE PARAGRAPH ABOVE.

   The rule above refused a SECOND NAME FOR ONE GROUND: two branches drew the
   same volt fill, one of them called it `--primary`, and the merge kept one
   name because that is how a sheet grows a second way to say the same thing.
   That argument holds and this does not reopen it. This is not the volt fill
   under another name and it is not the destroy fill under another name — it is
   a THIRD ground, ruled in words by the drawing, and the ruling is exactly that
   it must not be either of them. `21 Range Bag`: "the destructive action is a
   red outline rather than a red fill — it is a reset, not a deletion." And
   `21 (Mobile) Range Bag` says what the sheet would lose by folding it in:
   "Red outline, not red fill. This replaces a list, it does not delete an
   account, and the four-rank system spends its loudest treatment on the one
   thing that cannot be walked back." One ground still gets one name. There are
   now three grounds, and a sheet that could not say "outline" would have to
   paint replacing a packing list the same red it paints ending an account.

   `rd-bag.jsx`'s ScreenBagReset draws the act as `kind="d"`, which the shooter
   kit's button primitive defines as hollow, danger-stroked, danger-typed — the
   same treatment `.rd-btn__face--danger` already carries in the chamfer-button
   sheet. Nothing new is invented; this is that treatment reaching a control that
   could not ask for it.

   COLOUR AND CLIP ONLY, like the rule above — §5.5's invariant extends to kind,
   so no padding, no size, no type. THE STROKE IS AN INSET SHADOW, NOT A BORDER:
   the base rule sets `border: 0` and a border here would add 3px to the control
   in a row of buttons that must stay one height. NO `background` DECLARATION AT
   ALL rather than `transparent` — the base rule's fill is what has to go, and
   `background: none` is the removal, not a second colour. No radius: the
   square-cornered form is `.rd-confirm__go`'s and this changes no corner. */
.rd-confirm__go--dline {
  background: none;
  color: var(--rd-danger);
  box-shadow: inset 0 0 0 1.5px var(--rd-danger);
}

/* The confirm is in flight. UNDRAWN — built from 02-tbtn §5.5, which gives a
   filled control's disabled treatment as --rd-surf2 ground with --rd-fg3 label,
   "as the kit draws it today".

   §5.5's invariant rides with it: CONSTRUCTION AND HEIGHT NEVER CHANGE WITH
   STATE, only colour. So no border, no padding change, no size change here —
   a button that changes shape as its request settles is worse than one that
   does not respond. `cursor: default` rather than `not-allowed`: this is "not
   right now", not "never". */
.rd-confirm__go:disabled {
  background: var(--rd-surf2);
  color: var(--rd-fg3);
  cursor: default;
}

/* ⚠️ RULED 20 Aug 2026 — AND IT NARROWS THE RULE ABOVE.
   round-7-loading-rulings-reply.md §1: "on a danger fill the sweep draws in the
   danger fill's foreground ink, trackless, 13px, label kept." That is a
   statement that the danger fill is STILL THERE while the request is open — a
   greyed confirm has no fill and no white type, so the ruled mark could not be
   painted on it. The same reading the button primitive took on 19 Aug
   (rd-loading.jsx:191): "it keeps its own colour: grey is DISABLED ('you
   can't'), in-flight is 'it's going'."

   `pending` still sets the native `disabled` attribute — input stops, as ruled
   — so the rule above still fires and these restore the ground over it. The
   rule above is therefore NOT dead: `cursor: default` is deliberately not
   restated here and is still what it says, and its two colour declarations
   remain correct for a confirm disabled for any other reason.

   SPECIFICITY, NOT ORDER: 0,3,0 against the `:disabled` rule's 0,2,0. Both
   would work from this position in this file, and the compound form is written
   anyway so that moving either rule cannot silently swap the paint back.

   COLOUR AND THE FLEX ROW ONLY. 02-tbtn.md §5.5's invariant is that
   construction and height never change with state; the row is what seats the
   13px mark beside the unchanged label, exactly as .rd-btn__face--inflight does
   in kit-loading.css, and it changes no box. `inline-flex` rather than `flex`
   because .rd-confirm__acts is a flex row of shrink-to-fit controls.

   #fff is the same literal the base rule spells, for the reason it records:
   --rd-dfill's foreground ink has no token, and .rd-sweep--on-danger strokes
   the same literal so the mark matches the label beside it. */
.rd-confirm__go.rd-confirm__go--inflight {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
}

.rd-confirm__go.rd-confirm__go--inflight:disabled {
  background: var(--rd-dfill);
  color: #fff;
}

/* The primary kind keeps volt, for the reason the kind exists: a transfer or a
   publish destroys nothing, and a confirm that greyed mid-request would say the
   press had not landed. The sweep beside it draws in --rd-on-volt, which is the
   ink this rule sets. */
.rd-confirm__go--volt.rd-confirm__go--inflight:disabled {
  background: var(--rd-volt);
  color: var(--rd-on-volt);
}

/* The outline ground keeps ITS ground too, and here the rule is load-bearing
   rather than a refinement: without it the destructive override two rules above
   matches on `.rd-confirm__go` and a reset MID-FLIGHT repaints into the destroy
   fill under white type — the one treatment `21 Range Bag` rules out by name.
   The ground the ruling gives it is hollow, so "keeps its ground" here means
   keeping no ground at all.

   SPECIFICITY IS EQUAL AND SOURCE ORDER DECIDES, exactly as it does for the
   volt row above: 0,3,0 against the destructive override's 0,3,0, so this rule
   must stay BELOW it. The stroke is restated because the `:disabled` rule sets
   a background of its own and a shadow does not survive by inheritance; nothing
   structural is restated, per §5.5. */
.rd-confirm__go--dline.rd-confirm__go--inflight:disabled {
  background: none;
  color: var(--rd-danger);
  box-shadow: inset 0 0 0 1.5px var(--rd-danger);
}

/* 44px of target on both acts, painting nothing and moving nothing. The pseudo
   hit-tests as its own element, and the extension is VERTICAL ONLY because
   `.rd-confirm__acts` is a horizontal flex row at a 10px gap — a sideways
   extension would put Go's target under Keep's. Above and below the pair there
   is only the panel's own padding, which is nobody's target. Shared note:
   `kit-hit.css`. */
.rd-confirm__go::after,
.rd-confirm__keep::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 44px;
  /* `margin-top`, not a `transform`: a transform would make the pseudo a
     containing block for fixed descendants, which is a hazard the pool already
     polices by name in three places. Half of 44. */
  margin-top: -22px;
}

.rd-confirm__keep {
  flex: none; background: none;
  position: relative;
  border: 1.5px solid var(--rd-ctrl); color: var(--rd-fg);
  padding: 10px 18px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 11px; cursor: pointer;
}

/* THE THIRD WAY OUT — quiet danger text under the two buttons, added 18 Aug
   for `36 (Tablet) Pro Actual Range Day` / ScreenSquadLeave (the Squads tab's
   unsaved-changes guard) and drawn identically at `05 (Tablet) Stage Editor` /
   ScreenEditorLeave. OPTIONAL: absent, the panel is byte-identical to before
   this rule existed, same promise .rd-confirm__quiet makes for its own line.

   Both drawings give it centred, full width, `pdisp(700, 78, '.12em')` at 10px
   in the danger colour, 4px under the buttons. TEXT and not a ground, which is
   the drawing's argument rather than a taste: this is the only one of the three
   answers that loses work, so it is the only one that must not invite a reflex
   press. --rd-danger is the token the kit already spends on danger as TEXT
   (adm1-4, adm-plat); --rd-dfill is the fill under white type and would make a
   button of it.

   Still a <button>, so :disabled and :focus-visible both matter. Disabled takes
   --rd-fg3, the same "not right now" treatment .rd-confirm__go:disabled uses,
   and the same `cursor: default` for the same reason. */
/* ⚠️ A REAL `min-height` HERE, NOT THE PSEUDO — James, NTOA rehearsal
   2026-08-29. `Discard` sat at ~24px: a 10px line inside 8/4 of padding. It is
   full-width and it sits 4px UNDER the Go/Keep pair, so a pseudo grown to 44
   would reach up over Keep and quietly swallow taps meant for it — the sibling
   overlap `kit-hit.css` refuses. The box grows for real instead; it is the last
   row in the panel and the only thing it pushes is the panel's own bottom
   padding. `flex` keeps the label centred in the taller box, where `display:
   block` plus `text-align: center` would have pinned it to the top. */
.rd-confirm__discard {
  display: flex; width: 100%;
  align-items: center; justify-content: center;
  min-height: 44px; box-sizing: border-box;
  margin-top: 4px; padding: 8px 0 4px;
  background: none; border: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .12em;
  font-size: 10px; color: var(--rd-danger);
  text-align: center; cursor: pointer;
}

.rd-confirm__discard:disabled { color: var(--rd-fg3); cursor: default; }

.rd-confirm__go:focus-visible,
.rd-confirm__keep:focus-visible,
.rd-confirm__discard:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* ==== ADM1-3 · docs/design/kit/ADM1-events.md §3 · owner: lane4 ==== */

/* RdEmptyState — the centred "there is nothing here yet" block.

   ⚠️ EVERY VALUE HERE CAME OUT OF THE DRAWING, NOT OUT OF A SPEC. §3.5 rules
   this state "as drawn" and draws none of it; no spec carries its geometry,
   type role, size or tokens, and §8's claim that the empty sentences are pinned
   inline is not true of them (filed separately). The head architect, who holds
   the corpus, read them off rd-pro-events.jsx:199-203 on 2026-08-10:

     outer  flex, centred both axes, padding: 32
     inner  centred text, max-width: 420
     title  pdisp(800, 75, '.02em'), fontSize 19, t.fg, marginBottom: 12
     body   pbody, fontSize 13, t.fg3, lineHeight: 1.6

   The shipped signature-stage block already matched all of it. Nothing here is
   a correction; the values are now sourced rather than inherited, and tests pin
   each one — with no spec to check them against, those tests are the only thing
   standing between this component and silent drift.

   CASE IS A PROPERTY OF THE TYPE HELPER. pdisp carries textTransform:
   'uppercase' in its own definition (rd-pro-kit.jsx:102), so the title
   uppercases BY CONSTRUCTION and no spec restates it — the same mechanism
   00-substrate.md §2.1:454 records for tdisp. A spec's silence about case is
   not an absence of a rule. Neither case nor size is inferable from a heading's
   apparent role; the drawing sets both explicitly. */

.rd-empty {
  /* Claims the free height of the box it is given, so "centred" means centred
     in the list area rather than around its own two lines. A caller that mounts
     this outside a flex column gets a block hugging its content — a caller
     contract this cannot enforce. */
  flex: 1;
  display: flex; align-items: center; justify-content: center;
  padding: 32px;
}
.rd-empty__inner { text-align: center; max-width: 420px; }

.rd-empty__title {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 75%; letter-spacing: .02em;
  font-size: 19px; color: var(--rd-fg); margin-bottom: 12px;
}

/* pbody: no font-weight and no font-stretch, so 400 and 100 arrive as the CSS
   initial values — substrate §2.2's "absent is 100 and absent is correct".
   Writing either would condense or embolden the only reading-voice text here. */
.rd-empty__body {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px; color: var(--rd-fg3); line-height: 1.6;
}

/* The list row's date cell, when the event has an `Ends` — ruled 18 Aug
   (32 Pro Events, "The two sites a range does not fit — neither widens", and the
   drawn `Two-Gun Weekender` row: `['Sat 30 Aug –', 'Sun 1 Sep']` rendered as two
   stacked divs inside the same 108px cell).

   ⚠️ TWO DECLARATIONS, AND THE CELL ITSELF IS UNTOUCHED. `.rd-adm-ev__when`
   already carries the width, the type role and the colour, and it lives in the
   unowned `_legacy-events-list.css` — delete-only, so nothing here edits it.
   Everything below is on modifiers a single-day row never receives, which is why
   a single-day row is byte-identical to what it rendered before the ruling.

   `line-height: 1.5` is the drawn cell's own (rd-pro-events.jsx, ScreenEvents,
   the `when` cell) and only matters once there are two lines to space. */
.rd-adm-ev__when--range { line-height: 1.5; }

/* The cell is a flex item and therefore a block box, but its children are not:
   two inline spans would run together on one line, which is the one thing this
   treatment exists to avoid. */
.rd-adm-ev__when-line { display: block; }

/* ==== S16 · 16 Orgs — Find An Org (snapshot-2026-08-07) · owner: lane-2 ==== */

/* ---------------------------------------------------------------------------
   The shooter's org search. Drawn: rd-orgs.jsx:16-58 (OrgSearchField,
   ScreenFindOrg).

   ⚠️ SHOOTER SIDE — the kit primitives are native here, and the Pro families
   (rd-set-*, rd-ev-*) are the foreign ones. The frame, heading, body copy,
   field box and button are all kit; only the rhythm between them and the two
   quiet type roles the kit does not carry are page CSS.

   `RdText` owns the middle step of the hierarchy (--rd-fg2) and nothing else:
   it has no colour prop, by measurement, because 18 of 18 corpus sites are fg2.
   The meta step (--rd-fg3) is therefore the page's, which is why the hint and
   the footnote below are local rules rather than RdText call sites.
   --------------------------------------------------------------------------- */
.rd-find { display: flex; flex-direction: column; min-height: 0; flex: 1; }

/* 10px above the field, 12 below — the drawn `margin: '10px 0 12px'`.

   ⚠️ NO LONGER A FLEX ROW WITH A GAP. It was sized for a Search button beside
   the field; `rd-orgs.jsx:33-36` draws the field alone, the button is deleted,
   and a `gap` with one child is a rule that describes a tree that no longer
   exists. The field is now the whole row. */
.rd-find__field {
  margin: 10px 0 12px;
}

/* `RdSearchField`'s wrapper, between the drawn row and the box it draws.

   Two classes rather than one because the component owns the tree now — the
   child of `.rd-find__field` is the search field's wrapper, and the box is a
   grandchild. */
.rd-find__wrap {
  min-width: 0;
}

/* The field box's inner row. Geometry is the drawing's: 44 min-height, 13px
   side padding. The chamfer, border and stroke are `RdSearchField`'s props at
   the call site, not restated here. */
.rd-find__box {
  min-height: 44px;
  display: flex;
  align-items: center;
  padding: 0 13px;
}

/* The input is transparent and borderless: the BOX is the control's boundary,
   and a second border inside it would draw the affordance twice. */
.rd-find__input {
  width: 100%;
  background: none;
  border: 0;
  padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
}

/* The colour STEP is the whole filled/empty signal on a drawn field — the
   placeholder is the tertiary, the value is the primary, and nothing else
   changes between the two states. */
/* ⚠️ THE PRE-KIT BACKFILL WINS UNLESS OUTRANKED, and AU5 predicted this exact
   bite: *"THIS WILL BITE EVERY KIT PRIMITIVE THAT BINDS A REAL <input>"*
   (style.css, the AU5 block). The backfill styles inputs by ATTRIBUTE —
     input[type="text"] { border: 1px solid #d1d5db; outline: none; }
     input:focus { box-shadow: 0 0 0 2px #3b82f6; border-color: #3b82f6; }
   at (0,0,1,1), which beats a bare class at (0,0,1,0). Undefended, focusing
   this field paints a 2px BLUE ring inside a volt-accented kit box: two design
   systems on one control, in a colour the design does not contain.

   The compound below is (0,0,2,0) and outranks both, exactly as AU5's own
   defence does. Scoped to this page, editing nothing in the backfill block —
   which is not this lane's to touch — and still correct if that block goes. */
.rd-find__box .rd-find__input {
  border: 0;
  box-shadow: none;
  outline: none;
}

/* The colour step is the CONTENT axis and needs no signal — the browser keys it
   off emptiness. The box's stroke and border are the FOCUS axis, set at the
   call site. Two axes, deliberately not collapsed. */
.rd-find__input::placeholder { color: var(--rd-fg3); opacity: 1; }

/* ⚠️ `.rd-find__sweep` WAS HERE and is retired. The in-flight mark is the kit
   field's own end slot now (`.rd-search__sweep`, `au5.css`) — the same four
   declarations, written once instead of once per search surface. See
   `docs/design/kit/A6-search-field.md` §8.1. */

.rd-find__error {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-danger);
  margin-bottom: 12px;
}

/* The drawn `nomatch` state: centred in the remaining space, lifted off the
   bottom edge so it reads as the answer rather than as a footer. */
.rd-find__nomatch {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 10px;
  padding-bottom: 60px;
}

/* The meta step. This sentence is the one doing the work in the empty state —
   a failed search does not mean the org is absent, only that it is unlisted. */
.rd-find__hint {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  line-height: 1.5;
  text-wrap: pretty;
}

.rd-find__results { flex: 1; min-height: 0; display: flex; flex-direction: column; }

/* A result row. 56 min-height and a hairline between, none after the last —
   `:last-child` rather than an index test, so the rule holds however the list
   is built. */
.rd-find__row {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  min-height: 56px;
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  padding: 0;
  cursor: pointer;
}

.rd-find__row:last-child { border-bottom: none; }
.rd-find__row:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

.rd-find__name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .04em;
  font-size: 12.5px;
  color: var(--rd-fg);
}

.rd-find__desc {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  line-height: 1.35;
}

/* The footnote. `margin-top: auto` pins it to the foot of whatever space is
   left, which is what makes it qualify the results rather than preface them —
   the drawing moved this fact here from a subtitle above the field. */
.rd-find__foot {
  margin-top: auto;
  padding-top: 10px;
  padding-bottom: 12px;
  border-top: 1px solid var(--rd-line);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
  line-height: 1.45;
  text-wrap: pretty;
}

/* ---------------------------------------------------------------------------
   The public org page — the second screen of the same drawing (16 Orgs,
   ScreenOrgPublic). Rendered by `components/org_public.rs`, which is ONE
   renderer with two mounts: the shooter at /orgs/:id, and the Pro-side preview
   inside a 320px phone frame on 33 Pro Org Settings.

   ⚠️ THAT SECOND MOUNT IS WHY EVERY RULE HERE IS FLUID. The Pro preview drops
   this markup into a 320px box on a desktop console. Nothing below may assume
   the shooter shell's width, and nothing below may set a width of its own —
   the frame decides, and the card fills it. A fixed width here would be
   invisible on the phone and wrong in the preview, which is the worst of the
   two failure orderings.

   Two drawings draw this card. Where they disagree 16 Orgs wins (ruled
   2026-08-13), so the geometry below is the shooter export's: 46px rows, .04em
   name tracking, a 24px display heading over a chip strip. The Pro preview's
   own numbers — 9px row padding, .03em, a 17px name beside an OrgMark — are
   deliberately NOT here.

   ⚠️ SHOOTER SIDE, so the kit primitives are native and the Pro families
   (rd-set-*, rd-pub-*) are the foreign ones — the same reading the search
   screen above states. The chips, heading, body copy and micro-label are all
   kit call sites; what is left for this file is the rhythm between them, the
   row, and the two quiet type roles the kit does not carry.
   --------------------------------------------------------------------------- */
.rd-org { display: flex; flex-direction: column; min-height: 0; flex: 1; }

/* The identity strip: `display: flex, gap: 6, marginBottom: 9`. */
.rd-org__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 9px;
}

/* The drawn `margin: '10px 0 14px'` around the description. The paragraph
   itself is `RdText`, so only the space is here. */
.rd-org__desc { margin: 10px 0 14px; }

/* 4px under the `Public drills` label, and the list is the part that scrolls
   when an org has more drills than the frame is tall. `min-height: 0` is what
   lets it: a flex child defaults to `min-height: auto` and refuses to shrink
   below its content, which in the 320px preview frame pushes the locked door
   off the bottom edge instead of scrolling. */
.rd-org__list {
  margin-top: 4px;
  min-height: 0;
  overflow-y: auto;
}

/* The empty list. UNDRAWN — neither export draws an org with no public drills
   — so this takes the meta step and no more. */
.rd-org__none {
  margin: 0;
  padding: 8px 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
  line-height: 1.45;
  text-wrap: pretty;
}

/* A drill row. 46 min-height and a hairline between, none after the last —
   `:last-child` rather than an index test, so the rule holds however the list
   is built. A real <button>, so it is reachable and announced as a control;
   the drawing's <div> is a preview harness with no accessibility tree. */
.rd-org__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 46px;
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  padding: 0;
  cursor: pointer;
}

.rd-org__row:last-child { border-bottom: none; }
.rd-org__row:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

.rd-org__row-text { display: flex; flex-direction: column; min-width: 0; }

.rd-org__row-name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .04em;
  font-size: 11.5px;
  color: var(--rd-fg);
}

/* `Drill · 24 rds`. The meta step again — `RdText` owns --rd-fg2 and has no
   colour prop, so --rd-fg3 copy is the page's, exactly as the search screen
   above records for its hint and footnote. */
.rd-org__row-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
  margin-top: 2px;
}

.rd-org__chev { flex: none; stroke: var(--rd-fg3); }

/* The locked door. `margin-top: auto` is the whole reason it reads as the
   answer to "what do I press" rather than as a footer: it sits where every
   other screen puts its CTA, at the foot of whatever space is left. */
.rd-org__foot {
  margin-top: auto;
  flex: none;
  padding-bottom: 14px;
}

.rd-org__door {
  border-top: 1px solid var(--rd-line);
  padding-top: 11px;
}

/* The caps line states the rule. --rd-fg2 rather than --rd-fg3: this is the
   louder of the two lines, and the drawing steps them apart. */
.rd-org__door-head {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .12em;
  font-size: 9px;
  color: var(--rd-fg2);
}

/* And the sentence under it is the one doing the work — "membership is by
   invitation" alone still leaves a reader hunting for where to ask. */
.rd-org__door-body {
  margin: 4px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
  line-height: 1.45;
  text-wrap: pretty;
}

/* ==== S16-YOURS · 16 Orgs row 07 — Your Orgs + the closed-org record (rd-relay-15aug.jsx) · owner: lane-3 ==== */

/* ---------------------------------------------------------------------------
   The shooter's closed-org vocabulary, both screens:

     `/profile/orgs`      the picker      (`pages/your_orgs.rs`)
     `/profile/orgs/:id`  the org record  (`pages/org_record.rs`)

   DRAWING: `rd-relay-15aug.jsx` §4, `ScreenOrgClosed` — the `picker` branch and
   the record branch of one component, which is why one part paints both. They
   are the same document's two screens and are read together.

   ⚠️ THE SECOND NAME SPACE OFF DRAWING 16. `s16.css` (owner: lane-2) owns that
   document's Find An Org screens with `.rd-find*`; this file owns `.rd-yorgs*`
   and `.rd-orgrec*`. The three sets are disjoint by construction — neither file
   declares or overrides a selector the other names — so the two do not contend
   and their relative order is not load-bearing.

   ⚠️ THE TABLET VARIANT IS NOT HERE. `16 (Tablet) Orgs` row 02 draws the same
   record in a pane, with the Neutral strip at the top of the PANE rather than on
   the shell. `RdBanner` already supports that without a variant (its own doc
   records the measurement), and nothing in this file declares width, position or
   a shell mount — so the tablet lands as a mount change, not as a repaint.

   Every colour here is a theme token, so both themes come from `_legacy-tokens.css`
   and neither is written twice.
   --------------------------------------------------------------------------- */

/* ── The picker ──────────────────────────────────────────────────────────── */

.rd-yorgs {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
}

/* The drawn `marginTop: 16` under the heading. */
.rd-yorgs__list {
  margin-top: 16px;
}

/* One row: 62 min-height, 12 gap, a hairline under every row including the
   last — the drawing puts `borderBottom` on all three with no last-child
   exception, which reads as a list that continues rather than one that stops.

   ⚠️ IT IS AN `<a>`, AND THE RESET IS PART OF THE DRAWING. Both states are a
   `Link` — a closed row stays selectable, which is the ruling's own point — so
   the row needs the anchor's underline and inherited colour taken off it, and
   its own two type roles put back below. */
.rd-yorgs__row {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 62px;
  border-bottom: 1px solid var(--rd-line);
  text-decoration: none;
  color: inherit;
}

.rd-yorgs__row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* The dimmed state. It moves the NAME to the middle step and nothing else: the
   subline and the tag are already `--rd-fg3`, and dimming the whole row would
   say the row is disabled, which is the one thing it is not. */
.rd-yorgs__row--closed .rd-yorgs__name {
  color: var(--rd-fg2);
}

.rd-yorgs__text {
  flex: 1;
  min-width: 0;
}

/* 13.5px body, full-strength. Truncates rather than wraps: an org name is one
   line in the drawing and a two-line name would break the 62px rhythm. */
.rd-yorgs__name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* `38 members`, or the closed row's sentence. 10.5px, meta step, 3px under the
   name — all three drawn. It WRAPS where the name truncates, because the closed
   variant is a sentence and a clipped reassurance is worse than a tall row. */
.rd-yorgs__sub {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.35;
  color: var(--rd-fg3);
}

/* The `Closed` tag, in the chevron's place. The drawn display role at 8.5px:
   700 weight, 78% stretch, .1em tracking, uppercase, hairline border, 3px/7px
   padding.

   ⚠️ NOT `.rd-lab`. That role is 8.5px display type too, but its tracking is
   .2em and it is a section label, not a bordered tag — borrowing it would make
   a change to section labels repaint this tag. */
.rd-yorgs__tag {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8.5px;
  line-height: 1;
  color: var(--rd-fg3);
  border: 1px solid var(--rd-line);
  padding: 3px 7px;
}

.rd-yorgs__chevron {
  flex: none;
  width: 7px;
  height: 12px;
  color: var(--rd-fg3);
}

/* The two-sentence foot: 11px, meta step, 16 above, 1.5 line-height. */
.rd-yorgs__foot {
  margin-top: 16px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.5;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

/* ── The org record ──────────────────────────────────────────────────────── */

.rd-orgrec {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  padding-top: 4px;
}

/* `Closed 12 August · you were a member for 14 months`. 12px, meta step, 10
   under the heading. Wraps: the second clause is only present sometimes, and a
   line that truncates would drop the half that varies. */
.rd-orgrec__sub {
  margin-top: 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  line-height: 1.4;
  color: var(--rd-fg3);
}

/* The `What you keep` block. 18 above the label, 8 between it and the rows. */
.rd-orgrec__keep {
  margin-top: 18px;
}

.rd-orgrec__keep-list {
  margin-top: 8px;
}

/* One kept thing: 44 min-height, 12 gap, hairline under all but the last. The
   `:last-child` exception IS drawn here — the record's list is finite and
   closed, where the picker's is a list you scroll. */
.rd-orgrec__keep-row {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-orgrec__keep-row:last-child {
  border-bottom: none;
}

.rd-orgrec__keep-key {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
}

.rd-orgrec__keep-val {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* The foot that says the absent controls out loud. 11.5px, meta step, 18
   above, 1.55 line-height — all drawn. It is the last thing on the screen
   because it is the answer to the question the screen provokes. */
.rd-orgrec__foot {
  margin-top: 18px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

/* NOT DRAWN — the refusal arm's way back to the picker, offered as a link
   rather than taken as a redirect. Quiet accent text, the weight the corpus
   uses for a view switch that is not an act. */
.rd-orgrec__back-link {
  align-self: flex-start;
  margin-top: 14px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 10px;
  color: var(--rd-acc);
  text-decoration: none;
}

.rd-orgrec__back-link:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* ---------------------------------------------------------------------------
   leave-an-org — the exit, on the OPEN record only.

   DRAWING: `34 (Desktop) Pro Members` -> `ScreenLeaveOrg`, frame
   `Shooter · Profile · Your Orgs`. The drawn affordance is deliberately NOT a
   button-shaped control: it is `Leave` as plain 12px text in `fg2`, sitting at
   the end of the row. Drawn that way and kept that way — a filled destructive
   button on a screen whose whole job is a calm list would make leaving look
   like the thing you came here to do.

   The destructive weight lives in the CONFIRM, which is the kit's
   `RdConfirmModal` in its `Destructive` kind (`.rd-confirm*`, adm1-5-1.css) —
   so this part paints the trigger and borrows every pixel of the dangerous
   half. `--rd-danger` is danger as TEXT, which is exactly what this is; the
   `--rd-dfill` under white type belongs to the confirm's Leave button and is
   not reached for here.

   No new part: this screen already has one, and these classes are its
   screen's. Adding a file would have moved the `cli/src/css.rs` pin for two
   rules that have no home but here.
   --------------------------------------------------------------------------- */
.rd-orgrec__leave {
  align-self: flex-start;
  margin-top: 18px;
  padding: 0;
  border: 0;
  background: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-danger);
  cursor: pointer;
  text-underline-offset: 3px;
}

.rd-orgrec__leave:hover {
  text-decoration: underline;
}

.rd-orgrec__leave:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}
/* ==== S16-TABLET · 16 (Tablet) Orgs — the two paned org screens · owner: lane-3 ==== */

/* ---------------------------------------------------------------------------
   Drawing 16's TABLET forms, both of them:

     `/orgs`              Find An Org, paned   `ScreenTabOrgs`
                          (rd-tablet-social.jsx:282)
     `/orgs/:id`          the public page, one column   (org_page.rs)
     `/profile/orgs`      Your Orgs, paned, nothing picked
     `/profile/orgs/:id`  the same screen with the record in the pane
                          `ScreenTabOrgClosed` (rd-tablet-relay.jsx:18)

   ⚠️ THE THIRD NAME SPACE OFF DRAWING 16, and the three are disjoint by
   construction. `s16.css` (owner: lane-2) owns `.rd-find*` and `.rd-org*`;
   `s16-yours.css` (owner: lane-3) owns `.rd-yorgs*` and `.rd-orgrec*`; this
   file owns `.rd-tfind*`, `.rd-tyorgs*`, `.rd-torec*` and `.rd-torg*`. Nothing
   here DECLARES a selector either of those files names.

   ⚠️ WHAT IT DOES BORROW, AND WHAT IT OVERRIDES — the position in order.txt is
   load-bearing for exactly two rules and for nothing else.

     BORROWED, NEITHER DECLARED NOR OVERRIDDEN: `.rd-find__box` and
     `.rd-find__input` (the search field, mounted a second time at the top of
     the list column — including s16.css's `(0,0,2,0)` defence against the
     pre-kit `input[type="text"]` backfill, which comes with the classes rather
     than being re-derived here), `.rd-org*` (the public body in the detail
     pane) and `.rd-orgrec*` (the record in the detail pane).

     OVERRIDDEN, both scoped under a class this file declares, so neither can
     reach the phone: `.rd-torec .rd-orgrec__keep` takes the drawn 520px block
     width and `.rd-torec .rd-orgrec__foot` takes the drawn 68ch measure. Both
     are (0,0,2,0) against s16-yours.css's (0,0,1,0) and would win on
     specificity alone; the position keeps them winning if that ever changes.

   ⚠️ NO @MEDIA IN THIS FILE, and that is the substrate's rule rather than an
   omission. The breakpoint is asked ONCE, in Rust — `use_device_class()` picks
   `RdTabletShell` over `RdShell` — and these rules paint what that choice
   mounted. A second copy of `1024px` here would be a number to keep in step
   with `TABLET_MIN_WIDTH_PX` and its CSS twin in kit-tablet-shell.css, which
   is the drift that file's own test exists to prevent.

   Every colour is a theme token, so both themes come from `_legacy-tokens.css`
   and neither is written twice.
   --------------------------------------------------------------------------- */

/* ===========================================================================
   1. Find An Org — the list column
   =========================================================================== */

/* The search strip, at the TOP OF THE LIST COLUMN and pinned there: *"Search
   stays at the top of the list column. It is the only input on the page and it
   belongs with the results it filters"* (16 (Tablet) Orgs, Decided — This
   Device).

   `flex: none` is what pins it — the list pane is a flex column that scrolls,
   and a shrinkable strip would compress the field as results arrive. Drawn
   `padding: '14px 16px', borderBottom: 1px solid t.line`
   (rd-tablet-social.jsx:288).

   ⚠️ THE BUTTON BESIDE THE FIELD IS GONE, and the flex row with it. It was
   carried as undrawn-and-flagged ("the drawing is a still, and the shipped
   field has always needed something to submit it"), but the field submits on
   Enter through `RdSearchField` and searches as you type, so the control was
   duplicating a behaviour rather than supplying one — the same reading that
   retired the People page's (`loading-and-rulings-reply.md` item 4). This strip
   is a bare field div in the drawing (`rd-tablet-social.jsx:287-291`) and is
   one again. */
.rd-tfind__search {
  flex: none;
  padding: 14px 16px;
  border-bottom: 1px solid var(--rd-line);
  box-sizing: border-box;
}

.rd-tfind__error {
  flex: none;
  padding: 10px 16px 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-danger);
}

/* The results, and the only part of the column that grows. `min-height: 0` so
   it can shrink under the foot rather than pushing it out of the pane. */
.rd-tfind__rows {
  flex: 1;
  min-height: 0;
}

/* The no-match state, in the LIST column rather than over the whole screen —
   the search failed, and the search is here. Drawn `padding: '20px 18px'`
   (rd-tablet-social.jsx:293).

   The copy is the PHONE's two sentences, not the drawn tablet paraphrase. The
   drawn frame compresses them into one, and the phone's pair is pinned
   verbatim by a test in org_search.rs because the second sentence carries the
   rule — a failed search does not mean the org is absent. A rule is a fact
   about the page and not about a screen size, which is the same reason
   content/16-orgs.js exists. */
.rd-tfind__nomatch {
  padding: 20px 18px;
}

.rd-tfind__nomatch-head {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg2);
}

.rd-tfind__hint {
  margin: 8px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.6;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

/* One result. 68 min-height, `padding: '12px 16px'`, a hairline under every row
   and a 3px left rule that is transparent until the row is the selected one
   (rd-tablet-social.jsx:295).

   ⚠️ THE LEFT RULE IS ALWAYS THERE. Declaring it only on the selected row
   would move every other row 3px sideways as the selection travels — the
   drawing sets `borderLeft: 3px solid transparent` for exactly that reason.

   A real `<button>`: on this device the row changes what is beside it and goes
   nowhere, so it is a control over a pane rather than a link to an address.
   The reset below is what a `<button>` needs to look like a row. */
.rd-tfind__row {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  width: 100%;
  min-height: 68px;
  padding: 12px 16px;
  box-sizing: border-box;
  text-align: left;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  border-left: 3px solid transparent;
  cursor: pointer;
}

.rd-tfind__row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* The selected row: the accent edge and the raised ground, both drawn. */
.rd-tfind__row--sel {
  border-left-color: var(--rd-acc);
  background: var(--rd-surf2);
}

/* `tdisp(700, 78, '.01em')` at 13px, clamped to one line. */
.rd-tfind__name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .01em;
  font-size: 13px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The description, one line. It CLIPS where the phone's wraps: a result row
   here sits beside the same description in full, so the truncation costs
   nothing the reader cannot already see. */
.rd-tfind__desc {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The discoverability footnote, at the foot of the LIST column, where it
   qualifies the rows above it rather than the pane beside them. */
.rd-tfind__foot {
  flex: none;
  padding: 14px 16px;
  border-top: 1px solid var(--rd-line);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.5;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

/* ===========================================================================
   2. Find An Org — the detail pane, and the emptiness that is the design
   =========================================================================== */

/* Drawn `padding: '26px 30px'` (rd-tablet-social.jsx:302).

   ⚠️ THERE IS NO RULE BELOW THE BODY, AND THAT IS THE WHOLE SECTION. *"The
   pane stays mostly empty and that is the design. Every candidate for filling
   it — members, upcoming events, drills, a join button — is forbidden by the
   07-21 ruling or does not exist on the wire. A tablet layout that invents
   content to avoid whitespace is worse than one that leaves it."* So this
   declares a box with padding and a reading measure, and nothing that would
   fill the space under `.rd-org`.

   `max-width: 60ch` is the drawn measure (`maxWidth: '60ch'` on the
   description). It is on the PANE rather than on the paragraph because
   `.rd-org__desc` is s16.css's and the phone renders it inside a 448px column
   where a 60ch cap would never bite — putting the cap here scopes it to the
   width that needs it. */
.rd-tfind__detail {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  padding: 26px 30px;
  max-width: 60ch;
  box-sizing: border-box;
}

/* NOT DRAWN — the nothing-selected line. The meta step and one sentence: this
   pane is empty on purpose even when a result IS picked, so its unpicked state
   must not be louder than its picked one. */
.rd-tfind__unpicked {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

/* ===========================================================================
   3. `/orgs/:id` alone — the public page as one content column
   =========================================================================== */

/* The same body with no list beside it: you reached this URL from outside the
   search, so there is no query and no results, and a list column holding an
   empty search box would be a pane asserting a list that does not exist. The
   measure is the detail pane's, so the page reads identically whichever way
   it was reached. */
.rd-torg__solo {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
  padding: 26px 30px;
  max-width: 60ch;
  box-sizing: border-box;
  overflow-y: auto;
}

/* ===========================================================================
   4. Your Orgs — the list column
   =========================================================================== */

.rd-tyorgs__list {
  flex: 1;
  min-height: 0;
}

/* One org. 66 min-height, `gap: 11`, `padding: '0 16px'`, the hairline under
   every row and the same always-present 3px left rule the results list takes
   (rd-tablet-relay.jsx:26).

   ⚠️ IT IS AN `<a>` AND IT STAYS ONE, closed or open. Both states are a `Link`
   to the same route — the ruling this screen exists to carry — so the row
   needs the anchor's underline and inherited colour taken off it, exactly as
   the phone's row does. */
.rd-tyorgs__row {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 66px;
  padding: 0 16px;
  box-sizing: border-box;
  border-bottom: 1px solid var(--rd-line);
  border-left: 3px solid transparent;
  text-decoration: none;
  color: inherit;
}

.rd-tyorgs__row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-tyorgs__row--sel {
  border-left-color: var(--rd-acc);
  background: var(--rd-surf2);
}

/* ⚠️ THE CLOSED ROW DIMS ITS NAME AND NOTHING ELSE. *"The closed row keeps its
   tag, its readability and its selection. Not struck through and not dimmed to
   the point of looking disabled — the record is the reason it is still on the
   list."* The subline and the tag are already the meta step, and a rule that
   dimmed the row would say it is inert, which is the one thing it is not.

   It composes with `--sel`: a closed row that is also the selected one keeps
   the accent edge and the raised ground. */
.rd-tyorgs__row--closed .rd-tyorgs__name {
  color: var(--rd-fg2);
}

.rd-tyorgs__text {
  flex: 1;
  min-width: 0;
}

/* 13px body, clamped to one line — the drawn `tclamp(1)`. */
.rd-tyorgs__name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* `38 members`, or the closed row's *You keep everything you recorded here*.
   Clamped to one line here where the phone's wraps: the sentence is repeated
   in full in the pane beside it the moment the row is picked. */
.rd-tyorgs__sub {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The bordered `Closed` tag: `tdisp(700, 78, '.1em')` at 8.5px with a hairline
   border and 3px/7px padding, all drawn. `flex: none` so a long org name
   truncates instead of squeezing the tag. */
.rd-tyorgs__tag {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8.5px;
  line-height: 1;
  color: var(--rd-fg3);
  border: 1px solid var(--rd-line);
  padding: 3px 7px;
}

/* The picker's two-sentence foot, at the foot of the column it explains. */
.rd-tyorgs__foot {
  flex: none;
  padding: 14px 16px;
  border-top: 1px solid var(--rd-line);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.5;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

/* The empty account: no rows, so no list, so no panes — one column holding the
   empty state. */
.rd-tyorgs__solo {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
  padding: 26px 30px;
  box-sizing: border-box;
  overflow-y: auto;
}

/* ===========================================================================
   5. Your Orgs — the record in the detail pane
   =========================================================================== */

/* Drawn `padding: '22px 26px'` (rd-tablet-relay.jsx:37). A flex column because
   the refusal arm's way back (`.rd-orgrec__back-link`) is `align-self:
   flex-start` and needs one. */
.rd-torec {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
  padding: 22px 26px;
  box-sizing: border-box;
}

/* ⚠️ THE NEUTRAL STRIP, MOUNTED AT THE TOP OF THE PANE. *"The Neutral strip
   sits at the top of the pane rather than on the shell. On this device the
   sentence is about the record beside it, not about the app."*

   Only the SPACE under it is here. The strip itself is `RdBanner`'s Neutral
   tone, unchanged and unmodified — the component is mount-agnostic and its own
   doc cites this frame, so nothing about the strip is repainted for this
   position. Drawn `marginBottom: 18`. */
.rd-torec__strip {
  flex: none;
  margin-bottom: 18px;
}

/* NOT DRAWN — the nothing-picked line, before a row is chosen. */
.rd-torec__unpicked {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

/* The two overrides this file's banner names, both scoped under `.rd-torec` so
   the phone's record cannot see either.

   The drawn block width, `maxWidth: 520` (rd-tablet-relay.jsx:48). The phone
   has no cap because a 448px column is already narrower than one. */
.rd-torec .rd-orgrec__keep {
  max-width: 520px;
}

/* The foot's drawn measure, `maxWidth: '68ch'` (rd-tablet-relay.jsx:60) — the
   one number the drawing gives in characters rather than pixels, because it is
   a measure for reading and not a box. */
.rd-torec .rd-orgrec__foot {
  max-width: 68ch;
}
/* ==== PROFILE-COMMUNITY · 13/14/18/19/20 (design-corpus-2026-08-10) · owner: lane-2 ==== */

/* ---------------------------------------------------------------------------
   The shooter's Profile & Community cluster: achievements, profile, friends,
   friend code, messages, challenges. One file for the arc rather than one per
   screen — the six share one idiom, one lane owns them all, and a file per
   screen would put six banners in `order.txt` for rules that never collide.

   ⚠️ SHOOTER SIDE. The kit primitives are native here and the Pro families
   (`rd-set-*`, `rd-ev-*`) are the foreign ones — the same orientation `s16.css`
   states. Anything the kit already carries is composed at the call site, not
   restated here: the chip row is `RdChipSelector`, the heading is `RdHeading`,
   body copy is `RdText`. What lands in this file is the rhythm between them
   and the marks the kit does not carry.

   POSITION. This file must stay after `_legacy-pre-kit.css`. That backfill
   holds element-qualified selectors — `input:focus { box-shadow: … }` at
   (0,0,1,1) — which outrank a bare page class at (0,0,1,0); the same collision
   cost `org_settings.rs` three explicit defences. The constraint is "after the
   pre-kit backfill", not an index: a re-order that preserves the number and
   breaks the relation has broken the thing that matters.

   It sits before the `adm*` sections deliberately. The families are disjoint,
   so a collision would be a mistake either way — but if one happens it should
   resolve in the admin console's favour, because that surface is drawn,
   reviewed, and paid for.
   --------------------------------------------------------------------------- */

/* ── Shared across the arc ─────────────────────────────────────────────────── */

/* ⚠️ **The back row is the SCREEN's, and that is the kit's own rule, not a
   workaround.** The drawings give these screens a shell back (`TShell
   back="Profile"`), and `RdShell` does carry a `back` prop — but `ShellLayout`
   owns the shell for all 41 shooter routes and passes `Gutter::Flush`, which
   `flush_gutter_conflicts_with_shell_bar` asserts against a shell-owned bar: a
   shell bar in a flush shell renders its label hard against the screen edge.
   `Gutter::Flush`'s own doc records the resolution — *"all 13 flush sites
   supply their own top bar inside the content region"*. So this is the drawn
   affordance in the sanctioned place, not a substitute for one.

   42px tall because the whole row is one target: a chevron alone is far below a
   usable size outdoors with gloves, which is the reasoning `.rd-shell__back`
   already carries for the kit's version. */
.rd-pc__back {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  height: 42px;
  flex: 0 1 auto;
  min-width: 0;
  background: none;
  border: 0;
  padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-fg2);
  cursor: pointer;
}

.rd-pc__back:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* Tan is the accent everywhere else, but the designer's own nav note rules
   white over the accent outdoors — the same call `.rd-shell__back` makes. Guarded on
   (hover: hover) so a tap does not leave a stuck hover on a touch device. */
@media (hover: hover) {
  .rd-pc__back:hover { color: var(--rd-fg); }
}

/* ⚠️ THE NO-CLAIM GROUND, SHARED BY THE ARC (docs/design/kit/L1-loading.md §4.1,
   43 Loading States.html:52): "layout not predictable — the no-claim fallback:
   chrome real, ground empty, one 26px sweep". The mark and the watchdog are the
   kit's (`RdSweep`, `RdSweepStall`); what this file owns is the ground they
   stand on, and it is `rd-pc__` because three screens in the arc need the same
   one — the announcement being read, the code being minted, the code being
   looked up. It is deliberately NOT a skeleton anywhere it is used: each of
   those three resolves to a shape the screen cannot predict. */
.rd-pc__wait {
  padding: 32px 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* The WARM mark — 43 Loading States.html:50: content already on screen is never
   dimmed, blanked or covered, so a refetch changes exactly one thing. The 12px
   sweep is the freshness size; these lists carry no freshness line of their own,
   so the mark rides the top of the container that carries `aria-busy`. */
.rd-msg__refresh,
.rd-msg-t__refresh {
  display: flex;
  justify-content: flex-end;
  padding: 4px 0;
}

/* ── 18/19 Profile · ScreenProfileC (rd-profile-collapsed.jsx:15) ──────────── */

.rd-prof { display: flex; flex-direction: column; min-height: 0; }

.rd-prof__header { display: flex; align-items: center; gap: 12px; }

.rd-prof__ident { flex: 1; min-width: 0; }

/* Always the real display name — unlike the drawing's `fresh` toggle (a
   brand-new, nothing-set-yet account), a signed-in shooter always has one;
   `fresh`'s "Tag not set" swap is for that specific edge case, not for tag
   absence in general. See the tag note below and the file header doc. */
/* ⚠️ **THE NAME WRAPS. IT IS NEVER ELLIPSISED** — ruled 2026-08-27, C-44.1.
   `44 The Doors.html:50`: *"the name must survive the 208px rail untruncated,
   since naming the destination is the fixture's whole point — and where a name
   still overruns, it wraps to two lines; it is never cut (ruled 27 Aug)."*
   `rd-doors.jsx:66-68` states the same ruling and names this rule as the drift:
   *"that rule stands and long names WRAP — the fixture's old nowrap/ellipsis
   contradicted the rule it demonstrates, and the shipped Profile row's ellipsis
   is drift to fix."*

   This rule therefore declares NO `white-space`, NO `overflow` and NO
   `text-overflow`. The two-line bound is `.rd-clamp-2` (kit-clamp.css), carried
   at the call site (`pages/profile.rs`) — the same migration kit-clamp.css
   records for its four other consumers, and the reason a fifth hand-rolled
   `-webkit-box` quartet does not appear here. It matters that this rule stays
   silent about `display` and `overflow`: profile-community.css sits BELOW
   kit-clamp.css in `order.txt` (471 vs 329), so either declaration here would
   out-specify the utility at equal specificity and silently unclamp it — the
   hazard kit-clamp.css names in as many words.

   ⚠️ `line-height` 1.25, NOT the drawn 1. A wrapping name needs leading, and
   1.25 is not invented: it is the value the door's own name line takes for the
   same two-line case (`rd-doors.jsx:48` — `lineHeight: 1.25`). At 1 the second
   line's caps touch the first line's descenders at 900/62%. */
.rd-prof__name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 900;
  font-stretch: 62%;
  letter-spacing: .01em;
  font-size: 18px;
  line-height: 1.25;
  color: var(--rd-fg);
  overflow-wrap: anywhere;
}

/* ⚠️ **NOW BOTH ARMS — resolved 2026-08-15.** This rule used to carry a note
   saying the caller's own `username` was unreachable from any type the
   originating dispatch's SCOPE could touch: `CurrentUser` (`session.rs`) had no
   such field, so the prompt copy was not a choice between two strings but the
   only string that could be drawn. `CurrentUser` carries `username` now, and
   the element renders what the drawing draws — `@handle` when there is one,
   the "set a tag" prompt when there is not (`rd-profile-collapsed.jsx:26`).

   The prompt copy is unchanged and stays for the unset case on its original
   merit: it is a call to action rather than a claim about state. What changed
   is that it is a branch instead of a constant. The geometry serves both — a
   handle can be longer than the column, which is what the ellipsis is for. */
.rd-prof__tag {
  margin-top: 4px;
  font-size: 11px;
  color: var(--rd-fg3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* `Since Mar 2023` — the join date, on its OWN line and one step smaller than
   the tag above it (10px vs 11px, same `--rd-fg3`). Drawn at
   `rd-profile-collapsed.jsx:27`, and the separate line is the drawing's
   argument rather than a layout accident: *"the tag drops the join date onto
   its own smaller line rather than running long."* No dot, no bullet, no
   separator — it is not a second half of the tag line.

   No ellipsis machinery here, unlike the two lines above: this string is nine
   characters plus a month and can never overflow the column. */
.rd-prof__since {
  margin-top: 2px;
  font-size: 10px;
  color: var(--rd-fg3);
}

.rd-prof__code-btn {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  background: none;
  border: 0;
  padding: 4px;
  color: var(--rd-fg2);
  text-decoration: none;
  cursor: pointer;
}

.rd-prof__code-btn:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

.rd-prof__code-btn-inner {
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.rd-prof__section { margin-top: 16px; }

.rd-prof__section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 9px;
}

.rd-prof__section-count {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9px;
  color: var(--rd-acc);
}

.rd-prof__friends-empty,
.rd-prof__friends-row {
  display: block;
  color: inherit;
  text-decoration: none;
}

.rd-prof__friends-row { display: flex; align-items: center; gap: 10px; padding: 4px 0; }
.rd-prof__friends-row:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

.rd-prof__friends-empty-inner {
  padding: 12px 13px;
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 56px;
}

.rd-prof__friends-empty-text { flex: 1; }

.rd-prof__friends-empty-title {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 11.5px;
  color: var(--rd-fg);
}

.rd-prof__friends-empty-sub {
  margin-top: 3px;
  font-size: 10.5px;
  color: var(--rd-fg3);
}

.rd-prof__friends-empty-add {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-acc);
}

.rd-prof__facepile { display: flex; }

/* The cut-out overlap: each face after the first steps back 9px under a
   border matched to the page ground, so the stack reads as separate faces
   rather than a solid strip. */
.rd-prof__face {
  border-radius: 30px;
  border: 2px solid var(--rd-bg);
}

.rd-prof__face + .rd-prof__face { margin-left: -9px; }

.rd-prof__friends-names { flex: 1; font-size: 11px; color: var(--rd-fg3); }

.rd-prof__chevron { flex: none; width: 7px; height: 12px; color: var(--rd-fg3); }

.rd-prof__list { margin-top: 18px; border-top: 1px solid var(--rd-line); }

.rd-prof__row {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 52px;
  padding: 0 2px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-prof__row:last-child { border-bottom: none; }

.rd-prof__row--live { color: inherit; text-decoration: none; cursor: pointer; }
.rd-prof__row--live:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: -2px; }

/* ⚠️ **THE ACT ROW — the hub's one row that does not go anywhere.** Sign Out
   (`pages/profile.rs`, the last row) acts in place, so it is a `<button>` and
   carries no chevron — `edit_account.rs` states the rule once for both screens:
   a chevron means a screen, and one over an act promises a screen that never
   arrives.

   Everything here is the button reset and nothing here is paint. The row's
   geometry, its hairline and its ink all come from `.rd-prof__row` and
   `.rd-prof__row-name` above, which is the whole point: a quiet link row, in
   the list's own grammar, NOT a second treatment. The one thing it must undo is
   the user-agent button: a border on four sides where the row draws one, a grey
   ground, a centred 13.33px system font, and a width that shrinks to its text.
   Red is deliberately NOT spent here — `19 (Mobile) Profile Pages` colours this
   row where it sits next to Delete Account and needs telling apart from it;
   nothing red is adjacent on the landing. */
.rd-prof__row--act {
  width: 100%;
  box-sizing: border-box;
  border-left: 0;
  border-right: 0;
  border-top: 0;
  background: none;
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
}

.rd-prof__row--act:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: -2px; }

.rd-prof__row-text { flex: 1; min-width: 0; }

.rd-prof__row-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 12px;
  color: var(--rd-fg);
}

.rd-prof__row-sub { margin-top: 3px; font-size: 10.5px; color: var(--rd-fg3); }

/* ⚠️ **NEVER THE ACCENT VARIANT.** The drawing gives Messages' count an
   accent fill for "something new", which needs the same read-state the wire
   does not carry — the exact gap `messages.rs` (#692) already ruled on. This
   chip is always the neutral `--rd-chip` ground; adding an accent path here
   would be a second, quieter reintroduction of the mark #692 removed. */
.rd-prof__row-count {
  flex: none;
  padding: 3px 7px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 9.5px;
  color: var(--rd-fg2);
  background: var(--rd-chip);
  clip-path: polygon(5px 0, 100% 0, 100% calc(100% - 5px), calc(100% - 5px) 100%, 0 100%, 0 5px);
}

/* ── 20 Achievements · ScreenAchievements (rd-achievements.jsx:120) ────────── */

.rd-ach { display: flex; flex-direction: column; min-height: 0; flex: 1; }

/* Title and the earned figure share a baseline row: the drawn `alignItems:
   'flex-end'` with a 12px gap, the figure nudged 2px off the bottom so its
   digits sit on the heading's baseline rather than its box.

   ⚠️ THE FIGURE IS ONE NUMBER SINCE 2026-08-29. It was `9` over a quieter
   `/40`, above a 2px progress hairline; James ruled that only earned
   achievements are listed or returned, so the denominator counted rows the
   server does not send. The denominator's own rule, the progress hairline and
   its fill all went with the markup — a rewrite owns the CSS it strands, and
   nothing in CI can see this direction. (Their selectors are deliberately not
   spelled here: a deleted name written into a comment reads as a live one to
   both the dead-class checker and the screen's own sheet sweep.) */
.rd-ach__head {
  display: flex;
  align-items: flex-end;
  gap: 12px;
}

/* ⚠️ The VOICE comes from `.rd-num`, composed at the call site — this rule sets
   only what the drawing changes about it (size, colour, position).
   `substrate §P2`, adjudicated 2026-08-04: the numeric role is Archivo 800 /
   stretch 75% / tabular-nums and explicitly **NOT a system mono** — *"numbers
   are the loudest thing on a scoring surface, so they speak in the display
   voice"*, and `ui-monospace` survives only for verbatim technical strings,
   never for numbers. Restating a font-family here would fork that decision, and
   `check css` cannot see it: it verifies class resolution and font-stretch
   values, not which family a rule names.

   Tabular is load-bearing rather than decorative, and it comes with the role —
   which is the whole reason to compose rather than restate. No sheet-wide guard
   enforces this: the ruling is "never for numbers", not "never", and mono stays
   correct for verbatim technical strings (`adm-drillf.css:214`, the `/kit`
   review surface), so a blanket check would fail on rules that are right.
   Composing the role is the enforcement available.

   Colour is fg2, not the role's default fg: the count is a fact but the heading
   beside it is the subject, and two full-strength voices in one baseline row
   read as two headings. */
.rd-ach__count {
  flex: none;
  padding-bottom: 2px;
  font-size: 19px;
  color: var(--rd-fg2);
}

.rd-ach__list {
  flex: 1;
  min-height: 0;
  margin-top: 4px;
  overflow-y: auto;
}

/* ⚠️ EIGHT GROUPS, NOT FORTY ROWS — the drawing's own headline for row 01, and
   the reason it gives: *"Forty ungrouped rows is a scroll, not a list — and the
   eight groups are the only thing that shows the roster has a shape rather than
   a length."* The 13px top margin is the drawing's (`marginTop: 13`). */
.rd-ach__group { margin-top: 13px; }

/* Heading left, fraction right, sharing a baseline — `justifyContent:
   'space-between'` with `alignItems: 'baseline'`. */
.rd-ach__group-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

/* ⚠️ THE VOICE COMES FROM `.rd-num`, composed at the call site — exactly as
   `.rd-ach__count` above does, and for the reason stated there at length: the
   numeric role is the display voice with tabular figures, and restating a
   font-family here would fork a decision `check css` cannot see.

   ⚠️ A COUNT, NOT A FRACTION, SINCE 2026-08-29. The drawing argues the
   fraction — *"2/5 on Progression tells you where the room to move is"* — and
   James's earned-only ruling removes the denominator from every head on this
   screen. It stays the quietest number on the screen (fg3, 10px), because the
   heading beside it is the subject and the count at the top is the headline. */
.rd-ach__group-count {
  flex: none;
  font-size: 10px;
  color: var(--rd-fg3);
}

/* A row. 56 min-height and an 11px gutter are the drawing's; the hairline
   between rows is a border rather than a gap so a scrolled list has no seam
   floating at its edge.

   It is an `<a>` now — every row opens the achievement's own page — so the two
   anchor defaults the shooter sheet sets globally are turned back off here. The
   row's colour lives on the name and the description, never on the link. */
.rd-ach__row {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  min-height: 56px;
  padding: 10px 0;
  border-bottom: 1px solid var(--rd-line);
  text-decoration: none;
  color: inherit;
}

/* Last row of each GROUP, not of the list — the drawing's `last={i === g.length
   - 1}`. A group's closing hairline would double the heading rule below it. */
.rd-ach__row:last-child { border-bottom: none; }

/* ⚠️ THE KEYWORD MASK BLOCK WAS HERE AND IS DELETED (2026-08-29). It drew a
   44px rule and the sentence *"Find out by earning it"* in place of an UNEARNED
   Keyword row's description — the drawing's own argument against a blur. James
   ruled that only earned achievements are listed or returned, so the block's
   whole population left the wire; the markup went with it and these three rules
   went with the markup.

   ⚠️ THE BADGE IS A ONE-STATE MARK (`ABadge`, rd-achievements.jsx:82). It is
   drawn earned-or-not, and the unearned half went the same way as the mask —
   the roster cannot hold an unearned row. Nothing distinguishes one badge from
   another but its name and description.

   This replaced a client-side `achievement_style()` that substring-matched the
   achievement NAME against nine keywords to pick an emoji and a hex colour.
   That mapping existed on no wire and in no drawing: a server-side rename
   silently repainted a badge, and an achievement matching two keywords got
   whichever arm came first. It is deleted rather than tokenised, because the
   drawing does not ask for per-badge iconography at all.

   It is page CSS rather than `RdChamferBox` because the earned state is a SOLID
   volt fill and `Fill` has no volt variant — deliberately, since volt is a fill
   and never a stroke. The clip-path chamfer is the established page-side
   technique (`adm2.css`, `adm-plat.css`). */
.rd-ach__badge {
  /* ONE MARK AT TWO SIZES — 34 on a row, 72 on the detail page (`ABadge`'s `s`
     prop, which the drawing passes 34 and 72). The size is a custom property so
     the second site SETS it rather than overriding a declaration, and the
     drawn check inside scales with it: the markup sizes the `<svg>` in percent,
     which is the drawing's own `s * .4` by `s * .3` expressed once. */
  --rd-ach-badge-size: 34px;
  flex: none;
  width: var(--rd-ach-badge-size);
  height: var(--rd-ach-badge-size);
  display: flex;
  align-items: center;
  justify-content: center;
  clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
  /* The earned paint, folded into the base rule on 2026-08-29: it was the `--on`
     modifier beside an outlined `--off` one, and there is no unearned row left
     to wear the second. */
  background: var(--rd-volt);
  color: var(--rd-on-volt);
}

.rd-ach__body { flex: 1; min-width: 0; }

/* The name takes the full foreground. It was fg2 with an `--on` modifier lifting
   it to fg; every listed row is earned since 2026-08-29, so the modifier had no
   sibling left and the earned colour is the rule's own. */
.rd-ach__name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .04em;
  font-size: 12px;
  color: var(--rd-fg);
}

.rd-ach__desc {
  margin-top: 4px;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

/* ⚠️ `rd-ach__earned` WAS HERE AND IS DELETED (2026-08-26). It painted an
   `Earned <date>` line under every earned roster row — a line `ARow` does not
   draw (`components/rd-achievements.jsx:90-110`) and the component argues
   against by name: *"the interesting facts are when you got it and how rare it
   is, and neither belongs on a list row"*
   (`components/rd-achievements.jsx:178-179`). The markup went with it, so the
   rule had no caller left.

   The DETAIL screen's date stays and keeps its own rule, `.rd-achd__earned`
   below — which is where the drawing puts it. */

.rd-ach__msg { padding: 32px 0; color: var(--rd-fg2); font-size: 13px; line-height: 1.5; }

/* The second line under the pinned empty sentence (row 04): quieter and
   smaller, because the shipped sentence is the message and this is the reason
   to keep reading. Its own top padding is dropped — the two lines are one
   block, and 32px between them would read as two unrelated statements. */
.rd-ach__msg--sub {
  padding-top: 0;
  font-size: 11px;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

/* ── 20 Achievements · ScreenAchDetail (rd-achievements.jsx:180) ───────────── */

/* ⚠️ SAME FILE, SAME BLOCK FAMILY, DELIBERATELY. `.rd-achd*` is the detail
   page and it sits with `.rd-ach*` rather than in a part of its own: this file
   already declares drawing 20 (see its banner, `13/14/18/19/20`), and the
   detail page BORROWS the badge whole — same mark, same two states, one size
   custom property apart. `s18-foot.css`'s own warning is the argument: splitting
   one BEM block across two files is a thing to watch, and there is no second
   owner here to justify paying that cost. No part was added, so `order.txt`
   and the part-count pin in `cli/src/css.rs` are untouched (77 on this tree).

   The page is centred, which the roster is not — *"alignItems: 'center',
   textAlign: 'center'"* — because it is one thing looked at rather than a list
   read down. */
.rd-achd { display: flex; flex-direction: column; min-height: 0; flex: 1; }

.rd-achd__body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding-top: 8px;
  overflow-y: auto;
}

/* 72px, set through the badge's own custom property rather than by overriding
   its `width`/`height` — see the note on `.rd-ach__badge`. */
.rd-achd__badge { --rd-ach-badge-size: 72px; }

/* The one place in this arc a name is set in the big display voice: 24px, 900
   weight at 62% stretch, uppercased. Uppercase is the DRAWING's
   (`textTransform: 'uppercase'`) and not a house default — the roster row two
   taps back prints the same name in its own case, which is the copy's case. */
.rd-achd__name {
  margin-top: 15px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 900;
  font-stretch: 62%;
  letter-spacing: .01em;
  font-size: 24px;
  line-height: 1;
  text-transform: uppercase;
  color: var(--rd-fg);
}

/* 250px is the drawing's `maxWidth` — a centred paragraph wider than that stops
   being a caption and starts being a column, and the longest description in the
   roster fits. */
.rd-achd__desc {
  margin-top: 10px;
  max-width: 250px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--rd-fg2);
  text-wrap: pretty;
}

/* The drawn earned line, and now the ONLY one in the arc: the roster row's
   `rd-ach__earned` was deleted 2026-08-26 with the markup it painted, because
   the drawing puts this fact here and nowhere else
   (`components/rd-achievements.jsx:178-179`, `:191`). Composed from nothing —
   it was never borrowed from the row's rule, which differed in size and
   position. */
.rd-achd__earned {
  margin-top: 14px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .14em;
  font-size: 9px;
  text-transform: uppercase;
  color: var(--rd-acc);
}

/* ⚠️ THE LOCKED PAGE'S TWO RULES WERE HERE AND ARE DELETED (2026-08-29): the
   unearned description's step-back to fg3, and the *hidden on purpose* sentence
   under it. The drawing has a `Hidden, on purpose` frame; James ruled that only
   earned achievements are listed or returned, so no row can open that frame and
   both rules lost their markup. */

/* ⚠️ `margin-top: auto` IS THE DRAWN SPACER. The drawing puts a `flex: 1`
   filler above the button so it sits at the foot of whatever the page came to —
   *"Share is outlined, not volt. Nobody opened this page in order to post."*
   An `auto` margin is the same thing without an empty element to carry it. */
.rd-achd__share {
  width: 100%;
  margin-top: auto;
  padding: 20px 0 12px;
}

/* ── 19 Profile Pages · My Code (rd-profile-code.jsx, CodeCard/CodeOffline) ── */

.rd-code { display: flex; flex-direction: column; align-items: center; }

.rd-code__lab {
  align-self: flex-start;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .18em;
  font-size: 9.5px;
  color: var(--rd-fg3);
  margin-bottom: 12px;
}

/* ⚠️ **THE TILE IS WHITE IN BOTH THEMES AND THAT IS NOT A THEME BUG.**
   Do NOT "fix" this to a surface token in a theme sweep — a camera needs a
   light quiet zone around the modules to find the code at all, so the quiet
   zone is a functional requirement of the format, not a colour choice. The
   drawing states it: *"scanners need a light quiet zone, not a theme bug."*
   Same class as the focus-ring defences in `adm2.css` — a rule that looks like
   an oversight, is not, and would be silently broken by a correct-looking edit.

   The chamfer is the tile's only concession to the substrate: geometry carries
   the house style where colour cannot. */
.rd-code__tile {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px;
  background: #fff;
  box-shadow: inset 0 0 0 1px var(--rd-line);
  clip-path: polygon(12px 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%, 0 12px);
}

.rd-code__tile svg { display: block; width: 168px; height: 168px; }

/* ⚠️ **Expired BLURS rather than hides, and the direction is deliberate.**
   Leaving the shape there says *"this is the same thing, it stopped working"*;
   an empty tile reads as a loading failure, which is a different and wrong
   message. The QR stays present and unreadable.

   ⚠️ DO NOT HARMONIZE THIS WITH THE KEYWORD-MASK RULE IN `20 Achievements`.
   There, an unearned description is masked rather than blurred, because a
   blurred STRING still shows its length and for "Use the Keyword SHOTGUN…"
   the length is most of the answer. Here the persisting SHAPE is the whole
   message. Same technique, opposite verdicts, both derived from what the
   pixels communicate — a future sweep that unifies them will break one. */
.rd-code__tile--dead { background: #DADAD2; }
.rd-code__tile--dead svg { filter: blur(2.5px); opacity: .5; }

.rd-code__chip {
  position: absolute;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .16em;
  font-size: 10px;
  color: var(--rd-fg);
  background: var(--rd-bg);
  padding: 7px 11px;
  clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
}

.rd-code__copy {
  margin-top: 14px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--rd-fg2);
  text-align: center;
  text-wrap: pretty;
}

.rd-code__timer {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 7px;
  margin-top: 12px;
}

.rd-code__timer-lab {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .18em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

/* ⚠️ **TABULAR IS LOAD-BEARING HERE, not typographic taste.** The countdown
   re-renders every second and changes colour at the one-minute boundary. With
   proportional figures the glyph advances differ, so `1:00` → `0:59` reflows
   the line at the exact moment the colour changes — motion the reader reads as
   an event rather than a clock ticking. The drawing states the requirement:
   the amber *"appears without anything moving, because the numerals are
   tabular."*

   The voice comes from `.rd-num` composed at the call site, which carries
   `font-variant-numeric: tabular-nums` (substrate §P2 — Archivo 800 / 75%,
   never a system mono). This rule sets only size and colour. A test pins the
   composition, because the class list is where it would silently be lost. */
.rd-code__count { font-size: 15px; color: var(--rd-fg); }

/* Under a minute the countdown takes notification rank. Amber outranks the
   grey it sat in; it does not outrank the accent, because a code running out
   is a fact about the code and not an error the shooter has made. */
.rd-code__count--low { color: var(--rd-warn); }

.rd-code__act { margin-top: 14px; align-self: stretch; }

/* The other half of the transaction. Two people at a bay, one shows and one
   scans, and whoever opened the wrong one should not have to close this and go
   looking for Friends. Quiet by rank — it is the alternative, not the act. */
.rd-code__alt {
  margin-top: 12px;
  background: none;
  border: 0;
  padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-acc);
  cursor: pointer;
}

.rd-code__alt:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 3px; }

/* Offline. Left-aligned and headed, unlike the centred card: this is not a
   code that failed to render, it is the screen explaining that there is
   nothing to render yet. */
.rd-code__off { align-self: stretch; }

/* The rank mark, and it is AMBER rather than red on purpose: *"Nothing failed
   and nothing was denied — a range with no signal is the normal condition this
   product was built for"* (`19 (Mobile) Profile Pages.html:126`). Drawn as a
   40×40 `t.warn` square with an 11px chamfer, 16px above the heading
   (`components/rd-profile-code.jsx:59`). Bottom-right chamfer only, which is
   what the drawing's `tcham(n)` is (`components/rd-train-kit.jsx:23`). It
   carries no text and no role — the heading under it says the words. */
.rd-code__off-mark {
  width: 40px;
  height: 40px;
  margin-bottom: 16px;
  background: var(--rd-warn);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 11px), calc(100% - 11px) 100%, 0 100%);
}

.rd-code__off-h {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 900;
  font-stretch: 62%;
  letter-spacing: .01em;
  font-size: 21px;
  line-height: .95;
  color: var(--rd-fg);
}

.rd-code__off-p {
  margin-top: 11px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--rd-fg2);
  text-wrap: pretty;
}

/* The undrawn fallback: the raw code as text when the QR cannot be drawn.
   ⚠️ KEEP. It is the ONLY path to the code in that state — the code IS the
   payload and the QR is a convenience — and "not in the drawing" never deletes
   a last working path. Sized as a thing to be read aloud across a bay. */
.rd-code__raw { font-size: 24px; margin-top: 4px; }

/* ── 14 People · ScreenInbox (rd-people.jsx:266) ───────────────────────────── */

.rd-msg { display: flex; flex-direction: column; min-height: 0; flex: 1; }

/* "News and releases from T.REX Arms" — the screen's own subtitle, 7px under
   the heading. Quiet: it says what the list IS, and the list says the rest. */
.rd-msg__sub {
  margin-top: 7px;
  font-size: 11px;
  color: var(--rd-fg3);
}

.rd-msg__list {
  flex: 1;
  min-height: 0;
  margin-top: 14px;
  overflow-y: auto;
}

.rd-msg__row {
  display: flex;
  gap: 10px;
  min-height: 70px;
  padding: 11px 0;
  border-bottom: 1px solid var(--rd-line);
  width: 100%;
  text-align: left;
  background: none;
  border-left: 0;
  border-right: 0;
  border-top: 0;
  cursor: pointer;
}

.rd-msg__row:last-child { border-bottom: none; }
.rd-msg__row:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: -2px; }

/* ⚠️ **THE UNREAD MARK'S GUTTER IS RESERVED AND THE MARK IS ABSENT — this is
   the ruled interim, not an oversight.**

   The wire carries no read state: `MarkRead` writes and `FeedEntryDto` hands
   nothing back, so the client cannot know which entries are unread. A `read`
   bool is queued.

   The mark and the title's weight are ONE DATUM DRAWN TWICE — redundancy for
   scanning, not fact-plus-fallback — so they arrive together or not at all.
   Until then every row renders identically, and the square is transparent
   rather than removed so the gutter does not move when the datum lands.

   ⚠️ Do NOT "fix" this by marking every row. The shipped inbox did exactly
   that, and the drawing's own comment names the cost: it *"makes the mark mean
   'message' rather than 'unread'"*. A false version of a mark POISONS the
   correct version later — a reader who has learned the square means "message"
   will misread the real thing. Absent teaches nothing; wrong teaches wrongly. */
.rd-msg__mark {
  flex: none;
  width: 6px;
  height: 6px;
  margin-top: 5px;
  background: transparent;
}

.rd-msg__body { flex: 1; min-width: 0; }

/* Uniform 700/fg2 while the datum is missing — the quieter of the drawn pair.
   Deliberately NOT the 800/fg unread weight: rendering every row loud is the
   all-seven-blue failure wearing type instead of a square. */
.rd-msg__title {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .03em;
  font-size: 12px;
  line-height: 1.25;
  color: var(--rd-fg2);
}

/* Two lines, clamped by the box rather than by byte-truncation. The clamp is
   the better mechanism — it cuts at the rendered edge in any language and adds
   no ellipsis to a string that fits.

   ⚠️ THE CLAMP ITSELF MOVED OUT OF THIS RULE. It is `.rd-clamp-2`, applied at
   the call site in `pages/messages.rs`; this rule keeps only what is this
   element's own (its type, colour and offset). The four declarations were
   identical in four parts and the tablet kit rules the clamp belongs to the kit
   — rd-tablet-kit.jsx:21 ("Line clamp. Lives in the KIT, not in a page file").
   `messages.rs`'s expanded excerpt still unsets `-webkit-line-clamp` inline,
   which wins over the utility exactly as it won over this rule. */
.rd-msg__excerpt {
  margin-top: 4px;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
}

/* The announcement's graphic on the reading screen, above the body — the
   legacy announcement's own placement, restored.

   `contain` and not `cover`: an announcement graphic is authored artwork with
   text in it (a release banner, a product shot), and `cover` crops to fill,
   which is how a headline loses its first and last word. A letterboxed image
   shows all of what was published; a cropped one shows a confident lie about
   it. The 300px is a fixed BOX, not a scale factor, so a tall image and a wide
   one occupy the same vertical space and the body below does not move as the
   list is read.

   No background behind the letterbox bars: the page ground shows through, so an
   image narrower than the column reads as an image rather than as a card. */
.rd-msg__image {
  display: block;
  width: 100%;
  height: 300px;
  object-fit: contain;
  margin-top: 12px;
}

.rd-msg__when {
  margin-top: 5px;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

.rd-msg__msg { padding: 32px 0; font-size: 12.5px; line-height: 1.5; color: var(--rd-fg2); }

.rd-msg__more { margin-top: 16px; align-self: center; }

/* The reading screen's act slot — today it holds one door, `Open the event`,
   and it exists because the announcement's own tree is a flex column with no
   spacing of its own. UNDRAWN: `14 (Tablet) People` row 05 draws an action
   button (`Open The Assessment`) but never one for an event invitation, so this
   is geometry only — 16px under the body, left with the text rather than
   centred like `.rd-msg__more`, which is a pagination control and not part of
   the message.

   ⚠️ Scoped to nothing: it is ONE rule serving BOTH widths, because
   `MessageBody` is one tree with two mounts (row 05, "the page IS the detail
   pane"). A `.rd-msg-t__detail`-scoped override would be a second geometry for
   a slot the drawing has not spoken about. */
.rd-msg__act { margin-top: 16px; align-self: flex-start; }

/* ── A MESSAGE BODY'S MARKDOWN — `crate::markdown_view` ─────────────────────
   Phase 2 of the Markdown plan. The body is parsed in `libs/core` and walked
   into `rsx!` by `apps/web/src/markdown_view.rs`; these are the faces of what
   it emits. The same subset draws on Android and iOS, so a decision taken here
   about SPACING is this renderer's and a decision about STRUCTURE is not.

   ⚠️ **THE TYPE SCALE IS UNDRAWN AND RELAY ITEM 94 IS OPEN.** The plan's *Open
   with the designer* asks for exactly what is below — the two heading levels'
   scale, bullet spacing, and image/video width inside a body. Every value here
   is taken from a ruled precedent in this app, cited at the rule, rather than
   invented; none of them is a designer decision that has been made. When 94
   comes back, this block is what changes, and nothing in the renderer has to.

   ⚠️ **THE BODY'S TYPE LIVES ON THE WRAPPER, NOT ON EACH BLOCK.** `.rd-md`
   carries the size, leading and colour that `.rd-msg__excerpt` used to carry
   alone, and a paragraph, a list item and a caption inherit it. That is what
   keeps ONE paint per width instead of six copies of four declarations — the
   failure `.rd-msg__excerpt`'s own note already records, when its clamp was
   duplicated across four parts.

   The values are phase 0b's, unchanged: this replaces the mechanism that split
   a plain body into paragraphs, and a reader must not be able to see that it
   happened. */
.rd-md {
  margin-top: 4px;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
  min-width: 0;
}

/* ⚠️ THE RHYTHM BETWEEN BLOCKS IS ONE RULE, and that is the point. A body can
   put a heading after a list, an image after a paragraph, a video after a
   heading — every pair, in any order. Six `margin-top` declarations would have
   to agree with each other forever; one adjacent-sibling rule on the wrapper
   cannot disagree with itself. The 10px is phase 0b's paragraph gap, which was
   the only such pair that existed before this build.

   It reaches only the SECOND and later blocks, so the first keeps `.rd-md`'s
   own 4px offset from the title and nothing above the body moves. */
.rd-md > * + * { margin-top: 10px; }

.rd-md__p { text-wrap: pretty; }

/* The two heading levels — Markdown H1 and H2, drawn as `h2`/`h3` because the
   page owns the level above them (see the renderer's module note).

   UNDRAWN. Taken from `.rd-msg__title`, which is this screen's own ruled
   heading face: Archivo, the 78% stretch that is inside `ALLOWED_STRETCH`, the
   .03em tracking. What is added is a size step and the full-strength `--rd-fg`
   for the upper level, so two levels are distinguishable from each other and
   from the prose around them without a third mechanism. */
.rd-md__h2 {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .03em;
  font-size: 13px;
  line-height: 1.2;
  color: var(--rd-fg);
  text-transform: uppercase;
}

.rd-md__h3 {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .03em;
  font-size: 11.5px;
  line-height: 1.25;
  color: var(--rd-fg2);
}

/* The lists. `_legacy-base.css` zeroes every element's padding, so the indent
   has to be restored here or the markers render outside the box and the first
   character of every item sits under the marker. 18px is the marker column plus
   its gap at this size, measured rather than inherited from a browser default
   this sheet already removed.

   Core flattens nesting to one level (phase 1's out-of-subset table), so there
   is no nested-list case to space. */
.rd-md__ul { padding-left: 18px; list-style: disc; }
.rd-md__ol { padding-left: 18px; list-style: decimal; }

/* Items are closer together than blocks are: a list is ONE block, and spacing
   its rows at the between-blocks distance would read as several. 4px is
   `.rd-msg__excerpt`'s own offset — the smallest step this screen uses. */
.rd-md__li + .rd-md__li { margin-top: 4px; }

/* Media inside a body: the column's width, its own aspect ratio, never wider.

   ⚠️ **NOT `.rd-msg__image`'s FIXED 300px BOX, AND THE DIFFERENCE IS RULED BY
   WHAT THE TWO THINGS ARE.** That element is the announcement's single graphic
   in a fixed slot above the body, where a fixed box keeps a list of
   announcements from jumping as it is read. An image INSIDE prose is placed by
   the author between two paragraphs, at a size the author's file decides;
   letterboxing it into a 300px box would crop nothing but would pad a wide
   banner with dead space. `height: auto` with `max-width: 100%` is the honest
   rendering of "as published, no wider than the column". */
.rd-md__img,
.rd-md__video {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
}

.rd-md__figure { margin: 0; }

/* The video's caption — the author's alt text, associated by the `figure`.
   `.rd-msg__when`'s size and colour: the two are the same kind of thing, a
   quiet line of metadata under the content it describes. */
.rd-md__caption {
  margin-top: 6px;
  font-size: 9.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
}

/* A link inside a message body.

   ⚠️ **THIS RULE WAS `.rd-msg__a` AND IT IS THE SAME FACE, RENAMED.** #1373's
   `linkify` found addresses in plain prose; `markdown_view` renders the links an
   author wrote, and it draws the whole body rather than only this screen's, so
   the name follows the renderer. The old name is deleted rather than kept as an
   alias — a class no page emits is exactly what `rangeday check css` is for.

   UNDRAWN, and taken from the nearest ruled precedent rather than invented:
   `.rd-auth__link` / `.rd-chal-d__link` are this app's inline acts inside prose
   — accent text, underlined only on hover, the accent ring on focus. The
   underline is deliberately absent at rest: a body may hold several links, and
   a page of underlined runs reads as damage rather than as links. Colour
   carries it, and the pointer/keyboard states confirm it.

   `overflow-wrap` because an address an author pasted as its own label is one
   unbreakable word: a long one in a 58ch measure pushes the pane's own column
   open without it. */
.rd-md__a {
  color: var(--rd-acc);
  text-decoration: none;
  overflow-wrap: anywhere;
}
.rd-md__a:hover { text-decoration: underline; }
.rd-md__a:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* ── 14 (Tablet) People · row 05 · ScreenTabInbox (rd-tablet-social.jsx:423) ──
   "Messages: The Page Becomes The Pane" — the list column and the reading pane.
   Screen: apps/web/src/pages/messages.rs, which carries the two carried rulings
   (the unread interim, the action_url gap) and the rail-row flag. Only the
   values live here.

   ⚠️ **NOT A NEW PART, AND THAT IS THIS DOCUMENT'S OWN PRECEDENT.** s14-tablet.css
   holds `14 (Tablet) People` row 01 and says in its own header why row 02 is not
   in it: the challenges pane *"already ships as `.rd-chal-t__*` in s14-chal.css
   — a screen family drawn across two documents is still one family"*. Messages
   is the same call, one row down: its phone rules are `.rd-msg*` above, so its
   tablet rules sit beside them rather than three files away from the screen they
   restyle. Nothing below re-declares a `.rd-msg*` rule — these are additions and
   two scoped overrides, both marked.

   ⚠️ NO `@media` HERE, the standing rule for every tablet screen part: the
   breakpoint is asked ONCE in Rust (`crate::device::use_device_class` picks
   `RdTabletShell`), and the only width query in the app is
   kit-tablet-shell.css §8. A query here would paint this screen at phone width
   on a page whose Rust arm never mounted it.

   Chrome comes from the kit and nothing below re-declares any of it:
   `RdTabletShell` (`.rd-tshell*`), `RdPanes` (`.rd-tpanes*`, which owns the
   380px list column, the rule between the panes and BOTH scrolls) and
   `RdTabletRail` (`.rd-trail*`). What is left is the rows inside the list pane
   and the reading surface beside them. */

/* The list pane's own column. `RdPanes` already scrolls it; this is the drawn
   `flex: 1, minHeight: 0` so the rows fill the column and the Load More button
   sits under the last one rather than floating. */
.rd-msg-t__list {
  flex: 1;
  min-height: 0;
}

/* `rd-tablet-social.jsx:434` ("minHeight: 76") — minHeight 76, padding '12px 16px', gap 10, a
   bottom hairline, and a 3px LEFT rule that is transparent until the row is the
   selected one. The rule is declared on every row rather than only on `--on` so
   the 3px never changes the row's inner width when selection moves — a shifting
   text column reads as the list twitching. */
.rd-msg-t__row {
  display: flex;
  gap: 10px;
  min-height: 76px;
  padding: 12px 16px;
  box-sizing: border-box;
  border-bottom: 1px solid var(--rd-line);
  border-left: 3px solid transparent;
  background: transparent;
  text-decoration: none;
  cursor: pointer;
}

.rd-msg-t__row:last-child { border-bottom: none; }
.rd-msg-t__row:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: -2px; }

/* The selected row — `k === sel ? t.acc : 'transparent'` and
   `k === sel ? t.surf2 : 'transparent'`. Two signals for one fact, exactly as
   drawn: the accent rule reads at a glance across the column and the raised
   surface survives being looked at directly. */
.rd-msg-t__row--on {
  border-left-color: var(--rd-acc);
  background: var(--rd-surf2);
}

/* ⚠️ **THE UNREAD MARK'S GUTTER IS RESERVED AND THE MARK IS ABSENT — the ruled
   interim, carried unchanged from the phone row above.**

   Row 05's own note: *"The unread mark and the title's weight are one datum
   drawn twice. If the read field never ships, the interim render is no mark and
   uniform weight — never all-marked."* The wire still carries no read state —
   `FeedEntryDto` has no `read` field and `MarkRead` returns an empty message —
   so the square is transparent and the title below takes ONE weight.

   ⚠️ Do NOT "fix" this by painting every square volt. That is the all-marked
   render the ruling names, and it makes the mark mean "message" rather than
   "unread" — a reader who learns the wrong meaning misreads the real one when
   it lands. The square keeps its 6×6 box so the text column does not jump on
   the day the datum arrives. */
.rd-msg-t__mark {
  flex: none;
  width: 6px;
  height: 6px;
  margin-top: 5px;
  background: transparent;
}

.rd-msg-t__body { flex: 1; min-width: 0; }

/* Drawn `tdisp(un ? 800 : 700, 78, '.03em')` at 12px, `un ? t.fg : t.fg2`. The
   interim takes the QUIETER of the drawn pair — 700/fg2 — for the mark's reason
   one rule up: rendering every title at the unread weight is the all-marked
   failure wearing type instead of a square. Same values the phone's
   `.rd-msg__title` ships, and for the same ruling. */
.rd-msg-t__title {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .03em;
  font-size: 12px;
  line-height: 1.25;
  color: var(--rd-fg2);
}

/* Two lines, clamped by the box. The clamp itself is the kit's `.rd-clamp-2`,
   applied at the call site — `rd-tablet-kit.jsx:21` ("Line clamp. Lives in the
   KIT, not in a page file"). This rule carries only what is the element's own. */
.rd-msg-t__excerpt {
  margin-top: 4px;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
}

.rd-msg-t__when {
  margin-top: 4px;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

.rd-msg-t__more { padding: 16px; display: flex; justify-content: center; }

/* ── The reading pane ──────────────────────────────────────────────────────
   `rd-tablet-social.jsx:444` ("padding: '24px 30px'") — padding '24px 30px', a flex column. The scroll
   is `.rd-tpanes__detail`'s and is not restated. */
.rd-msg-t__detail {
  flex: 1;
  min-height: 0;
  padding: 24px 30px;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
}

/* ⚠️ SCOPED OVERRIDES, AND THEY EXIST SO THE READING TREE IS NOT FORKED.
   `MessageBody` is ONE component with two mounts (the phone page, this pane) —
   row 05's *"the page IS the detail pane"* executed literally — so the pane's
   drawn type is a paint over the same elements rather than a second component.
   Everything below is `.rd-msg-t__detail`-scoped and changes nothing on the
   phone page. */

/* The announcement's title: `tdisp(900, 68, '.01em')` at 24px, uppercase, 6px
   under the date. The phone page renders the same element at 12px.

   ⚠️ font-stretch 62, NOT the drawn 68 — the identical call s15-tablet.css §0
   already made for its own two 24px display titles, with the same citation. 68
   is outside `ALLOWED_STRETCH` (62, 72, 75-78, 100) and `rangeday check css`
   refuses it by design: a new width is a designer decision, not a call-site
   choice. 62 is the correct member of the set for this size band — the
   designer's own rule, quoted in kit-tablet-shell.css §5: *"62 is right at 30px
   and slightly too tight below about 18px."* This title is 24px, the 30px end of
   that band, and `.rd-tshell__title` took the same decision at 26px. */
.rd-msg-t__detail .rd-msg__title {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 900;
  font-stretch: 62%;
  letter-spacing: .01em;
  font-size: 24px;
  line-height: 1;
  color: var(--rd-fg);
  text-transform: uppercase;
  margin-top: 6px;
}

/* The body: 12.5px at a 58ch measure, `textWrap: 'pretty'`. The measure is the
   drawn number and it is the reason this pane is not simply the page made wide
   — a 900px line of 12.5px prose is unreadable, and the drawing says so by
   capping it.

   ⚠️ THE SELECTOR IS `.rd-md` AND IT USED TO BE `.rd-msg__excerpt`. The drawn
   values below are unchanged — 12.5px at a 58ch measure, `textWrap: 'pretty'` —
   and only what carries them moved: the reading pane renders a parsed Markdown
   body now (`markdown_view`), so the paint belongs on the body's wrapper rather
   than on a paragraph class the pane no longer emits. The old compound rule was
   deleted rather than left: two live class names either side of a space is
   exactly the kind of dead rule `rangeday check css` cannot see.

   The between-blocks rhythm widens with the leading: prose at 12.5px/1.55 wants
   at least the 13px drawn under the title before the next block reads as a
   separate one. */
.rd-msg-t__detail .rd-md {
  max-width: 58ch;
  margin-top: 13px;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--rd-fg2);
  text-wrap: pretty;
}

.rd-msg-t__detail .rd-md > * + * { margin-top: 13px; }

/* The heading steps up with the body — the pane's prose is 12.5px against the
   page's 10.5px, so a 13px heading would no longer outrank it. Same ratio, one
   size band up. UNDRAWN with the rest of the scale; relay item 94. */
.rd-msg-t__detail .rd-md__h2 { font-size: 15.5px; }
.rd-msg-t__detail .rd-md__h3 { font-size: 13.5px; }

/* ⚠️ **THE DATE IS DRAWN ABOVE THE TITLE, AND `order` IS WHY THERE IS STILL ONE
   CONTENT TREE.** The drawn pane opens with the date (`2 February`) and the
   phone page closes with it. That is the ONE place where this pane's drawn
   geometry is not purely a paint — and re-ordering it in the markup would mean
   two `MessageBody` trees, which is the fork row 05 exists to prevent. The pane
   is already a flex column, so `order` states the drawn sequence where the rest
   of the geometry lives.

   ⚠️ It is a VISUAL order only: the DOM order is unchanged, so a screen reader
   still reads title-then-body-then-date. That is the honest reading order for a
   dated announcement, and it is what the phone page reads today — so the two
   devices agree in speech even where they differ in paint. Do not "fix" this by
   moving the element in `MessageBody`. */
.rd-msg-t__detail .rd-msg__when {
  order: -1;
  margin-top: 0;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* ── The two stateless panes ───────────────────────────────────────────────
   The empty inbox (drawn: `ScreenTabInbox`'s `empty` frame, a centred block
   across the WHOLE content pane with no list rule beside it) and the
   nothing-selected pane (undrawn; copy per relay-4 §2's ruled shape). One
   geometry because they are the same shape — a short centred sentence — and the
   difference between them is which element they sit inside. */
.rd-msg-t__none {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 40px;
  text-align: center;
}

/* The nothing-selected pane IS a `.rd-msg-t__detail`, so it inherits the 24/30
   padding; this modifier centres it instead of stacking from the top. */
.rd-msg-t__detail--none {
  align-items: center;
  justify-content: center;
  text-align: center;
}

.rd-msg-t__none-lead {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .03em;
  font-size: 13px;
  color: var(--rd-fg2);
}

/* `maxWidth: '46ch'`, 13px, fg3 — the drawn empty-frame sentence. */
.rd-msg-t__none-p {
  max-width: 46ch;
  margin-top: 8px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--rd-fg3);
}
/* ==== P1 · docs/design/kit/P1-banner.md · owner: lane4 ==== */

/* RdBanner — the inline message strip.

   ⚠️ THE ONE KIT PRIMITIVE WITH NO DRAWING. Every value here comes from
   measuring the product's own 76 banner-shaped sites, not from a corpus — see
   P1 §0 for the ruling that permits that and §1 for the method. Two
   consequences worth carrying at the top of the block:

     - The spec and this block AGREE BECAUSE THEY SHARE A SOURCE. That is not
       corroboration (P1 §1.3's cousin); it proves accurate copying and nothing
       more. The tests below are what actually holds these values.
     - With no drawing to re-read, a value changed here is a value changed
       everywhere. There is no second copy to catch it.

   SQUARE CORNERS ARE A CONSTRAINT, NOT A PREFERENCE (P1 §6). All 76 sites use
   Tailwind's `rounded`/`rounded-lg` — a rounded rectangle, which substrate §1.5
   bans outright. This is the only place in the primitive where the product's own
   practice is overruled rather than transcribed. A chamfer would need an `r`
   nothing measures and would be a substrate change; closed by head 2026-08-11. */

/* THE BASE RULE NAMES NO COLOURS. It owns padding, ground, face, line-height
   and the border's WIDTH AND STYLE; the modifiers below own colour outright.

   That is P1 §11 Q1, answered by the designer 2026-08-11, and the reason is his:

     "Two declarations nothing can reach are two things every future reader has
      to disprove, and §5.1 is the record of that costing a whole draft already."

   What was here: `border: 1px solid var(--rd-line)` and `color: var(--rd-fg2)`.
   Neither ever rendered — `tone` is required, `BannerTone::class()` always emits
   a modifier, and all three override `border-color` and `color` at equal
   specificity. An earlier draft of the spec argued about `--rd-line`'s contrast
   against the 3:1 floor before noticing it was unreachable: reasoning about the
   stylesheet as written rather than about what a render produces.

   ⚠️ AND THERE IS NO TONE-LESS BANNER — not "not yet". Also the designer:
   "A strip that says nothing about what happened is a paragraph with a border."
   A neutral strip, if ever wanted, arrives as a FOURTH TONE with a call site
   behind it. That answer is what makes dropping the two safe: there is no
   accidental foresight here for a later reader to inherit and build on.

   ⚠️ THAT CONDITION FIRED. 2026-08-17: three page-local grey strips had grown
   against this absence (Range Bag, Plan A Range Day, Course Builder), and the
   designer ruled the fourth tone in — "two arrived, so it lands." It is a
   FOURTH TONE with call sites behind it, which is exactly the arrival this
   paragraph describes, and not a tone-less banner: `tone` is still required and
   every legal render still carries a modifier. Drawn in `01 Foundations` §07 in
   both themes. The rule is `.rd-banner--neutral` below.

   `border: 1px solid` with no colour resolves to `currentColor`, so a tone that
   ever set only `color` would still get a matching edge. */

/* ⚠️ THE SLOT RULING — 2026-08-28, JAMES, AND IT REVERSES THE NO-MARGIN RULE
   THIS BLOCK USED TO STATE. Read the reversal before you restore anything.

   The report: on staging `/admin/members/invite`, the *"12 invitations sent."*
   strip TOUCHED the `EMAIL ADDRESSES` label under it and sat ~32px inside the
   left edge every other block on that screen aligns to. The ruling that closed
   it is wider than the page:

     "Every such notice, shooter and Pro, phone and tablet, sits in ONE
      consistent slot that never touches other elements."

   ⚠️ WHAT WAS HERE, AND WHY IT WENT. The primitive used to take NO margin at
   all — "callers space it, exactly as `RdChamferBox` does" — and the argument
   for it was a real measurement: the 63 error sites it replaced were written
   eleven ways, differing only in their spacing, and "when most spellings of a
   thing differ only by their spacing, the spacing was never the thing's
   property."

   That argument survives its own conclusion being wrong. Handing the spacing to
   the callers did not produce one spacing; it produced TWENTY-SEVEN banner-slot
   rules across the pool spelling the gap 2, 8, 10, 11, 12, 13, 14, 15, 16 and
   18px, three of them writing a horizontal inset ON TOP of a column that was
   already inset, and one — `.rd-mem__banner` — writing a bottom gap of ZERO.
   The eleven spellings had become twenty-seven. A property that every caller
   has to remember is a property no caller owns, which is the same defect the
   original measurement recorded, one level up.

   So the gap is the PRIMITIVE's now. `--rd-banner-gap` is the kit's block gap:
   16px at phone, 20px from the tablet breakpoint up. THERE IS NO HORIZONTAL
   MARGIN AND THERE MUST NOT BE ONE — a banner takes the full width of whatever
   column mounts it, so a banner mounted as the first block of a padded content
   column aligns to that column's edges with nothing declared at the call site.
   That is the whole fix for the inset half of the report.

   ⚠️ `order.txt`'s note beside `sx-verify.css` still says a caller's stacking
   rules living in this part "is how `.rd-banner`'s no-margin ruling would get
   quietly reversed." That warning was against a QUIET reversal. This one is
   loud, dated, and signed, and it is the primitive's own spacing rather than
   any caller's stacking. The note stands for what it was written against.

   ⚠️ A COMPOSITION THAT WRAPS THE STRIP OPTS OUT AT ITS OWN EDGE, NOT HERE.
   `.rd-vblk` (sx-verify.css) puts acts UNDER the strip inside one block; there
   the shelf belongs to the block, so that part zeroes the strip's own bottom
   margin and carries `--rd-banner-gap` itself. Two rules, both naming the
   token — not a second number. */

/* 16 phone / 20 tablet-and-up. The twin of `device::TABLET_MIN_WIDTH_PX`
   (= 1024) and of the one `@media (min-width: 1024px)` in
   `kit-tablet-shell.css` — the breakpoint is 1024 for the reasons `device.rs`
   gives at length, and a second number here would be a second breakpoint.

   Declared in the primitive's own part on purpose: the value is the primitive's
   contract, and `banner.rs`'s tests read THIS section. A token in the shared
   sheet would be pinned by nothing. */
:root { --rd-banner-gap: 16px; }
@media (min-width: 1024px) {
  :root { --rd-banner-gap: 20px; }
}

.rd-banner {
  padding: 12px 14px;
  border: 1px solid;
  background: var(--rd-surf);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.5;

  /* THE SLOT. Bottom gap only, no horizontal inset — see THE SLOT RULING
     above. Written as a shorthand so a caller cannot leave a stale
     `margin-left` standing beside it. */
  margin: 0 0 var(--rd-banner-gap);
}
.rd-banner--error { border-color: var(--rd-danger); color: var(--rd-danger); }
.rd-banner--success { border-color: var(--rd-acc); color: var(--rd-fg); }
.rd-banner--warning { border-color: var(--rd-warn); color: var(--rd-fg); }

/* NEUTRAL — the fourth tone, ruled 2026-08-17, drawn in `01 Foundations` §07 in
   both themes.

   ⚠️ WHY `line` AND NOT `ctrl`. Quoting the ruling, because the next reader will
   see a 1.4:1 hairline and reach for the control token that exists to clear 3:1:

     "The other three tones each buy something — red says this failed, volt says
      this worked, amber says read this before you continue. An at-rest strip has
      none of those to sell, so it spends no colour: the border drops to the same
      hairline every other resting surface in the kit uses. It is the quietest of
      the four and still a statement. `ctrl` would have been wrong for a second
      reason — it is the interactive token, and a banner is not tappable."

   So `--rd-ctrl`'s 3:1 FLOOR (see `_legacy-tokens.css`) does not apply here.
   That floor is WCAG 1.4.11, which asks 3:1 of the visual information
   identifying a COMPONENT — a control. This border identifies nothing; the
   sentence inside it does, at full strength, and it is the sentence a reader
   reads. Do not "fix" this to `--rd-ctrl`.

   ⚠️ FULL-STRENGTH TEXT IS THE WHOLE DISTINCTION, and it is `fg` rather than
   `fg2`/`fg3`. Also the ruling:

     "Full-strength text is what separates it from the validation summary drawn
      beside it. That block is grey copy on a grey border and reads as inert,
      which is right for a list of things you have not done yet. Neutral is `fg`
      copy on a hairline: something is true right now and you should know it.
      Same restraint, opposite posture — and the distinction is the text colour,
      not the border. Worth knowing before someone reuses `#3A3A3A` here."

   The three page-local strips this replaces all took `--rd-fg2`, so the migration
   is a deliberate CHANGE and not a transcription: they were the inert posture,
   and the tone that lands is the other one. Pinned by `ruling_`-prefixed tests
   in `banner.rs` — a failure there is a designer question, not a number to bump.

   ⚠️ NO MOUNT ASSUMPTION, AND THE TABLET NEEDS THAT. `16 (Tablet) Orgs` row 02:
   "The Neutral strip sits at the top of the pane rather than on the shell. On
   this device the sentence is about the record beside it, not about the app."
   Nothing in this block or in `.rd-banner` above declares width or position —
   full-bleed is `.rd-shell__banner`'s and the frame's, never the strip's — so a
   pane mount needs no variant. ⚠️ IT DID SAY "width, margin or position", and
   the margin half is no longer true: THE SLOT RULING above gives `.rd-banner` a
   bottom gap. That changes nothing for this tone's mount-agnosticism, which is
   about WIDTH and POSITION: the gap is vertical, so a pane mount and a shell
   mount get the same strip and the same shelf under it. Already demonstrated in-tree:
   `course_builder.rs` mounts it as a grid item inside `.rd-cb__foot`, mid-pane,
   while the other two sites take the shell slot. Do not add a tablet modifier
   without a drawing that needs one. */
.rd-banner--neutral { border-color: var(--rd-line); color: var(--rd-fg); }

/* ==== PRO-KIT · rd-pro-kit.jsx:272-307 · owner: spine ==== */
/* `ProChip` and `ProTabs` — one chip and one tab strip for the whole Pro
   surface, replacing `rd-evstatus*` and `rd-ev-tabs*`.

   ⚠️ **The tone model separates text from border, and the old one could not.**
   `.rd-evstatus` used `border: 1px solid currentColor`, so a chip's border was
   always its text colour. `PTONE` (rd-pro-kit.jsx:272) gives `plain` and `dim` a
   NEUTRAL border (`t.line`) with coloured text, and only `live` and `flag` are
   self-coloured. That is a model change, not a palette tweak — `currentColor`
   cannot express it.

   ⚠️ **Draft is no longer `--rd-warn`.** The designer removed it
   (rd-pro-events.jsx:91): "a draft is not a warning, it is how every event
   starts, and spending the product's only alarm colour on the normal case
   leaves nothing for the abnormal one." Every Draft chip in the product changes
   appearance. */

/* ⚠️ **THE CHIP IS CHAMFERED, AND A STROKED CHAMFER IS TWO LAYERS.** Staging
   defect, 26 Aug (James, `/admin/members`): at phone width the chips showed an
   EMPTY GAP at the cut — the corner removed and no edge drawn across it — and at
   tablet width they had no chamfer at all, square corners. Two spellings of one
   omission:

   - this rule shipped `border: 1px solid` and NO `clip-path`, so every width
     that did not override it drew a square box (the tablet symptom);
   - the phone strips that DID chamfer themselves put a `border` UNDER a
     `clip-path` (`adm-mob.css` §3.2/§3.3, `adm-lib-mob.css` §2), and the clip
     removes the corner INCLUDING the border drawn on it — a bare diagonal
     (the phone symptom). `48 Kit Rules`: *"a stroked chamfer must be two
     layers"*; `01 Foundations`:85; `chamfer_box.rs`'s header states the same
     mechanism.

   So the chip is now the kit's two-layer construction, at EVERY width, with no
   width-scoped rule anywhere touching its corner:

     .rd-prochip        the OUTER layer, which IS the stroke — a solid box in the
                        stroke colour, clipped at the control cut, padded by the
                        stroke width so the padding INSETS the face.
     .rd-prochip__face  the INNER layer, the surface, clipped at
                        `inner_chamfer_px(13, 1)` so the stroke's thickness is
                        uniform along the diagonal too.

   ⚠️ **THE TONE MODEL IS UNCHANGED AND ONLY ITS SPELLING MOVED**: what used to
   be `border-color` on the element is now `background` on the OUTER layer, and
   `color` still rides on the outer and inherits into the face. The separation
   the block header describes — a NEUTRAL stroke under coloured text for `plain`
   and `dim` — survives intact, which `currentColor` still could not express.

   ⚠️ **13, THE RULED CONTROL CUT** — `01 Foundations` §03, re-ruled 26 Aug 2026,
   mirrored by `CONTROL_CHAMFER_PX` (`kit/chamfer_box.rs`) and by
   `--rd-chamfer-control` on `:root` (`kit-p0.css`). The variable is named here
   rather than the literal so all three move together; the INNER cut has to be a
   literal because `calc()` cannot carry the √2 term, and
   `the_chip_inner_cut_is_the_house_formula` pins it against
   `inner_chamfer_px(CONTROL_CHAMFER_PX, 1.0)` = 12.4142…

   ⚠️ **THE FACE IS OPAQUE, AND IT HAS TO BE.** `--rd-hollow-fill` is the kit's
   outlined-control surface (`Fill::Hollow`); a transparent face would let the
   OUTER layer's stroke colour flood the whole chip, which is the one way this
   construction fails loudly. */

.rd-prochip {
  display: inline-block;
  box-sizing: border-box;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9px;
  white-space: nowrap;
  /* The stroke width, as padding: it INSETS the face rather than growing the
     chip, so the box is the same 1px-bordered box it has always been. */
  padding: 1px;
  background: var(--rd-line);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--rd-chamfer-control)), calc(100% - var(--rd-chamfer-control)) 100%, 0 100%);
}

/* The surface. The drawn `4px 9px` lives HERE now — on the layer that holds the
   word — and the type does not, because it inherits from the outer and a second
   spelling of 9px would be a second thing to keep in step. */
.rd-prochip__face {
  display: block;
  padding: 4px 9px;
  background: var(--rd-hollow-fill);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 12.41px), calc(100% - 12.41px) 100%, 0 100%);
}

/* The drawing's second size (:223), for a chip beside a page title. The size is
   the FACE's padding and the outer's inherited type — the cut does not scale
   with it, because 13 is the control cut and this is still one control. */
.rd-prochip--big { font-size: 10px; }
.rd-prochip--big .rd-prochip__face { padding: 5px 11px; }

/* Read it. A fact about the row — the default. */
.rd-prochip--plain { color: var(--rd-fg2); background: var(--rd-line); }
/* The current state, and what everything else is relative to. */
.rd-prochip--live { color: var(--rd-acc); background: var(--rd-acc); }
/* Something needs a person. Outlined, never filled: as a fill it would be a red
   badge shouting across a table of names (rd-pro-kit.jsx:282-284). The FILL here
   is the outer layer's, i.e. the stroke — the face stays hollow. */
.rd-prochip--flag { color: var(--rd-danger); background: var(--rd-danger); }
/* True, and no longer in play. */
.rd-prochip--dim { color: var(--rd-fg3); background: var(--rd-line); }
/* A passing verdict, and only that — GREEN, James 30 Aug 2026. `--rd-ok` is the
   scorer's PASS tone (kit-ok.css, #1307). The ink sits on the face's
   `--rd-hollow-fill`: #4EB56D on #111111 = 7.3:1 dark, #2E6B3C on #F1F1EC =
   5.6:1 light — both past AA for this 9px/800 face. Same two-layer outline as
   the rest. */
.rd-prochip--ok { color: var(--rd-ok); background: var(--rd-ok); }

/* One tab strip (rd-pro-kit.jsx:298). The active tab's 2px underline sits ON the
   strip's own hairline via the negative margin — not below it. */
/* ⚠️ THE STRIP'S 18px OF TOP PADDING NOW BELONGS TO THE TAB — James, NTOA
   rehearsal 2026-08-29. It was `padding: 18px 32px 0` here and `0 0 12px` on
   the tab, which made every tab ~28px tall: a 13px label with 12px under it,
   and 18px of dead strip above it that belonged to nobody. The tab was the text
   and the padding was the strip's.

   Moving the 18 down one level changes NO drawing at all — the label sits the
   same 18px below the strip's top edge either way, and the underline is still
   the tab's own bottom border on the strip's hairline — and it hands the tab a
   48px box (18 + ~16 line + 12 + 2 border). The one thing that changes is where
   a tap counts, which is the point. */
.rd-protabs {
  flex: none;
  display: flex;
  gap: 26px;
  padding: 0 32px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-protabs__tab {
  display: flex;
  align-items: baseline;
  gap: 7px;
  padding: 18px 0 12px;
  /* A floor, not a height: at the drawn 13px label the box is already 48, so
     this never fires. It is here so that a future smaller label cannot quietly
     take the tab back under the minimum. */
  min-height: 44px;
  box-sizing: border-box;
  margin-bottom: -1px;
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  cursor: pointer;
  font: inherit;
}

.rd-protabs__label {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  font-weight: 400;
  color: var(--rd-fg2);
}

/* The count slot. Present only when the strip passes counts — and a zero prints,
   because "hiding the number makes an empty tab look unvisited" (:231). */
.rd-protabs__count {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
}

.rd-protabs__tab--on { border-bottom-color: var(--rd-acc); }
.rd-protabs__tab--on .rd-protabs__label { color: var(--rd-fg); font-weight: 600; }
.rd-protabs__tab:hover .rd-protabs__label { color: var(--rd-fg); }
.rd-protabs__tab:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }
/* ==== ADM-PLAT · platform admin · owner: lane-2 ==== */

/* ---------------------------------------------------------------------------
   ⚠️ THIS SCREEN IS DRAWN NOW, AND IT WAS NOT WHEN THIS PART WAS WRITTEN. The
   block that stood here said so at length — no file drew a platform- or
   instance-admin screen, so the styling was "minimal consistent Pro treatment,
   not an interpretation", and it ended with: *"When this screen IS drawn, this
   block is what gets replaced."* This is that replacement.

   The drawing is `rd-platform.jsx`'s `ScreenPlatAccounts` (`50 (Desktop)
   Platform.html`), which draws the screen **as shipped rather than as wished**:
   the same search and the same account deletion, in a four-cell row.

   ⚠️ **The drawn app's chrome is NOT taken.** `PlatShell` draws Platform as a
   separate deployable with its own rail and an environment band — ruled 14 Aug,
   not built. `PlatTop` maps onto the console's `ProTop` (same title / sub /
   single-action shape), so nothing here restyles the page head; only the body
   below it lives in this part.

   Its own class family rather than ADM2's `rd-set-*`: those are named for the
   org settings screen, and borrowing them would be the naming lie this lane
   refused for `rd-ev-tabs`.
   --------------------------------------------------------------------------- */
/* ⚠️ **CORRECTED 2026-08-25.** This read: "`position: relative` IS LOAD-BEARING.
   `RdConfirmModal`'s scrim is `position: absolute` and resolves against the
   nearest positioned ancestor, so without this the delete confirm escapes to the
   viewport and covers the rail too. Same line, same reason, as `.rd-mem-body`.
   The modal states this as a caller contract it cannot enforce from inside."

   `.rd-confirm` is `position: fixed` now (adm1-5-1.css). Still the same line and
   the same reason as `.rd-mem-body` — both are kept as the reversal path and
   neither is the confirm's containing block any more. */
/* ⚠️ The max-width GREW with the drawing, from 760px, and it is a measurement
   rather than a preference: the drawn row is a 250px identity column, a flexing
   name, a 104px chip and the action — at 760px the name column collapsed under
   the two fixed ones. The padding is the drawing's own `18px 28px 24px`, with
   the console's 32px gutter kept so the body lines up with `ProTop` above it. */
.rd-plat { position: relative; padding: 18px 32px 24px; max-width: 1000px; }

/* The search field. ONE BOX AND NO CONTROL BESIDE IT — the drawing has no
   submit, and the page searches as you type (~300ms) with Enter submitting
   immediately. The row survives as a flex line only because the sweep sits in
   the field's tail. */
/* `RdSearchField`'s wrapper. It is no longer a flex row: the box, the input and
   the in-flight mark are one component's tree now, so all this carries is the
   drawn width cap and the gap below it. */
.rd-plat__search {
  max-width: 460px;
  margin-bottom: 18px;
}

/* ⚠️ **THE BOX IS NOT HERE ANY MORE — Q2, `A6-search-field.md` §10.**
   This rule used to draw a plain 1px bordered rectangle with its own fill and
   padding. That shape is NOT drawn: `50 (Desktop) Platform.html` describes the
   search in prose and draws no field at all. It is the kit's chamfered box now
   (RdChamferBox at r=12, w=1, Fill::Field, `--rd-ctrl` at rest and `--rd-acc`
   on focus), on the grounds that an undrawn field should look like every other
   field rather than like itself. Flagged in the spec, not hidden — a word from
   the designer reverts it.

   What survives is the ONE fact the console still owns: this console's type is
   13px, a step down from the kit field's 16. `au5.css` sits at 317 in
   order.txt and this part at 361, so a single class here beats a single class
   there on a tie, which is exactly how a site keeps its drawn deviation. */
.rd-plat__input {
  font-size: 13px;
}

/* ⚠️ `.rd-plat__input::placeholder`, `.rd-plat__search .rd-plat__input` and
   `.rd-plat__input:focus` WERE HERE and are retired with the box that needed
   them: the placeholder colour, the pre-kit backfill defence and the focus
   stroke are all `RdSearchField`'s now (`au5.css`, and the box's own stroke
   props). `.rd-plat__sweep` is retired the same way — the in-flight mark is the
   field's end slot, `.rd-search__sweep`. */

.rd-plat__banner { margin: 0; }

/* ---------------------------------------------------------------------------
   ⚠️ `.rd-plat__rows`, `.rd-plat__row` AND `.rd-plat__row:last-child` ARE GONE,
   AND THE ROW SHAPE THEY CARRIED WAS NEVER THIS PART'S TO OWN.

   What stood here was a hand-typed copy of the designer's own constant:

     const prow = { display: 'flex', alignItems: 'center', gap: 18, padding: '13px 0' };
     (rd-platform.jsx:76)

   `docs/design/kit/15-pro-table.md` §5 calls that constant the primitive's
   charter — the one place in the corpus where the row shape is NAMED rather
   than re-typed — and the page now asks `ProTable` / `ProRow` for it at
   `RowDensity::Compact13` (kit-pro-table.css §3 and §5). `.rd-plat__rows`'s
   `display: flex; flex-direction: column` is `.rd-ptable`'s; the hairline is
   `.rd-prow`'s.

   ⚠️ THE `:last-child` RULE IS NOT CARRIED OVER, AND THE REASON IS A FINDING.
   Spec §8.2 built `last_row_no_rule` on the strength of the rule that used to
   sit here, calling Platform the one table whose "account list ends at the
   frame edge". It does not, and it never did:

     * The drawing gives EVERY row a hairline — `<div style={{ ...prow,
       borderBottom: `1px solid ${t.line}` }}>` at rd-platform.jsx:329, inside a
       `rows.map` — and closes with the footer paragraph at :337.
     * In the shipped markup the footer paragraph was the last child of
       `.rd-plat__rows`, so `.rd-plat__row:last-child` matched NOTHING. The rule
       was inert for its whole life and the last hairline has always painted.

   So the flag is not passed: passing it would be the first time this screen
   ever lost that hairline, against the drawing. Reported, not silently
   resolved — `kit-pro-table.css` §6 and spec §8.2 both cite the rule deleted
   here as their reason to exist, and that citation now needs the designer's
   word rather than a repoint.
   --------------------------------------------------------------------------- */

/* The identity column: the address, or the `@handle` when search returns no
   address (it returns none today). Mono, because it is the machine-readable
   half of the row and a proportional address is harder to compare down a
   column.

   ⚠️ TYPE ONLY NOW — the drawn `width: 250, flex: 'none'` is the `ProColumn`'s
   (`platform_admin.rs::account_columns`), declared once and consumed by every
   band, which is the ruling at adm-lib.css:110-120.

   ⚠️ AND THE TYPE IS WHY THIS CLASS SURVIVES AT ALL. `CellRole::Numeric` is the
   substrate's `mono` role — Archivo 800 / stretch 75% / tabular, a
   PROPORTIONAL face. This cell is the drawing's `pmono`
   (`fontFamily: 'ui-monospace, Menlo, monospace'`, rd-platform.jsx:78) at 12px
   in --rd-fg. Two different things are called mono in the corpus and this is
   the other one, so the cell is a `Slot` and the face stays here. Routed to the
   designer beside spec §6.1's Q5.

   `overflow-wrap: anywhere` is load-bearing twice over: it wraps a long address
   inside 250px, and — now that this span is a flex item of the `Slot` cell — it
   is also what drops the item's min-content contribution so the wrap can
   happen at all. */
.rd-plat__ident {
  font-family: ui-monospace, Menlo, monospace;
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--rd-fg);
  overflow-wrap: anywhere;
}

/* ⚠️ `flex: 1` IS GONE AND `min-width: 0` STAYS. The flex is the column's now
   (`width: None` → `flex: 1; min-width: 0` from `column_style`); the
   `min-width: 0` here is a second, different one — this span is a flex item of
   its own `Slot` cell and needs its own release, or a long unbroken name pushes
   the cell wider than its share.

   `CellRole::Name` is 14px in --rd-fg and ellipsizes on one line; this cell is
   13px in --rd-fg2 and wraps (rd-platform.jsx:331). Same Q5. */
.rd-plat__name {
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg2);
}

/* An account with no display name prints the drawn em dash, dimmed — the
   drawing's own `nm === '—' ? t.fg3 : t.fg2`. Dimming the absence is what keeps
   it from reading as a name. */
.rd-plat__name--absent { color: var(--rd-fg3); }

/* ⚠️ `.rd-plat__status` IS GONE. It was pure geometry — `width: 104px;
   flex: none` — reserving the drawn column so the action lines up down the
   table whether the word is `Active` or `Placeholder`. A width is exactly what
   a `ProColumn` declares, so it moved there whole
   (`platform_admin.rs::account_columns`, from rd-platform.jsx:332) and nothing
   was left for a class to hold: the chip is a `ProChip` and owns its own look.
   Deleted rather than left unreferenced. */

/* The destructive row action. Text, in the danger role, gaining its boundary on
   hover — the same restraint ADM1's roster remove uses, and deliberately NOT a
   filled button: a page listing accounts should not read as a row of buttons
   inviting the act.

   ⚠️ It rests in `--rd-danger` now rather than `--rd-fg3`. The drawing paints
   this cell `t.danger` at rest, and the word shrank from `Delete account` to
   `Delete` — a quiet grey `Delete` in a right-hand column is indistinguishable
   from a label. The restraint stays in the SHAPE (no fill, no border at rest),
   which is what the note above is actually about. */
/* ⚠️ `flex: none` IS GONE with the row it was a flex item of. The button sits
   inside a `CellRole::Act` cell now — a block, not a flex container — so the
   declaration had become a no-op, and the cell's own `text-align: right` (the
   drawn `textAlign: 'right'`, rd-platform.jsx:334) is what places it. */
.rd-plat__del {
  background: none;
  border: 1px solid transparent;
  padding: 5px 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .08em;
  font-size: 9px;
  color: var(--rd-danger);
  cursor: pointer;
}

.rd-plat__del:hover { border-color: var(--rd-danger); }
.rd-plat__del:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* The drawn footer note, under the rows. Quiet, measured, and it replaces the
   loud warning paragraph that used to sit ABOVE the field: the enumeration of
   what a delete destroys moved to nowhere — it never left the confirm panel,
   which is the thing actually read before acting.

   ⚠️ NOT `ProTableFoot`, AND NOT INSIDE THE TABLE. Two reasons, both
   load-bearing. `.rd-ptable__foot` owns `margin-top: 18px` (spec §9, surveyed
   from six drawn foots); THIS foot is drawn at `marginTop: 16`
   (rd-platform.jsx:337) — a seventh foot the survey missed, and mounting the
   primitive would move it 2px. And a foot inside the table becomes the table's
   last child, which is what made the deleted `.rd-plat__row:last-child` rule
   inert in the first place; kept outside, the last ROW stays the last child and
   that question remains answerable. */
.rd-plat__foot {
  margin: 16px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
  line-height: 1.55;
  max-width: 78ch;
}

/* ---------------------------------------------------------------------------
   NEW ACCOUNT — `ScreenPlatNewAccount`.

   ⚠️ THE STUB'S ONE LINE IS GONE, and with it `.rd-plat__stub`. It said *"the
   creation form lands with the create-account RPC"*; the RPC exists and the
   form is below, so the sentence would now be false. Deleted rather than left
   unreferenced — a class nothing names is the next screen's accidental
   inheritance.

   The form reuses `.rd-plat` as its body ground (same family, same 32px console
   gutter under `ProTop`), so only the column and its two prose blocks live
   here. The drawn body padding is `20px 28px` against Accounts' `18px 28px 24px`
   — 2px of top padding, not worth a second container class to reproduce.
   --------------------------------------------------------------------------- */

/* The drawn column: `{ maxWidth: 520, display: 'grid', gap: 15 }`, verbatim.
   Grid rather than a flex column because the gap is the ONLY spacing on this
   screen — no field carries a margin, and the divider block's own top padding
   sits inside it. */
.rd-plat-new {
  max-width: 520px;
  display: grid;
  gap: 15px;
}

/* Mono values, on three of the four fields (`pmono` in the drawing): the email,
   the username and the temporary password. Each is a string the operator will
   compare or transcribe character by character, and a proportional font makes
   `l`/`1` and `O`/`0` a guess — on a password that is handed over verbally,
   that is the whole act failing quietly.

   ⚠️ A WRAPPER, NOT AN `RdTextField` PROP. AU5's prop surface has no font axis
   and adding one for three sites on one screen would be a kit change made from
   a single drawing. The compound is (0,0,2,0), which clears both the kit's own
   `.rd-field__input` (0,0,1,0) and the pre-kit attribute backfill (0,0,1,1)
   that `.rd-plat__input` above already defends against.

   The 12.5px is the drawing's; it sits under the kit's own input size because a
   monospace face at the same nominal size reads larger. */
.rd-plat-new__mono .rd-field__input {
  font-family: ui-monospace, Menlo, monospace;
  font-variant-numeric: tabular-nums;
  font-size: 12.5px;
}

/* The divider and what it separates. `padding-top` INSIDE the block and the
   rule on its top edge, as drawn — so the 15px grid gap above the rule and the
   15px padding below it frame it evenly without a margin fighting the grid. */
.rd-plat-new__org {
  padding-top: 15px;
  border-top: 1px solid var(--rd-line);
}

/* `pdisp(800, 78, '.06em')` at 10.5px, `t.fg2` — the same display role the
   `.rd-pro-top__btn` label carries, one step quieter in colour because this
   names a consequence rather than an action. */
.rd-plat-new__org-head {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 10.5px;
  color: var(--rd-fg2);
}

/* The paragraph under it. `62ch` is the drawing's own measure and is narrower
   than the 520px column, so the copy wraps on the sentence rather than on the
   column edge. */
.rd-plat-new__org-copy {
  margin: 8px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
  line-height: 1.55;
  max-width: 62ch;
}

/* ---------------------------------------------------------------------------
   NEW ORGANIZATION — `ScreenPlatNewOrg` (`rd-platform.jsx:220`).

   Shares `.rd-plat` (body ground) and `.rd-plat-new` (the drawn 520px column at
   `gap: 15`) with New Account above rather than restating either: the drawing
   gives the two forms the SAME column, and a second copy of `maxWidth: 520` is
   the drift that makes one screen's redraw miss the other.

   ⚠️ WHAT IS NOT HERE, AND WAS NOT FORGOTTEN. No billing block in either of its
   drawn states, and no created/waiting block — James ruled both out on 24 Aug
   (see `pages/admin/platform_new_org.rs`'s head). Absent, not greyed: a greyed
   control is a question the operator cannot answer, which is the drawing's own
   argument for present-or-absent, applied to a block it drew as present.

   The classes below are `rd-plat-org__*` rather than `rd-plat-new__*`: they are
   the OWNER PICKER and the TYPE indicator, neither of which New Account has,
   and naming them for the shared column would send the next reader there.
   --------------------------------------------------------------------------- */

/* The drawn `lab` — `phead` in `t.fg3` with `marginBottom: 7`
   (rd-platform.jsx:222). It is an `<h2>` on the page (`ProTop`'s title is the
   one `<h1>`), so the browser's heading margin and size are both reset here:
   this is a LABEL that happens to be a heading, not a heading that happens to
   label. Deliberately NOT `.rd-field__label` — that class is the kit field's
   own, and borrowing it for a block containing a search and a list would be the
   naming lie this part refused for `rd-set-*`. */
.rd-plat-org__label {
  margin: 0 0 7px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 10.5px;
  color: var(--rd-fg3);
}

/* The note under the owner block and under the type row. The drawing's own
   `fontSize: 11`, `t.fg3`, `marginTop: 6`/`7`, `lineHeight: 1.5` — one class
   for both, because the two differ by a single pixel of top margin that no
   reader can see and no second class should encode. */
.rd-plat-org__note {
  margin: 7px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  line-height: 1.5;
  max-width: 62ch;
}

/* The owner field's own refusal, in the danger role. ⚠️ ABOVE the note rather
   than below it: the note explains the rule and the message says which rule was
   broken, and an operator reads the complaint first. It matches the kit field's
   error type without borrowing `.rd-field__error`, for the same reason
   `.rd-plat-org__label` does not borrow `.rd-field__label` — this error belongs
   to a picker, not to an `RdTextField`. */
.rd-plat-org__error {
  margin: 7px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-danger);
  line-height: 1.5;
}

/* ⚠️ UNDRAWN, ALL OF IT — the frame draws a plain text box for the owner, and
   James's ruling replaced it with a search over existing accounts. The shapes
   below are engineering defaults, flagged in the page's head and in the PR, and
   a word from the designer redraws them. They are built out of what this screen
   family ALREADY draws — the 13px console type, the row hairline, the quiet
   act — rather than out of a new visual language. */

/* `.rd-plat__search` above already caps the field at 460px and gaps it below;
   the results pick up directly under it. The list reset is explicit because
   this IS a `<ul>` — a list of choices, and a screen reader should be told how
   many there are. */
.rd-plat-org__results {
  margin: 0;
  padding: 0;
  list-style: none;
  max-width: 460px;
  border-top: 1px solid var(--rd-line);
}

/* One choice. Full width so the whole row is the target, and every row keeps
   its hairline — the answer `.rd-prow` gives, and the one the drawn
   Organizations table gives its own rows. */
.rd-plat-org__result {
  display: flex;
  align-items: baseline;
  gap: 10px;
  width: 100%;
  padding: 9px 4px;
  background: none;
  border: none;
  border-bottom: 1px solid var(--rd-line);
  text-align: left;
  cursor: pointer;
}

.rd-plat-org__result:hover { background: var(--rd-field); }
.rd-plat-org__result:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: -2px; }

/* A row whose account carries no id cannot be sent, so the markup disables it;
   this is what that reads as. Not hidden — an operator searching for a person
   should see that the person exists and that this build cannot use the row,
   rather than search again for someone who is right there. */
.rd-plat-org__result:disabled { cursor: default; opacity: .5; }
.rd-plat-org__result:disabled:hover { background: none; }

/* The console's 13px, matching `.rd-plat__name` in the table above: the same
   kind of cell — a person's name in an operator's list — on the same screen
   family, one step brighter because this one is the thing being chosen. */
.rd-plat-org__result-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg);
}

/* An account with no display name prints the drawn em dash, dimmed — the same
   treatment and the same reason as `.rd-plat__name--absent`: dimming the
   absence keeps it from reading as a name. Compound (0,0,2,0), so it beats the
   base above regardless of order within the part. */
.rd-plat-org__result--absent .rd-plat-org__result-name { color: var(--rd-fg3); }

/* The machine-readable half: an address when the wire ever carries one,
   `@handle` today. `pmono` at 11.5px in `--rd-fg3`, exactly as the table's
   `.rd-plat__ident` treats the same value — one screen family, one face for one
   kind of string. */
.rd-plat-org__result-handle {
  font-family: ui-monospace, Menlo, monospace;
  font-variant-numeric: tabular-nums;
  font-size: 11.5px;
  color: var(--rd-fg3);
  overflow-wrap: anywhere;
}

/* The chosen account, which REPLACES the search box whole. A bordered block
   rather than another row: the question is answered, and a row still shaped
   like a list item invites a second click with nothing to act on. */
.rd-plat-org__picked {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  max-width: 460px;
  padding: 10px 12px;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
}

.rd-plat-org__picked-who { min-width: 0; }

.rd-plat-org__picked-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg);
}

.rd-plat-org__picked-handle {
  margin-top: 3px;
  font-family: ui-monospace, Menlo, monospace;
  font-variant-numeric: tabular-nums;
  font-size: 11.5px;
  color: var(--rd-fg3);
  overflow-wrap: anywhere;
}

/* The quiet act, gaining its boundary on hover — the restraint `.rd-plat__del`
   above uses, in the neutral role rather than the danger one: clearing a pick
   destroys nothing. */
.rd-plat-org__clear {
  flex: none;
  background: none;
  border: 1px solid transparent;
  padding: 5px 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .08em;
  font-size: 9px;
  color: var(--rd-fg3);
  cursor: pointer;
}

.rd-plat-org__clear:hover { border-color: var(--rd-ctrl); color: var(--rd-fg2); }
.rd-plat-org__clear:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* The TYPE row. ⚠️ AN INDICATOR, NOT A SEGMENTED CONTROL — `CreateOrgRequest`
   carries no type field, personal orgs are sign-up's to make, and nothing on
   this screen can select one. The drawing draws both halves as plain `div`s
   (rd-platform.jsx:240) and so does this: no hover, no cursor, no focus ring,
   nothing that offers a click. The markup is `aria-hidden` beside a real label,
   so the accessibility tree is not told about a choice that does not exist.

   Geometry verbatim: `padding: '8px 14px'`, `gap: 8`, `pdisp(700, 78, '.06em')`
   at 10.5px, the lit one bordered `t.fg` in `t.fg` and the other `t.ctrl` in
   `t.fg3`. */
.rd-plat-org__seg { display: flex; gap: 8px; }

.rd-plat-org__seg-on,
.rd-plat-org__seg-off {
  padding: 8px 14px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .06em;
  font-size: 10.5px;
}

.rd-plat-org__seg-on { border: 1px solid var(--rd-fg); color: var(--rd-fg); }
.rd-plat-org__seg-off { border: 1px solid var(--rd-ctrl); color: var(--rd-fg3); }

/* ⚠️ The owner and type blocks carry NO `border-top`, and that is a decision.
   The drawing's only rule on this screen is the one above the BILLING block,
   and that block is ruled out — a divider kept after the thing it divided is
   gone separates the form from nothing. Their spacing is `.rd-plat-new`'s grid
   gap, so neither carries a margin either. */
.rd-plat-org__owner,
.rd-plat-org__type { margin: 0; }

/* ---------------------------------------------------------------------------
   ORGANIZATIONS — `ScreenPlatOrgs` (`rd-platform.jsx:179-207`).

   The list's body ground is `.rd-plat` above, unchanged: same console, same
   32px gutter under `ProTop`, same `position: relative`. Only the five cells'
   TYPE lives here, because geometry is the `ProColumn`'s
   (`platform_orgs.rs::org_columns`) and the row shape is `ProTable`'s.

   ⚠️ Two of the five spans exist ONLY to carry a half-pixel. `15-pro-table.md`
   §6.1 Q5 ships the kit's `Name` role at 14px and its `Meta` role at 12.5px as
   the MODE of the drawn sites, not as a ruling, and names the inner span as the
   seam every page whose drawn size differs should use — *"the span is the seam,
   and nothing here should absorb it."* Answering Q5 "the sizes are one number"
   deletes `.rd-plat-orgs__name` and `.rd-plat-orgs__made`; answering it "they are
   a scale" turns them into role variants.
   --------------------------------------------------------------------------- */

/* The org name. `pbody` at 13.5px in `--rd-fg` (`rd-platform.jsx:193`) — half a
   pixel under `CellRole::Name`'s 14. The ellipsis is NOT restated here: the
   drawn cell truncates on one line and the kit's `Name` role already carries
   `overflow: hidden; text-overflow: ellipsis; white-space: nowrap` on the cell
   itself, so a second copy on the span would be two declarations able to
   disagree. */
.rd-plat-orgs__name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
}

/* The owner cell — a stack, where the drawing has one line.

   ⚠️ THE SECOND LINE IS OURS AND IS FLAGGED. `rd-platform.jsx:195` draws the
   address alone, because its fixture is four rows the designer wrote himself.
   `PlatformOrgRow` carries `owner_display_name` beside the address precisely so
   an operator's row can name a PERSON — `Org.owner_id` is a UUID and the drawn
   column is a human being — so both are printed, the name leading. Routed to
   the designer; deleting the name line is one line of markup and one rule here.

   `min-width: 0` is load-bearing: this div is a flex item of its `Slot` cell,
   and without its own release a long address pushes the cell past its 210px. */
.rd-plat-orgs__owner {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

/* The name half: the console's body face, one step quieter than the row's
   subject, so the address below it stays the column's machine-readable half. */
.rd-plat-orgs__owner-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The address half — the drawn cell, byte for byte: `pmono` at 11.5px in
   `--rd-fg3`, ellipsized on one line (`rd-platform.jsx:195`).

   ⚠️ A TRUE monospace stack, and `CellRole::Numeric` is NOT it. The substrate
   adjudicated the drawn `mono` helper to Archivo 800 / stretch 75% / tabular, a
   PROPORTIONAL face; this is the corpus's other mono (`pmono`,
   `rd-platform.jsx:78`), and the column is where `l`/`1` in an address must not
   be a guess. Same finding `.rd-plat__ident` records above, same Q5. */
.rd-plat-orgs__owner-mail {
  font-family: ui-monospace, Menlo, monospace;
  font-variant-numeric: tabular-nums;
  font-size: 11.5px;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The headcount. `pmono` at 13px in `--rd-fg2` (`rd-platform.jsx:196`), and the
   same "which mono" note as the address above.

   The right EDGE is not here: the column is `CellAlign::Right`, and
   `column_style` answers a right-aligned `Slot` with `justify-content:
   flex-end` — a `Slot` is a flex container that `text-align` alone would not
   move. Declaring the alignment twice is how a header slides off its column
   three edits from now. */
.rd-plat-orgs__count {
  font-family: ui-monospace, Menlo, monospace;
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  color: var(--rd-fg2);
}

/* When the org was created — `pbody` at 12px in `--rd-fg3`
   (`rd-platform.jsx:197`), half a pixel under `CellRole::Meta`'s 12.5. The
   colour matches the role and is restated anyway: the span sits inside the
   role's cell, and a rule that set only the size would leave a reader unable to
   tell which of the two objects owns the colour. */
.rd-plat-orgs__made {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
}

/* ⚠️ UNDRAWN — the pager. `ScreenPlatOrgs` draws no `Load more` on a list whose
   own fixture says 214 orgs and whose server clamps a page to 50
   (`apps/server/src/user/provider.rs::list_all_orgs`). The control is the
   console's own `.rd-pro-top__btn`, so nothing here paints it — only the band it
   sits in, between the last row's hairline and the foot paragraph's 16px.

   Routed to the designer with the search: both are spec §5 Q7. */
.rd-plat-orgs__pager {
  margin-top: 16px;
}
/* ==== ADM0-picker · 31 Pro Shell (design-corpus-2026-08-10) · owner: lane-3 ==== */
/*
   The org picker. Drawn at `rd-pro-kit.jsx:180-197`; the header's open state at
   `:99`. Appended in this lane's own banner — nothing above is edited.

   The rail becomes the positioning context so the picker can hang under the
   header and over the nav list, as drawn (`position: absolute; top: 71`).
*/
.rd-pro-rail { position: relative; }

/* The header is a control only when there is a picker behind it, so these
   reset the button back to the div it was; the drawn header does not change
   shape when it becomes clickable. */
button.rd-pro-rail__head {
  width: 100%;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

button.rd-pro-rail__head:hover { background: var(--rd-surf2); }

/* Open: the header takes the picker's ground, so the two read as one surface
   (`rd-pro-kit.jsx:120`, `background: open ? t.surf2`). */
.rd-pro-rail__head--open { background: var(--rd-surf2); }

.rd-pro-rail__chev--open { transform: rotate(180deg); }

.rd-pro-orgmenu {
  position: absolute;
  top: var(--rd-pro-rail-head-h, 71px);
  left: 0;
  width: var(--rd-pro-rail-w);
  background: var(--rd-surf2);
  border-bottom: 1px solid var(--rd-line);
  box-shadow: 0 18px 40px rgb(0 0 0 / .5);
  z-index: 5;
}

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

.rd-pro-orgmenu__row {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 16px;
  background: none;
  border: 0;
  border-left: 2px solid transparent;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.rd-pro-orgmenu__row:hover { background: var(--rd-surf); }

/* The current org: accent edge, heavier name. The mark's own accent comes from
   `OrgMark`'s `on` prop, not from here. */
.rd-pro-orgmenu__row--on { border-left-color: var(--rd-acc); }

.rd-pro-orgmenu__text { flex: 1; min-width: 0; display: block; }

.rd-pro-orgmenu__name {
  display: block;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rd-pro-orgmenu__row--on .rd-pro-orgmenu__name { font-weight: 600; }

.rd-pro-orgmenu__role {
  display: block;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8.5px;
  color: var(--rd-fg3);
  margin-top: 3px;
}

/* The closed tag, beside the role word.

   UNDRAWN — rd-pro-kit.jsx has no closed row. It reuses the role line's
   micro-type wholesale (uppercase, 8.5px, the same tracking) by sitting INSIDE
   .rd-pro-orgmenu__role and declaring only what has to differ: a separator, and
   a colour that is not the role's. The alternative — a chip with its own box —
   would out-weigh the org's own name on a two-line row for the state that most
   deserves to be stated quietly.

   --rd-warn is the token the closed banner already carries (BannerTone::Warning),
   so the frame's sentence and this word agree in colour as well as in wording.
   The `::before` is a pipe rather than a margin, because the two words are one
   phrase read left to right and a gap alone reads as a wrapped line. */
.rd-pro-orgmenu__closed { color: var(--rd-warn); }

.rd-pro-orgmenu__closed::before {
  content: '\00b7';
  color: var(--rd-fg3);
  margin: 0 5px;
}

/* The frame's closed-org banner (admin/layout.rs), which sits between the top of
   the content column and the outlet. (It sat under the account strip until that
   row was removed — 27 Aug; the banner's own box is unchanged by that, because
   it never depended on the strip's border.)

   It is OUTSIDE .rd-pro-page — a sibling of it in the frame's content column —
   and it must NOT take .rd-pro-page's `flex: 1 1 auto`, which would grow the
   banner to fill the column and push the page off the bottom. RdBanner owns its
   own padding; the inset here is `.rd-pro-top`'s own 32px, so the sentence lines
   up with the page head under it rather than with the frame's edge.

   ⚠️ IT NO LONGER NAMES A GROUND, AND THAT SENTENCE IS THE PART THAT CHANGED.
   This used to read "so this names the ground rather than inheriting one" and
   carried `background: var(--rd-app-bg)`, which was invisible while the frame's
   ground was the same flat Ink. It stopped being invisible when the dark ground
   became the concrete texture (`--rd-app-ground`, kit-shell.css §0b): an opaque
   Ink slab, full width of the content column, across the top of every screen of
   a closed org — a flat band in the middle of a textured page.

   Transparent for exactly the reason `.rd-pro-page` beside it is: `.rd-pro`
   paints the ground once, on the box that does not scroll and does not grow,
   and everything in its column shows it through. "Inheriting one" was never the
   risk — the frame is the app ground, not an accident. */
.rd-pro__closed {
  flex: none;
  padding: 14px 32px 0;
  background: transparent;
}

/* The sentence that explains the personal org's absence. Not a row, and styled
   as prose so it does not read as one. */
.rd-pro-orgmenu__note {
  margin: 0;
  padding: 11px 16px 13px;
  border-top: 1px solid var(--rd-line);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
  line-height: 1.45;
}

/* Focus, which the drawing does not show and a keyboard user cannot do without.
   `:focus-visible` rather than `:focus`, so a pointer click does not leave a
   ring behind — the drawn hover already answers the pointer. */
button.rd-pro-rail__head:focus-visible,
.rd-pro-orgmenu__row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* ---------------------------------------------------------------------------
   The picker as a SCREEN — the console's entry, 2026-08-20.
   ---------------------------------------------------------------------------

   `44 The Doors.html` rules that a multi-org switch lands "on the org picker
   instead of presuming one", which the console did not have: it auto-selected
   the first staff org in the session's order and opened it. `admin/layout.rs`
   now shows this screen when nobody has picked yet and there is more than one
   org to pick from.

   ⚠️ THE FRAME IS UNDRAWN AND THE ROWS ARE NOT, which is the whole reason this
   block sits in THIS part rather than in one of its own. `44 The Doors` names
   the picker as a destination and draws no picker frame, so every value that
   could be transcribed is transcribed from the DROPDOWN above — the same 26px
   mark, the same name/role pair, the same `Closed` tag with its pipe, the same
   closing sentence (one constant, `PERSONAL_ORG_NOTE`, shared by both). What is
   ours is the frame: a centred card, a heading and one instruction. Nothing was
   invented to fill it, and when the picker is drawn this is what it replaces.

   The two blocks share no selector — `.rd-pro-orgmenu*` there, `.rd-pro-pick*`
   here — so neither outranks the other. They are two presentations of one list,
   kept beside each other on purpose: a value changed in one and not the other is
   visible in a single screen of this file.

   ⚠️ THIS IS A FULL-VIEWPORT ROOT, NOT A PAGE INSIDE THE FRAME, so it paints
   the app ground itself. `admin/layout.rs`'s `needs_org_pick` arm is an early
   `return`: no rail, no `ProChrome`, no `.rd-pro` above it — *"there is no
   current org yet, and a frame that named one would be answering the question
   the screen is asking."* So unlike `.rd-pro-page` and `.rd-pro__closed`, which
   are transparent over `.rd-pro`'s ground, this one has nothing behind it and
   must name `--rd-app-ground` (kit-shell.css §0b) or show the browser's own
   canvas. It was left on the flat `--rd-app-bg` when the texture shipped, which
   made the console's ENTRY the one post-auth screen without the app's ground.

   Two things about this box are RECORDED rather than worked around. Both
   predate this change, neither is caused by it, and both are smaller than the
   structural edit that would fix them on an UNDRAWN frame:

     - `min-height`, so it can grow past the viewport if a reader holds staff
       roles in enough orgs to overflow the card, and `cover` then scales to the
       grown box rather than to the viewport. A staff-org list that long is not
       a shape this screen has. The alternatives — a fixed layer, or a scroller
       inside a definite-height box — are both larger than the defect earns.
     - `100vh` and NOT `100dvh`, which is what every other full-viewport root
       here uses and what `kit-shell.css` §1 argues for at length ("on mobile
       browsers 100vh is the URL-bar-collapsed height, so a 100vh shell is
       taller than the visible viewport"). Because this is `min-height` rather
       than `height`, the symptom is a ground slightly TALLER than the glass
       rather than content below the fold, which is why it has gone unnoticed.
       Left alone here — converging it is a change to this screen's geometry,
       not to its ground — but worth doing the next time it is opened. */
.rd-pro-pick {
  flex: 1 1 auto;
  min-height: 100vh;
  box-sizing: border-box;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 64px 24px;
  background: var(--rd-app-ground);
}

.rd-pro-pick__card {
  width: 100%;
  max-width: 420px;
  background: var(--rd-surf);
  border: 1px solid var(--rd-line);
}

/* The page's h1, in the Pro page-title role — `pdisp(800, 72, '.02em')`, the
   named width ALLOWED_STRETCH admits with a 20–27px band. 22px is inside it. */
.rd-pro-pick__title {
  margin: 0;
  padding: 22px 22px 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 72%;
  letter-spacing: .02em;
  font-size: 22px;
  line-height: 1.05;
  color: var(--rd-fg);
}

/* One sentence of instruction. Body copy, not a label — it is read once. */
.rd-pro-pick__note {
  margin: 0;
  padding: 10px 22px 18px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

.rd-pro-pick__list {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--rd-line);
}

/* The row, transcribed from `.rd-pro-orgmenu__row` above: same 26px mark, same
   11px gap, same left rule declared on every row so a future lit state costs no
   layout. Wider padding, because this is a card and not a 232px dropdown. */
.rd-pro-pick__row {
  width: 100%;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 13px 22px;
  border: 0;
  border-left: 2px solid transparent;
  background: transparent;
  text-align: left;
  cursor: pointer;
  font: inherit;
}

.rd-pro-pick__row:hover {
  background: var(--rd-surf2);
}

.rd-pro-pick__row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-pro-pick__text {
  flex: 1;
  min-width: 0;
}

.rd-pro-pick__name {
  display: block;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
}

.rd-pro-pick__role {
  display: block;
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9px;
  color: var(--rd-fg3);
}

/* --rd-warn and the pipe, both transcribed from `.rd-pro-orgmenu__closed`:
   the frame's closed-org banner is BannerTone::Warning, so the word and the
   sentence agree in colour as well as in wording. */
.rd-pro-pick__closed {
  color: var(--rd-warn);
}

.rd-pro-pick__closed::before {
  content: '\00b7';
  color: var(--rd-fg3);
  margin: 0 5px;
}

.rd-pro-pick__foot {
  margin: 0;
  padding: 13px 22px 16px;
  border-top: 1px solid var(--rd-line);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
  line-height: 1.45;
}

/* ==== SCORE-INPUTS · apps/web/src/pages/score_inputs.rs · owner: lane-3 ==== */
/*
   The shared per-entry score input cluster: zone-hit steppers, the time /
   penalties / pass-fail fields, notes, and the live preview. Visual
   conversion only — every `<input>`/`<textarea>`'s type, step, min and
   handler is unchanged; only the classes carrying its paint moved here.

   No kit primitive serves a stepper or a two-way toggle (same gap AU5 and
   RdNumericField leave open for a plain number — see `adm-drillf.css`'s own
   note), so these stay plain elements with token-based CSS, matching that
   file's precedent rather than wrapping them in a component built for a
   different shape.
*/
.rd-scorein__section { margin-bottom: 16px; }

.rd-scorein__heading {
  display: block;
  margin-bottom: 8px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .12em;
  font-size: 10px; color: var(--rd-fg3);
}

.rd-scorein__zone-list { display: flex; flex-direction: column; }

.rd-scorein__zone-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 9px 0;
  border-bottom: 1px solid var(--rd-line);
}

/* The drawn zone row is a two-line copy block on the left (`rd-train-run.jsx:
   186-191`): the zone letter, and under it what one hit in it is worth. The
   value was already in hand at the call site and thrown away. */
.rd-scorein__zone-copy { display: flex; flex-direction: column; min-width: 0; }

.rd-scorein__zone-name {
  display: block;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg);
}

.rd-scorein__zone-value {
  display: block;
  margin-top: 1px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
}

.rd-scorein__stepper { display: flex; align-items: center; gap: 10px; }

.rd-scorein__stepper-btn {
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  color: var(--rd-fg);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
}

.rd-scorein__stepper-btn:hover:not(:disabled) {
  border-color: var(--rd-acc);
  color: var(--rd-acc);
}

.rd-scorein__stepper-btn:disabled { opacity: .35; cursor: not-allowed; }

.rd-scorein__stepper-btn:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-scorein__stepper-count {
  min-width: 24px;
  text-align: center;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 14px;
  color: var(--rd-fg);
}

/* The specificity defence, same construction as `adm-drillf.css`'s: the
   pre-kit backfill styles bare inputs and textareas by ATTRIBUTE
   (`input[type="number"]`, `textarea`, `input:focus` — `_legacy-base.css`),
   which is (0,0,1,1) and outranks a single class. The compound below is
   (0,0,2,0) and outranks it. */
.rd-scorein__field .rd-scorein__control {
  width: 100%;
  box-sizing: border-box;
  padding: 11px 12px;
  border: 1px solid var(--rd-ctrl);
  border-radius: 0;
  background: var(--rd-field);
  box-shadow: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 14px;
  color: var(--rd-fg);
  appearance: none;
  -webkit-appearance: none;
}

.rd-scorein__field .rd-scorein__control:focus {
  outline: none;
  border-color: var(--rd-acc);
  box-shadow: none;
}

.rd-scorein__charcount {
  margin-top: 5px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
}

/* The sub-line under a field — drawn as `TNum`'s `sub`
   (`rd-train-kit.jsx:247`), and used here by the one field that has one: the
   OPTIONAL total time a points-scored qualifier gets (`rd-assess.jsx:240`).

   ⚠️ 11px, NOT THE DRAWN 9.5, and NOT CENTRED. The size is the mixed-case type
   floor again (see `.rd-scorein__toggle-note` below). The centring goes because
   the drawn `TNum` is a centred readout box and the shipped control is a
   full-width left-aligned `<input>` — a centred sub-line under a left-aligned
   field would be centred against nothing. */
.rd-scorein__field-sub {
  margin-top: 5px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.45;
  color: var(--rd-fg3);
}

.rd-scorein__toggle-row { display: flex; gap: 8px; }

.rd-scorein__toggle-btn {
  flex: 1;
  padding: 11px 16px;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  color: var(--rd-fg2);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .1em;
  font-size: 11px;
  cursor: pointer;
}

.rd-scorein__toggle-btn:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* ⚠️ **FAIL IS NOT RED, AND PASS IS THE ONE GREEN FILL IN THE PRODUCT.**
   `content/08-assessments.js:16` rules it: *"A fail is a result the shooter
   reported, not an error the app caught. It keeps the foreground ink and a warn
   eyebrow; danger stays reserved for things that are broken."* So `--rd-danger`
   leaves this control entirely.

   The drawing is `rd-assess.jsx:244-245`, the "Your call" pair: selected Pass is
   `TBtn kind="ok"` — a SOLID `t.ok` fill with contrast ink
   (`rd-train-kit.jsx:214`, `color: t.tex ? '#000' : '#fff'`, which is
   `--rd-on-ok`) — and selected Fail is the default kind, the solid volt fill
   with `t.onVolt` ink (`rd-train-kit.jsx:231-232`). Both unselected halves are
   `kind="n"`, the neutral `--rd-ctrl` outline the base rule above already
   draws.

   The washes are gone with the danger pair: two washed halves read as two
   hairlines, where the drawing puts a filled half beside an outlined one and
   that contrast IS the selected state. The light theme's own volt correction
   (`rd-train-kit.jsx:227-231`, a 1.5px acc hairline under the fill) is already
   satisfied here — the border is `--rd-acc`, the same token as the fill.

   ⚠️ THE `ok` FILL NOW HAS A SURFACE. `kit-ok.css`'s head recorded the kind as
   STUBBED ("A button kind with no surface would be a fill nothing can reach"),
   because its one drawn call site was the declare screen this app does not
   ship. The verdict toggle is that call site, reached through `ScoreEntry`
   instead — the token and its companion ink are used as declared there, and
   that note is corrected in place. */
.rd-scorein__toggle-btn--pass-on {
  background: var(--rd-ok);
  border-color: var(--rd-ok);
  color: var(--rd-on-ok);
}

.rd-scorein__toggle-btn--fail-on {
  background: var(--rd-acc);
  border-color: var(--rd-acc);
  color: var(--rd-on-volt);
}

/* The one attribution sentence under the pair — `rd-assess.jsx:247-249`. The
   drawing's first half quotes a passing standard that has no field on our wire
   (audit r7 doc 08, Q5), so only the second sentence ships, verbatim.

   ⚠️ 11px, NOT THE DRAWN 10 — the mixed-case half of the type floor
   (`01 Foundations` §05, "Mixed-case text never goes below 11px on the
   tablet"), applied not transcribed, the same correction `adm-loc.css:640`
   records for its own drawn 10.5. This cluster renders inside `ScoreEntry`'s
   tablet arm as well as the phone's, so the tablet floor has jurisdiction. */
.rd-scorein__toggle-note {
  margin: 8px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.45;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

.rd-scorein__preview {
  padding: 12px 14px;
  border: 1px solid var(--rd-line);
  background: var(--rd-surf2);
  margin-bottom: 16px;
}

.rd-scorein__preview-heading {
  margin-bottom: 6px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .12em;
  font-size: 10px; color: var(--rd-fg3);
}

.rd-scorein__preview-row {
  display: flex;
  gap: 16px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
}

.rd-scorein__preview-item { color: var(--rd-fg2); }

/* ==== SCORE-HISTORY · apps/web/src/pages/score_history.rs · owner: lane-3 ==== */
/*
   The flat run list James ruled (2026-08-12, in 3-reviewer's session): one
   list, newest first, five filter chips — three real (All, Squad, Courses,
   via RdChipSelector) and two stubbed (Drills, Assessments — the drill's
   category isn't on the score yet). The stub chips are plain disabled
   buttons, matching leaderboard.rs's period-stub construction rather than
   reaching for the kit's chip primitive for a control that isn't a chip.
*/
.rd-history__chips { margin: 12px 0 8px; }

.rd-history__stub-chips { display: flex; gap: 8px; margin-bottom: 6px; }

.rd-history__stub-chip {
  padding: 7px 11px;
  background: none;
  border: 1px solid var(--rd-line);
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .1em;
  font-size: 9.5px;
  cursor: not-allowed;
}

.rd-history__error {
  margin: 12px 0;
  padding: 11px 14px;
  border: 1px solid var(--rd-danger);
  background: var(--rd-danger-wash);
  color: var(--rd-danger);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
}

.rd-history__empty { padding: 32px 0; }

.rd-history__list { display: flex; flex-direction: column; margin-top: 8px; }

.rd-history__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 12px 0;
  border-bottom: 1px solid var(--rd-line);
  background: none;
  border-left: 0; border-right: 0; border-top: 0;
  text-align: left;
  cursor: pointer;
  /* History runs to MAX_RUNS = 1000 rows in one flat list, so off-screen rows
     skip layout and paint. 61px = 12+12 padding + the name line (13px, ~16px
     at normal leading, ~21px when a Voided chip rides in it) + the 4px stack
     gap + the sub-line (11px, ~14px) + the 1px hairline. The right column is
     the taller of the two by at most a hair (13px metric + 4px + 11px time),
     so the estimate holds for the drawn anatomy as it held for the transposed
     one; `auto` keeps the real height once measured. */
  content-visibility: auto;
  contain-intrinsic-size: auto 61px;
}

.rd-history__row:hover { background: var(--rd-surf2); }

.rd-history__row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-history__row-main { flex: 1; display: flex; flex-direction: column; gap: 4px; min-width: 0; }

.rd-history__row-drill {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* THE DRAWN ROW ANATOMY (`rd-train-shell.jsx:144-167`) — name and its marks on
   the main line, `{date}` as the sub-line under it, and the metric with the
   time under it in the RIGHT column. The transpose that shipped before put the
   metric on the name line and the date alone on the right. */
.rd-history__row-name-line { display: flex; align-items: center; gap: 8px; min-width: 0; }

.rd-history__row-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* `flex: none` and `text-align: right`: the figures never shrink to give the
   name room — the name is the part that ellipses (`.rd-history__row-drill`). */
.rd-history__row-figs {
  flex: none;
  margin-left: 12px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  text-align: right;
}

.rd-history__row-result {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700; font-stretch: 78%;
  font-size: 13px;
  color: var(--rd-acc);
}

.rd-history__row-time {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

.rd-history__truncated {
  margin-top: 12px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* ==== ADM0-mobile · 45 (Mobile) Pro Shell — chrome, drawer, org sheet, and 59's chromeless root · owner: lane-3 ==== */
/*
   Drawn in `rd-pro-mobile.jsx` under `45 (Mobile) Pro Shell.html`. Components:
   apps/web/src/components/pro/mobile.rs, which carries the two structural
   rulings, the active edge's ruling history (drawn 3px → superseded to 2px by
   `design-reply-2026-08-21` §3 → ruled back to 3px for both apps by export 25's
   `48 Kit Rules.html`, Shell Geometry) and the refusal-copy argument. Only the
   values live here.

   ANATOMY:

     [ .rd-prom — the phone shell root ]
       [ .rd-prom__chrome — menu control · org mark · name · role · chevron ]
       [ .rd-prom__body — the Outlet, and the page's own ProTop inside it ]
       [ .rd-prom__overlay — scrim + one of: ]
         [ .rd-prom__drawer — head · rows · spacer · account · door ]
         [ .rd-prom__sheet  — "Switch org" · team rows · personal rows ]

     [ .rd-prom-bare — the CHROMELESS root (59 (Mobile) Pro Scoring §37) ]
       [ the Outlet, and nothing above it ]

   ⚠️ NO `@media` IN THIS FILE. The breakpoint is asked ONCE, in Rust
   (`device::use_device_class`), and these rules paint what it mounted. The same
   call `s16-tablet.css` makes, and its order note says why: a second statement
   of the threshold in CSS drifts against the Rust one and the two disagree at
   exactly the boundary width.

   ⚠️ NO NEW TOKENS. Every colour below is one of the eleven ADM0 already
   declares. The drawing paints the drawer and the sheet with raw dark hexes
   because its `t.tex` branch is a texture preview, not a palette:

     #101010 (drawer ground, rd-pro-mobile.jsx:90)  -> --rd-surf
     #141414 (sheet ground,  rd-pro-mobile.jsx:124) -> --rd-surf2
     #1A1A1A (lit nav row,   rd-pro-mobile.jsx:102) -> --rd-surf2
     #1C1C1C (lit org row,   rd-pro-mobile.jsx:128) -> --rd-surf2

   Each of those is the `t.tex ? <hex> : <token>` pair's OTHER half in the same
   declaration — the drawing names the token itself beside the hex, so this is a
   transcription of the drawing's own non-texture branch rather than a colour
   decision of ours. The one place it is not a straight read is the drawer
   ground: the JSX pairs #101010 with `t.surf`, and the sheet pairs #141414 with
   `t.surf` too. Two different grounds cannot both be `--rd-surf` and stay
   distinguishable where they overlap, so the sheet — which sits OVER a screen
   painted `--rd-surf` and needs to read as a layer above it — takes
   `--rd-surf2`, which is what the drawing's own darker hex says in the texture
   branch. Recorded rather than silently reconciled.

   ⚠️ TWO TYPE FLOORS, applied not transcribed. `01 Foundations` §05 Type Scale,
   "Two floors, one exemption": mixed-case text never goes below 11px, and
   condensed uppercase labels with tracking may go to 9.5px. The drawing's 8.5px
   tracked labels ship at 9.5 and its 10px mixed-case personal note ships at 11.
   Marked at each site.
*/

/* ---------------------------------------------------------------------------
   1. The shell root.
   --------------------------------------------------------------------------- */
/* `rd-pro-mobile.jsx:48` puts the screen in `TShell … pad={18}` and the column
   inside it at `paddingTop: 6`. The 18px gutter is the phone frame's, so it is
   authored here rather than borrowed: this shell is not inside `RdShell` (Pro
   has no tab bar and no shooter frame) and nothing else supplies it.

   ⚠️ THE TOP INSET IS OURS TO SUPPLY, AND NOTHING ELSE WAS SUPPLYING IT.
   `TShell` draws a fixed spacer of `top` px above everything
   (`rd-train-kit.jsx:151`), and every frame in `45 (Mobile) Pro Shell.html` and
   `46 (Mobile) Pro Destinations.html` passes `top={58}` — the device status-bar
   inset, the same number the shooter documents pass. The shooter side ships that
   as `--rd-shell-inset-top` (`kit-shell.css`: `calc(12px + env(safe-area-inset-top))`),
   but the admin routes close `ShellLayout` before they open (`main.rs`), so
   `.rd-prom` IS the outermost frame and inherits none of it. The manifest is
   `display: standalone` and `index.html` sets `viewport-fit=cover`, so on a
   notched phone the installed app really does paint under the status bar: the
   org chrome — the second structural tell, the block that says whose console
   this is — was landing beneath it.

   The env() term ALONE, and not the shooter's 12px of air beside it. 58 in the
   frames is the device inset, not design air; the drawn air above the chrome is
   the `paddingTop: 6` this rule already carries. In a browser tab env() resolves
   to 0, so this changes nothing about the rendering shipped today and everything
   about the installed one. The file already spells the bottom half of exactly
   this idiom on `.rd-prom__foot` and `.rd-prom__sheet`; this is the top edge,
   which had been missed. */
/* ⚠️ A HEIGHT AND A CLIP, NOT A FLOOR — CHANGED 2026-08-27, AND IT IS THE SAME
   DEFECT `adm0-chrome.css` FIXED ON `.rd-pro`, ARRIVING ON THE PHONE.

   This was `min-height: 100vh; min-height: 100dvh` with nothing clipped, so the
   DOCUMENT was the console's scroller on the phone. James, iPad, 27 Aug: open
   the on-screen keyboard on a Pro form, close it, and the org chrome is above
   the fold. iOS does not shrink the layout viewport for the keyboard — only the
   visual one — so to reveal the focused field Safari scrolls the nearest
   scrollable ancestor, and a scrollable document is one. Nothing scrolls it back
   on dismissal. `kit-shell.css` §0d carries the full argument and the shooter
   half of the same fix; this is the console's phone half, and it is what let
   that rule drop its `[data-app="shooter"]` scoping.

   ⚠️ THIS IS THE DRAWING, NOT A DEVIATION FROM IT. `ProMobileScreen`
   (`rd-pro-mobile.jsx:48`) mounts every Pro phone screen inside `TShell` — the
   shooter kit's own shell — and `TShell` (`rd-train-kit.jsx:150`) is
   `height: '100%'`, `overflow: 'hidden'`, with its content region at
   `flex: 1, minHeight: 0` (`:159`). The frame was always bounded in the corpus;
   the document scroll was ours. `45 (Mobile) Pro Shell.html` renders those same
   frames at a fixed device height, so there was never a drawn state in which the
   page scrolled behind the chrome.

   `100vh` FIRST AND `100dvh` SECOND, the pair this rule already carried: the
   first is the fallback for a browser without `dvh` units, the second is what
   every modern one takes — and `dvh` is the one that tracks the URL bar, which
   is the whole reason `kit-shell.css` §1 refuses a bare `vh` on a shell.

   The three FIXED overlays are unaffected: `.rd-prom__overlay` is
   `position: fixed; inset: 0` (§4), so the drawer and the org sheet still span
   the real viewport rather than this box. */
.rd-prom {
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background: var(--rd-app-bg);
  padding: calc(6px + env(safe-area-inset-top, 0px)) 18px 0;
  font-family: 'Archivo', system-ui, sans-serif;
}

/* ⚠️ `.offline-banner ~ .rd-prom { height: calc(100dvh - …) }` STOOD HERE AND
   IS DELETED, 2026-09-01 (James, option 2) — the third spelling of one rule,
   gone with the other two.

   It was needed while `ConnectivityShell` rendered the banner as this frame's
   SIBLING: a 100dvh frame under a 63px strip overflowed its parent by exactly
   the strip and the last row went below the fold, which is the state a console
   at a range is most likely to be in. `ProMobileShell` mounts `OfflineBanner`
   as the first child of this column now, above the org chrome, so the frame
   keeps its clean 100dvh and `.rd-prom__body` — the one scroller, `flex: 1 1
   auto; min-height: 0` — is what gives up the height. */

/* The destination column, AND THE CONSOLE'S ONE SCROLLER ON THE PHONE.

   `min-height: 0` is the load-bearing line, as on the desktop shell's main
   column: a flex child defaults to `min-height: auto` and refuses to shrink
   below its content, so without it a long list grows this column past the frame
   above and only that frame's clip stops it — the page would be CUT OFF instead
   of scrolling. Same line, same reason, as `.rd-pro__main` (`adm0-chrome.css`)
   and `.rd-shell__content` (`kit-shell.css` §6).

   `overflow-y: auto` is the other half and it arrived with the frame's clip
   (§1, 2026-08-27). Scrolling the REGION rather than the document is what keeps
   the org chrome planted while content moves under it, and it is where iOS's
   focused-field reveal now lands.

   ⚠️ `auto` ON ONE AXIS ONLY, deliberately, and the horizontal axis is left
   alone rather than set to `hidden`: `kit-shell.css` §6 measured that argument
   for the shooter's content region — `hidden` does not prevent a wide row's
   overflow, it CONCEALS it, and unmigrated console pages are exactly the
   population that has wide rows. An unset `overflow-x` computes to `auto` here
   anyway once `overflow-y` is set, so the visible-overflow case is stated by
   saying nothing rather than by contradicting it. */
.rd-prom__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}

/* ---------------------------------------------------------------------------
   1b. The CHROMELESS root — 59 (Mobile) Pro Scoring, §37.
   --------------------------------------------------------------------------- */
/* ⚠️ A SECOND ROOT, NOT A MODIFIER, AND NOT A NEW PART.

   `59 (Mobile) Pro Scoring.html:37` takes the chrome off every screen in the
   scoring lab: "the menu control and the org header come off every screen in
   this lab (James, 27 Aug) — mid-squad, identity is overhead and the full
   device height goes to the work; the back row is the only way out." The
   drawing spells it as the same component with a flag — every screen in
   `rd-pro-scoring-lab.jsx` is `<ProMobileScreen … chromeless>` (:20, :75, :139,
   :189) — so the frame it wants is `.rd-prom`'s frame with §2's block absent.
   `admin/layout.rs`'s chromeless arm renders this root instead of mounting the
   shell.

   WHY IT LIVES IN THIS FILE rather than in a part of its own: `.rd-prom*` is
   this part's name space and nothing else in the pool declares it. Half a
   root's vocabulary in another file is the drift the order-file's own filing
   notes keep refusing.

   WHY IT IS NOT GROUPED WITH `.rd-prom` ABOVE, which would remove the four
   repeated declarations outright: `pro/shell.rs`'s
   `ruling_no_console_part_declares_a_competing_scroller` reads the whole
   selector prelude as ONE string and matches its allowlist against it, so
   `.rd-prom, .rd-prom-bare { … }` would stop matching the `.rd-prom` entry that
   part already carries and report the pair as an unbounded viewport claim. The
   grouping would trade four honest duplicate lines for a guard that no longer
   sees either root. Both roots are named in that allowlist instead, separately.

   WHAT IS DELIBERATELY NOT REPEATED: `background`. §1's root paints
   `--rd-app-bg` because the chrome sits on it; a scoring surface fills its own
   ground and the page inside declares it, so this root only supplies the frame.

   The scroll model is §1's, unchanged and for §1's reason: `min-height: 100dvh`
   and the DOCUMENT scrolls. No `overflow` here — a scrolling box would need
   `min-height: 0` and would be a second scroll model on the one surface whose
   whole point is the full device height. */
.rd-prom-bare {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  padding: calc(6px + env(safe-area-inset-top, 0px)) 18px 0;
  font-family: 'Archivo', system-ui, sans-serif;
}

/* ---------------------------------------------------------------------------
   2. The permanent chrome — rd-pro-mobile.jsx:30.
   --------------------------------------------------------------------------- */
/* `display: 'flex', alignItems: 'center', gap: 11, padding: '6px 0 14px',
   borderBottom: 1px t.line, marginBottom: 16`. */
.rd-prom__chrome {
  flex: none;
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 6px 0 14px;
  border-bottom: 1px solid var(--rd-line);
  margin-bottom: 16px;
}

/* The menu control — rd-pro-mobile.jsx:20: `width: 40, height: 40, flex: 'none',
   border: 1.5px solid t.ctrl`, three 14x1.5 bars in `t.fg2` with `gap: 4`.

   ⚠️ THE HAMBURGER IS A PLACEHOLDER BY RULING, not by omission. `45 (Mobile)
   Pro Shell.html`: "placeholder glyphs ship and get replaced when real art
   lands; the menu control's hamburger is the first." Drawn in markup rather
   than as an icon because `RdIconName` has no menu mark.

   The drawn `tcham(9)` chamfer is NOT taken: a chamfer here would mean an
   `RdChamferBox` around three bars, and the box's opaque inner layer plus the
   40px tap target is more machinery than a placeholder glyph earns for the one
   release it exists. Recorded as a divergence rather than transcribed. */
/* The drawn tile is 40×40 and stays 40×40. `::after` lends it the last 4px in
   each axis so the target clears 44 — James, NTOA rehearsal 2026-08-29. 2px a
   side against `.rd-prom__chrome`'s 11px gap leaves 7px still clear between
   this target and the org button's, so nothing overlaps; a real 44px tile would
   have moved the chrome's whole baseline. Shared note: `kit-hit.css`. */
.rd-prom__menu::after {
  content: "";
  position: absolute;
  inset: -2px;
}

.rd-prom__menu {
  flex: none;
  position: relative;
  width: 40px;
  height: 40px;
  padding: 0;
  background: none;
  border: 1.5px solid var(--rd-ctrl);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.rd-prom__menu:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

.rd-prom__menu-bars {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}

.rd-prom__menu-bar {
  display: block;
  width: 14px;
  height: 1.5px;
  background: var(--rd-fg2);
}

/* The org identity block. `flex: 1, minWidth: 0` on the text so a long org name
   truncates instead of pushing the chevron off the row. */
.rd-prom__org {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 11px;
  text-align: left;
}

/* A control only when there is a sheet behind it, so this resets the button
   back to the div it was — the drawn block does not change shape when it
   becomes clickable. The same treatment `button.rd-pro-rail__head` takes. */
button.rd-prom__org--btn {
  padding: 0;
  background: none;
  border: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
}

button.rd-prom__org--btn:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

.rd-prom__org-text {
  flex: 1 1 auto;
  min-width: 0;
}

/* rd-pro-mobile.jsx:34 — `pdisp(800, 78, '.03em')` at 13px, `color: t.fg`,
   nowrap + ellipsis. The ellipsis IS drawn here and is kept: unlike the switch
   door's name line, this block names the org you are ALREADY in and the drawer
   head restates it at 17px with room to wrap. */
.rd-prom__org-name {
  font-weight: 800;
  font-stretch: 78%;
  font-size: 13px;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--rd-fg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* rd-pro-mobile.jsx:35 — `pdisp(700, 78, '.12em')` at 8.5px, `color: t.fg3`,
   `marginTop: 3`.

   ⚠️ 9.5px, NOT THE DRAWN 8.5 — the condensed-uppercase floor (`01 Foundations`
   §05). The same correction `.rd-door__lab` took. */
.rd-prom__org-role {
  font-weight: 700;
  font-stretch: 78%;
  font-size: 9.5px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--rd-fg3);
  margin-top: 3px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* rd-pro-mobile.jsx:37 — `color: t.fg3, fontSize: 11`. */
.rd-prom__chev {
  flex: none;
  font-size: 11px;
  line-height: 1;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   3. The title row — the page's own ProTop, re-laid under this shell.
   --------------------------------------------------------------------------- */
/* ⚠️ DESCENDANT OVERRIDES, NOT A WRAPPER. Every Pro destination already mounts
   `ProTop`; the phone shell re-lays it here so no page changes and no second
   title row exists to keep in step with the desktop one.

   `rd-pro-mobile.jsx:52` draws the row as `display: flex, alignItems:
   'flex-end', gap: 12` with the title at 27px and one optional act beside it —
   `46 (Mobile) Pro Destinations.html:48`: "Page acts sit beside the title, one
   per screen at most." `ProTop` takes exactly one `action`, so the ceiling is
   structural.

   The desktop's `padding: 26px 32px 20px` and its hairline both go: the shell
   already carries the 18px gutter, and the chrome above already draws the one
   rule this screen needs. The title's own type is unchanged — the desktop rule
   is already 27px, which is the drawn `TH1 size={27}`. */
/* ⚠️ `min-height: 0` (then `height: auto`) IS PART OF THE RE-LAY, NOT TIDYING.
   The desktop rule gained `min-height: 76px` on 2026-08-24 (`48 Kit Rules.html`,
   Shell Geometry — a DESKTOP header-bar ruling: "header bars 76 min") and was
   upgraded to a hard `height: 76px` on 27 Aug — a relay note, not an edit to
   that doc (`design-reply-2026-08-27.md`, Shell Lab: "Header bar: HARD 76").
   The phone frame draws no such bar at either ruling: `rd-pro-mobile.jsx:53`
   is a title row inside the shell's own gutter, with no rule under it and no
   fixed floor or cap. Inheriting either would put an invisible 76px band above
   every phone screen's first row — with the desktop rule now a hard `height`
   rather than a `min-height`, that inheritance would win on the `height`
   property too unless overridden here explicitly — a fixed `height: 0` would
   be wrong the same way inheriting 76 would be (it would force the row to
   zero and paint its content overflowing above the box instead of sizing to
   it), so the override is `height: auto`, added alongside `min-height: 0`
   rather than replacing it, so the row is content-sized against either
   desktop declaration. `align-items` was already `flex-end` before either
   change and stays stated, because this rule re-lays the desktop box rather
   than depending on what it happens to hold. */
.rd-prom .rd-pro-top {
  padding: 0;
  min-height: 0;
  height: auto;
  border-bottom: 0;
  align-items: flex-end;
  gap: 12px;
}

/* ⚠️ THE PHONE TITLE STAYS 27px. The desktop rule went 27 -> 26 on 2026-08-24
   (`rd-pro-kit.jsx:226`, "fontSize: 26"), and the phone frame did not move with
   it — `rd-pro-mobile.jsx:53` still draws `TH1 size={27}`. One number changed,
   in one frame, so the other frame states its own. Without this the phone title
   would have silently followed a desktop redraw it was not part of. */
.rd-prom .rd-pro-top__title {
  font-size: 27px;
}

/* rd-pro-mobile.jsx:54 — the act is `minHeight: 40, padding: '0 14px',
   border: 1.5px solid t.acc, color: t.acc, pdisp(800, 78, '.1em')` at 10.5px.
   Floored to 11px: it is tracked uppercase, so 9.5 is its floor and 10.5 clears
   it — the drawn size ships unchanged. Authored on the descendant rather than
   on `.rd-pro-top__action` itself so the desktop act is untouched. */
.rd-prom .rd-pro-top__action {
  align-self: flex-end;
}

/* The sub-line is drawn away at this width: the drawn title row has a title and
   an act and no third line, and 448px has no room for one under a 27px head. */
.rd-prom .rd-pro-top__sub {
  margin-top: 6px;
  font-size: 11px;
}

/* ---------------------------------------------------------------------------
   4. The overlay layer — scrim, drawer, sheet.
   --------------------------------------------------------------------------- */
/* `position: fixed`, not absolute: the drawn frame is a 100%-height preview
   canvas (`rd-pro-mobile.jsx:87`), and on a real phone the shell scrolls. A
   drawer anchored to the document would slide up out of view with the page. */
.rd-prom__overlay {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
}

/* rd-pro-mobile.jsx:89 — `background: 'rgba(0,0,0,.55)'`.

   ⚠️ A LITERAL, DELIBERATELY. This is not a surface colour that flips with the
   theme; it is a dimming of whatever is behind it, and it is the same value in
   both themes because the thing it dims is the same screen. No token carries a
   translucent black, and inventing one for a single site would be a token with
   one caller.

   It is a real `button` in the markup — the only dismissal a thumb can reach,
   since the drawer covers 78% of the screen — so these reset the UA button. */
.rd-prom__scrim {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: rgba(0, 0, 0, .55);
  cursor: pointer;
}

.rd-prom__scrim:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -4px;
}

/* ---------------------------------------------------------------------------
   5. The drawer — rd-pro-mobile.jsx:90.
   --------------------------------------------------------------------------- */
/* `position: absolute, top/bottom 0, left 0, width: '78%'`, ground #101010 /
   t.surf, `borderRight: 1px t.line`, `paddingTop: top + 10`.

   The width is a custom property because 78% is the drawing's number and the
   one thing a later frame is most likely to restate.

   ⚠️ `paddingTop: top + 10` IS TWO NUMBERS AND ONLY ONE OF THEM WAS BUILT.
   `rd-pro-mobile.jsx:90` adds the frame's `top` — 58, the device inset — to its
   own 10px of air, because the drawer is `position: absolute; top: 0` inside the
   frame and so has the notch above it just as the screen behind it does. Ours is
   `position: fixed; inset: 0` (see §4), which puts it against the real viewport's
   top edge, so it needs the real inset for the same reason: without it the
   drawer's org head — the same identity block the chrome carries — slides under
   the status bar of an installed app. `env()` is 0 in a browser tab, so the 10px
   is what is painted there, unchanged. See `.rd-prom` §1 for the full argument. */
.rd-prom__drawer {
  --rd-prom-drawer-w: 78%;
  position: relative;
  width: var(--rd-prom-drawer-w);
  max-width: 320px;
  display: flex;
  flex-direction: column;
  background: var(--rd-surf);
  border-right: 1px solid var(--rd-line);
  padding-top: calc(10px + env(safe-area-inset-top, 0px));
  overflow-y: auto;
}

/* rd-pro-mobile.jsx:91 — `gap: 11, padding: '6px 16px 16px',
   borderBottom: 1px t.line, marginBottom: 8`. */
.rd-prom__drawer-head {
  flex: none;
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 6px 16px 16px;
  border-bottom: 1px solid var(--rd-line);
  margin-bottom: 8px;
  text-align: left;
}

button.rd-prom__drawer-head--btn {
  width: 100%;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  font: inherit;
  color: inherit;
  cursor: pointer;
}

button.rd-prom__drawer-head--btn:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-prom__drawer-text {
  flex: 1 1 auto;
  min-width: 0;
}

/* rd-pro-mobile.jsx:94 — `pdisp(900, 66, '.01em')` at 17px, `color: t.fg`,
   `lineHeight: 1`. It WRAPS rather than truncating: this is the drawer's own
   statement of which org you are in, it has 78% of the screen, and the chrome
   above already carries the truncating copy.

   ⚠️ 66, RESOLVED 2026-08-27 — was held at 62 pending the designer. Design
   reply, `~/trex-arms/uploads/design-reply-2026-08-27.md` §C-45.1: "The 64-66
   small-display cluster resolves to one named role at 66 - the existing Tab
   Title width, adopted for the drawer name and doc 55's thumb words ... The
   shipped `.rd-prom__drawer-name` at 62 is drift." 66 is now in
   `ALLOWED_STRETCH` (cli/src/css.rs) with that citation. The compromise this
   comment used to describe (62 at 17px, "right at 30px and slightly too tight
   below about 18px") is closed by the same ruling — this is the site that
   named the role. */
.rd-prom__drawer-name {
  font-weight: 900;
  font-stretch: 66%;
  font-size: 17px;
  letter-spacing: .01em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--rd-fg);
  overflow-wrap: anywhere;
}

/* rd-pro-mobile.jsx:95 — `pdisp(700, 78, '.14em')` at 8.5px, `color: t.fg3`,
   `marginTop: 5`. 9.5px by the condensed-uppercase floor. */
.rd-prom__drawer-role {
  font-weight: 700;
  font-stretch: 78%;
  font-size: 9.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--rd-fg3);
  margin-top: 5px;
}

/* rd-pro-mobile.jsx:98 — `display: 'grid'`, one row per PRO_NAV entry. */
.rd-prom__list {
  flex: none;
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
}

/* rd-pro-mobile.jsx:102 ("minHeight: 56") — the drawer row, and :101's
   `pdisp(800, 78, '.05em')` at 13px, `color: on ? t.fg : t.fg2`.

   ⚠️ THE ROW'S GEOMETRY MOVED IN THE 24 Aug e25 EXPORT AND IS NOW BUILT. Until
   e25 `:100` drew `minHeight: 48, padding: '0 16px'`; e25 redraws it as
   `minHeight: 56, padding: '9px 16px'` stacked in a column with `gap: 3`, and
   adds a second line at `:102` — `PRO_NAV_SUB[k]` at 10.5px `t.fg3`
   (rd-pro-kit.jsx:55-62), the same two-line anatomy `RailItem` takes at
   rd-pro-kit.jsx:131-141. `48 Kit Rules.html`'s Shell Geometry section rules
   the numbers for both apps at once: *"rail rows 56 min with a 3px active
   edge"*.

   ⚠️ THE EDGE IS 3px NOW, AND THAT REVERSES A SUPERSESSION RATHER THAN FIXING A
   TYPO. This rule shipped 2px on `design-reply-2026-08-21.md` §3 — *"The 2px
   accent left edge on the active row is the shipped rail treatment; build
   that."* — which beat the drawn 3px at the time. Export 25 rules the other way
   in the corpus itself: Shell Geometry names 3px for both rails, the shooter
   rail already ships 3px (`kit-tablet-rail.css`), and this file's own desktop
   sibling `adm0-chrome.css` now does too. The later ruling wins. Pinned by
   `ruling_the_active_edge_is_three_px` in mobile.rs — a failure there is a
   designer question, not a number to bump.

   The transparent edge is declared on the base row, not only on the lit one, so
   lighting a row does not shift its own label 3px to the right. */
.rd-prom__item {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  min-height: 56px;
  padding: 9px 16px;
  box-sizing: border-box;
  border-left: 3px solid transparent;
  font-weight: 800;
  font-stretch: 78%;
  font-size: 13px;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--rd-fg2);
  text-decoration: none;
}

/* The row's second line — `rd-pro-mobile.jsx:104`
   ("fontSize: 10.5, color: t.fg3"): `...tbody, fontSize: 10.5, color: t.fg3,
   lineHeight: 1.3`. `tbody` is font-family only, so every one of the label's
   display-role declarations above has to be undone here: this is mixed-case
   body copy sitting inside a tracked-uppercase row.

   ⚠️ 11px, NOT THE DRAWN 10.5, AND IT IS A FLOOR RATHER THAN A ROUNDING.
   `01 Foundations.html` §05 Type Scale, verbatim: *"Mixed-case text never goes
   below 11px on the tablet. Condensed uppercase labels with tracking may go to
   9.5px … Where an existing tablet drawing shows mixed-case below 11, build 11:
   the scale outranks the drawing"*. The sub is mixed-case (`tbody` sets no
   `text-transform`), so it is the 11px floor that governs and not the 9.5px
   tracked-uppercase one.

   ⚠️ THE SCALE SAYS "TABLET" AND THIS IS A PHONE PART — the extension is THIS
   FILE'S OWN ESTABLISHED PRACTICE, not a reading invented here. Its banner
   applies both floors to this phone document already ("The drawing's 8.5px
   tracked labels ship at 9.5 and its 10px mixed-case personal note ships at
   11"), `.rd-prom__org-role` and `.rd-prom .rd-pro-top__sub` are both floored
   sites in this file, and `.rd-trail__sub` in `kit-tablet-rail.css` is the same
   object one app over (drawn 10, shipped 11, and the drawing has since been
   corrected to 11). Half a pixel, written down because a lane reading `10.5` in
   the JSX and `11` here would otherwise read it as drift.

   THE ×1.25 READING IS NOT TAKEN, AND THAT IS SETTLED — C-01.3 (ratified
   2026-08-27, design-reply-2026-08-27.md): phone screens build at the
   drawing's numbers, with the type floors; the 312 frame is a drawing
   convenience, not a scale factor, and no app-wide resize applies. ×1.25
   exists in exactly one place — a phone component reused inside a tablet
   frame — and this part is not that case: it is a phone document, not a
   phone component reused inside a tablet frame. This part ships drawn
   phone-frame numbers verbatim with the floors applied, including the 13px
   label two rules up, and that stays. */
.rd-prom__sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.3;
  font-weight: 400;
  font-stretch: 100%;
  letter-spacing: 0;
  text-transform: none;
  color: var(--rd-fg3);
}

.rd-prom__item:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-prom__item--on {
  border-left-color: var(--rd-acc);
  background: var(--rd-surf2);
  color: var(--rd-fg);
}

/* rd-pro-mobile.jsx:109 — the drawn `<div style={{ flex: 1 }} />` that pushes
   the fixture to the foot however few rows are above it. Kept as an ELEMENT
   rather than folded into a `margin-top: auto` on the foot: it is what the frame
   draws, and it is what put a second fixture below it back when the account
   strip sat here (removed 27 Aug — see the note below). */
.rd-prom__spacer {
  flex: 1 1 auto;
  min-height: 12px;
}

/* ⚠️ **NO ACCOUNT STRIP AT THIS FOOT EITHER — the `.rd-prom .rd-pro__account`
   re-lay WAS HERE AND IS GONE.** The desktop fixture it re-laid was undrawn and
   James ruled it out on 27 Aug; `45 (Mobile) Pro Shell.html` gives the drawer's
   foot exactly one fixture, the dense "Switch to → Range Day" door, and signing
   out is a shooter-side act on Edit Account. Reintroducing a row here would give
   the drawer two feet again. */

/* `45 (Mobile) Pro Shell.html`: the switch fixture sits "above the
   home-indicator clearance (18px floor, safe-area inset wins)". `max()` is that
   sentence exactly — 18px unless the device asks for more. The drawn
   `paddingBottom: 8` (rd-pro-mobile.jsx:110) is a preview canvas with no home
   indicator in it; the document's prose is the ruling and it wins. */
.rd-prom__foot {
  flex: none;
  padding-bottom: max(18px, env(safe-area-inset-bottom));
}

/* The door is dense at this foot (see kit-switch-door.css §1a). Its own
   `margin-top: auto` is inert inside this wrapper, which is what the spacer
   above is for. */

/* ---------------------------------------------------------------------------
   6. The org sheet — rd-pro-mobile.jsx:124.
   --------------------------------------------------------------------------- */
/* `position: absolute, left/right 0, bottom 0`, ground #141414 / t.surf,
   `borderTop: 1px t.line, padding: '18px 18px 40px'`. `--rd-surf2` here — see
   the token note in this file's banner.

   ⚠️ THE BOTTOM IS 40, AND IT IS NOT THIS PART'S NUMBER. It was
   `max(26px, env(safe-area-inset-bottom))` — the drawn 26 over the drawer's
   18px floor — until export 29 moved the drawing to `'18px 18px 40px'` and
   `48 Kit Rules.html` ruled why: *"Nothing tappable sits in the home-indicator
   zone … every bottom sheet ends with 40px under its last act"* (James,
   25 Aug). The value now arrives as `--rd-sheet-clear` from `kit-shell.css` §0,
   which is where the rule says the clearance lives, so the sheets that carry it
   cannot drift apart. This sheet takes the token because it is VIEWPORT-anchored
   — it sits inside `.rd-prom__overlay` (`position: fixed; inset: 0`), so
   nothing above it has already spent the clearance. */
.rd-prom__sheet {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  max-height: 80%;
  overflow-y: auto;
  background: var(--rd-surf2);
  border-top: 1px solid var(--rd-line);
  padding: 18px 18px var(--rd-sheet-clear);
}

/* rd-pro-mobile.jsx:125 — `pdisp(700, 78, '.14em')` at 8.5px, `color: t.fg3`,
   `marginBottom: 12`. 9.5px by the condensed-uppercase floor. */
.rd-prom__sheet-label {
  font-weight: 700;
  font-stretch: 78%;
  font-size: 9.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--rd-fg3);
  margin-bottom: 12px;
}

/* rd-pro-mobile.jsx:126 — `display: 'grid', gap: 4`. */
.rd-prom__sheet-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 4px;
}

/* rd-pro-mobile.jsx:128 — `gap: 11, padding: '10px 10px'`, 2px accent left edge
   when current, ground #1C1C1C / t.surf2 when current. Rows are buttons: picking
   an org re-scopes every listing and recomputes permissions, which is an act. */
.rd-prom__row {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 10px;
  border: 0;
  border-left: 2px solid transparent;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.rd-prom__row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-prom__row--on {
  border-left-color: var(--rd-acc);
  background: var(--rd-surf);
}

.rd-prom__row-text {
  flex: 1 1 auto;
  min-width: 0;
}

/* rd-pro-mobile.jsx:131 — `pdisp(800, 78, '.03em')` at 12.5px, `color: t.fg`. */
.rd-prom__row-name {
  display: block;
  font-weight: 800;
  font-stretch: 78%;
  font-size: 12.5px;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--rd-fg);
  overflow-wrap: anywhere;
}

/* rd-pro-mobile.jsx:132 — `pdisp(700, 78, '.1em')` at 8.5px, `color: t.fg3`,
   `marginTop: 3`. 9.5px by the condensed-uppercase floor. */
.rd-prom__row-role {
  display: block;
  font-weight: 700;
  font-stretch: 78%;
  font-size: 9.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--rd-fg3);
  margin-top: 3px;
}

/* The closed mark, on the role line rather than on its own — the same call
   `.rd-pro-orgmenu__closed` makes, for the same reason: a second line would make
   a two-line row into a three-line one for the case that needs the least
   attention drawn to it. */
.rd-prom__row-closed {
  margin-left: 8px;
  color: var(--rd-fg2);
}

/* rd-pro-mobile.jsx:136 — `gap: 11, padding: '10px 10px', opacity: .5`.

   ⚠️ NOT A BUTTON, and the half-opacity is the drawn statement of why: a
   personal org is refused at nearly every Pro endpoint, so offering it would be
   "a door that opens onto a refusal". A disabled button would still be a
   control; an `li` is a statement. The 2px of left padding matches the rows
   above so the marks line up under their transparent edges. */
.rd-prom__personal {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 10px;
  padding-left: 12px;
  opacity: .5;
}

.rd-prom__personal-text {
  flex: 1 1 auto;
  min-width: 0;
}

/* rd-pro-mobile.jsx:139 — `pdisp(800, 78, '.03em')` at 12.5px, `color: t.fg2`. */
.rd-prom__personal-name {
  display: block;
  font-weight: 800;
  font-stretch: 78%;
  font-size: 12.5px;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--rd-fg2);
  overflow-wrap: anywhere;
}

/* rd-pro-mobile.jsx:140 — `pbody` at 10px, `color: t.fg3, marginTop: 3`.

   ⚠️ 11px, NOT THE DRAWN 10 — the mixed-case half of the type floor
   (`01 Foundations` §05). `pbody` sets no `text-transform`, so this is body
   copy and 11 is its floor.

   ⚠️ AND THE SENTENCE IS THE LONG ONE. The frame draws "Personal orgs have no
   Pro side"; its own comment at rd-pro-mobile.jsx:117 says the refusal is
   "verbatim from OrgMenu". The comment names a source and the short form does
   not, so `PERSONAL_ORG_NOTE` ships — see mobile.rs. `line-height` is set
   because two lines of it is the common case at this width. */
.rd-prom__personal-note {
  display: block;
  font-size: 11px;
  line-height: 1.4;
  color: var(--rd-fg3);
  margin-top: 3px;
}
/* ==== ADM-DRILLF · docs/design/kit/04 Authoring (evidence, not authority) · owner: lane4 ==== */

/* The drill authoring form's frame and its field helper.

   ⚠️ SOURCE CAVEAT, carried at the top because it governs every value below:
   the drawn authoring flow (design-corpus/components/rd-authoring-create.jsx)
   is the SHOOTER-side wizard — seven screens, phone-shaped, TShell-wrapped.
   This is one desktop admin page. Where the two diverge the drawing is
   EVIDENCE, not authority, and the admin form's behaviour is kept. */

/* ── THE BACK ROW WAS HERE AND IS GONE (2026-08-29) ────────────────────────
   `.rd-drillf__back` and `.rd-drillf__back-link` are both retired. The row this
   page draws is `ProPushedScreen`'s now — `.rd-prom__back`, geometry in
   `adm-evd-mob.css` §6, console gutter in `adm0-chrome.css`
   (`.rd-pro__main .rd-prom__back`, #1347). Nothing here styles it, and nothing
   here may: a page-local rule beside the fixture's is the second spelling that
   produced BOTH of James's findings on this exact row.

   The history is kept rather than deleted, because it is the reason the rule
   may not come back:

   1. ⚠️ **JAMES RED-LINE, 2026-08-18 (item 12)** — the link was raw text
      (`.rd-drillf__back-link`: bare `color` + `:hover`, no border, no touch
      target), flagged unstyled. It took `.rd-drillf__btn`, the SAME class
      `Cancel` wears for the identical `Route::AdminDrills` destination.
   2. ⚠️ **NTOA REHEARSAL, 2026-08-24, finding 2 — IT TOUCHED THE RAIL.** The
      row was a direct child of `.rd-pro-page`, which paints ground and nothing
      else (`adm0-chrome.css`, "The page ground": no padding, on purpose — every
      page pane supplies its own). It declared a bottom margin and no horizontal
      pad, so it started at x=0, hard against the 208px rail's 1px border, while
      `ProTop` above it and `.rd-drillf` below it both sat at 32. Fixed with
      `padding: 0 32px`.
   3. ⚠️ **NTOA REHEARSAL, 2026-08-29 — IT TOUCHED THE HEADER.** The other axis
      of the same defect, and the one this deletion answers: `margin: 0 0 18px`
      is a bottom gap with no top one, so the row sat flush under
      `.rd-pro-top`'s hairline. A top margin here would have been a fourth
      invented number on a row the desktop corpus does not draw at all.

   **32 was drawn, not chosen, and the citation survives** because
   `.rd-pro__main .rd-prom__back` borrows the same number: `rd-pro-kit.jsx:224`
   authors `ProTop` as `padding: '10px 32px 16px'` — the value #1093 (rail 208 /
   rows 56 / edge 3px / head 76) landed into `.rd-pro-top` — and every Pro page
   pane in the corpus repeats it: `rd-pro-kit.jsx:300` (`ProTabs`,
   `'18px 32px 0'`), `rd-pro-library.jsx:110/159/214`, `rd-pro-events.jsx:212/497`,
   `rd-pro-shell.jsx:25`, `rd-pro-rangeday.jsx:216/226/261`. `ProShell`'s content
   column carries no pad of its own (`rd-pro-kit.jsx:328`, `flex: 1, minWidth: 0`),
   so the 32 is the pane's to supply and the rail contributes only its 208 + 1px
   border. Drawn content edge: 208 + 1 + 32 = 241 from the viewport left.

   ⚠️ AND THE ROW ITSELF IS STILL A DEVIATION AT CONSOLE WIDTH, unchanged by
   this. No desktop or tablet Pro screen in the corpus draws a back row at all —
   `rd-tablet-kit.jsx:104-105` states why (*"the rail IS the back button"*), and
   `rd-pro-kit.jsx` exports no back component. It survives as James's
   2026-08-18 red-line, and it survives as a CALLER of the console's one row
   rather than as a bespoke one. (The one drawn Pro back row is PHONE —
   `rd-pro-event-detail.jsx:27-32`, gutter 18 via `TShell pad={18}` — which is
   why the fixture's phone geometry needs no console number and its console
   gutter is read off the head.)

   Pinned by `drill_form.rs::tablet_layout_tests::ruling_the_page_declares_no_back_row_of_its_own`. */

/* RdBanner owns its padding AND its bottom gap (`p1.css`, THE SLOT RULING
   2026-08-28), so the caller no longer spaces it. Kept at `0` rather than
   deleted, for the reason `adm2.css` gives beside the same change. */
.rd-drillf__banner { margin: 0; }

/* ── The field helper ──────────────────────────────────────────────────────
   For the twelve controls no kit primitive serves: 1 textarea (AU5 §3.3
   excludes multiline), 4 selects, 6 numbers (RdNumericField binds no input —
   §8.2), 1 checkbox. Text inputs use RdTextField and are NOT wrapped here;
   that primitive carries its own defence and two owners would be two cleanups. */
/* ⚠️ NO `margin-bottom` HERE — the container owns the gap, and it must, because
   the container is now a GRID.

   PR A gave this a `margin-bottom: 16px` while the form was still a block box
   under Tailwind's `space-y-4` (a `margin-top` on every child but the first).
   Adjacent siblings' bottom and top margins COLLAPSE in a block formatting
   context, so 16 and 16 rendered as 16 and the duplication was invisible.
   **Margins do not collapse between grid items.** Left in place under
   `.rd-drillf`'s `gap: 20px`, every field would have sat 36px from the next —
   a defect introduced by a container change in a rule that was not edited, and
   visible in no diff of either file. */
.rd-drillf__field { margin: 0; }

.rd-drillf__label {
  display: block;
  margin-bottom: 6px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .16em;
  font-size: 10px; color: var(--rd-fg3);
}

/* ⚠️ THE SPECIFICITY DEFENCE, AND IT LIVES HERE ONCE.

   The pre-kit backfill styles bare inputs BY ATTRIBUTE — style.css:98 and its
   second copy at :1475 —
     input[type="text"], input[type="email"], input[type="password"] { border: 1px solid #d1d5db; }
     input:focus { box-shadow: 0 0 0 2px #3b82f6; border-color: #3b82f6; }
   Those are (0,0,1,1). A single class is (0,0,1,0), so THEY WIN: undefended,
   every control here draws a raw-hex grey border and, on focus, a blue ring —
   the one hue the kit refuses to name (blue has zero call sites, the same
   measurement that keeps Info out of RdBanner, P1 §1.4).

   The compound below is (0,0,2,0) and outranks both. It edits nothing in the
   backfill block, which is not this lane's to touch.

   REMOVAL TRIGGER: when the pre-kit pages finish migrating and the backfill
   rules go. #617 records the kit owner's ruling AGAINST retokenising them —
   they still serve fixed-white pre-kit pages, so no single token is correct on
   both grounds. This defence and RdTextField's each delete on their own
   signal. */
/* ⚠️ TWO ANCHORS, AND THE SECOND ONE IS NOT COSMETIC.
   The defence works by being (0,2,0), so it needs a REAL ancestor class in the
   selector — which means every context a control appears in has to be listed.
   `.rd-drillf__item` is the repeatable row, added with steps and targets; its
   inputs are not inside a `__field`, so under the one-anchor version they fell
   straight back to the pre-kit attribute rule and drew a `#d1d5db` border and a
   blue focus ring while every field above them looked correct. Anyone adding a
   third context for `.rd-drillf__control` must add it here too — that is the
   cost of winning on specificity rather than on source order, and it is still
   cheaper than the alternative. */
.rd-drillf__field .rd-drillf__control,
.rd-drillf__item .rd-drillf__control,
.rd-drillf__zone-row .rd-drillf__control {
  width: 100%;
  box-sizing: border-box;
  padding: 11px 12px;
  border: 1px solid var(--rd-ctrl);
  border-radius: 0;
  background: var(--rd-field);
  box-shadow: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 14px;
  color: var(--rd-fg);
  /* The four selects would otherwise draw the OS control's own chrome inside a
     box the design already draws. Declared here rather than in a `--select`
     modifier: the drawing draws ONE box, and a modifier that only ever removes
     native paint is a name for nothing. Harmless on the textarea and numbers. */
  appearance: none;
  -webkit-appearance: none;
}
.rd-drillf__field .rd-drillf__control:focus,
.rd-drillf__item .rd-drillf__control:focus,
.rd-drillf__zone-row .rd-drillf__control:focus {
  outline: none;
  border-color: var(--rd-acc);
  box-shadow: none;
}
/* The Dry Fire rule's two locked fields — Scoring Profile and Category, both
   disabled with their one legal value showing.

   ⚠️ FIRST DISABLED FORM CONTROL IN THE APP, AND THE DESIGN DRAWS NONE.
   `01 Foundations.html` §06 specifies no disabled state, and three separate
   docs rule AGAINST greying a control (`35 (Desktop) Pro Org Library.html:41`
   and `:62`, `02 (Mobile) Auth.html:157` — "removed, not disabled"). The
   product instruction here is explicitly a disabled control with the value
   auto-selected, so the treatment is borrowed rather than invented: the dimmest
   foreground token plus `cursor: default`, which is what this file's own
   disabled BUTTONS already use (`.rd-drillm__del:disabled`, and the
   opacity/cursor pair on `__reorder`/`__btn`). Flagged in the PR for James.

   The three anchors are not optional — see the note above the base rule. */
.rd-drillf__field .rd-drillf__control:disabled,
.rd-drillf__item .rd-drillf__control:disabled,
.rd-drillf__zone-row .rd-drillf__control:disabled {
  background: var(--rd-surf2);
  color: var(--rd-fg3);
  border-color: var(--rd-line);
  cursor: default;
}

/* ── The form surface ──────────────────────────────────────────────────────
   Replaces `bg-white rounded-lg shadow p-6` — a white card with a drop shadow,
   which is the pre-kit idiom and wrong twice over here: it is a fixed white in
   a themed console, and the Pro surface carries no shadow anywhere in the
   corpus. Matches `.rd-ev-form`, the one other Pro form on master, rather than
   inventing a second answer: same padding, same measure. */
/* `space-y-4` becomes the container's own grid gap. The fields are a list, so
   the spacing belongs to the container rather than to a margin on each child
   that the last child then has to cancel — which is also what `space-y-*` was
   doing, in a selector that had to exclude its first child to do it. */
.rd-drillf {
  display: grid;
  gap: 20px;
  padding: 26px 32px;
  max-width: 720px;
}

/* The 2-up and 3-up rows. Wraps rather than crushing, on the event form's
   measured reasoning: below ~560px three fields would each be narrower than the
   number spinner the browser puts inside them. `grid-cols-3` could not wrap. */
.rd-drillf__row { display: flex; flex-wrap: wrap; gap: 16px; }
.rd-drillf__row .rd-drillf__field { flex: 1 1 180px; min-width: 0; }

/* A field's shape while the drill is still on the wire — the label mark over
   the control mark, at `.rd-drillf__label`'s own 6px separation, so the cold
   form has the geometry the warm one lands in rather than a stack of touching
   blocks. It carries no ground and no border of its own: the kit's skeleton
   ink is the only thing that should be visible here.

   The `__row` rule is `.rd-drillf__field`'s, repeated rather than joined to
   it, because these two selectors mean different things (a real field vs a
   field's placeholder) and a shared rule would hide that from the next reader
   deleting one of them. */
.rd-drillf__skel-field { display: grid; gap: 6px; }
.rd-drillf__row .rd-drillf__skel-field { flex: 1 1 180px; min-width: 0; }

/* ⚠️ The validation list KEEPS ITS `<ul>` — it does not become an `RdBanner`.
   P1 §10.2 and the Q5 ruling: `message: String` is FINAL, and plural content
   belongs to a separate validation-summary primitive (#638).

   ⚠️ THAT PRIMITIVE NOW EXISTS — `RdErrorList`. Its wiring (a role="alert"
   wrapper around the list, which the `<ul>` itself cannot carry without
   ceasing to be a list) landed first and left the values behind on purpose,
   because collapsing two lane-owned blocks was a separate diff from the
   wiring one. That diff has now landed too: `.rd-drillf__errors` and
   `admin/event_form.rs`'s `.rd-ev-form__errors` were the same block twice,
   and both moved to the kit's own `.rd-error-list` (`styles/kit-error-list.css`).
   Nothing here declares those values any more — see that file. */

/* The one checkbox. A real `<input type="checkbox">`, deliberately: the drawing
   has no checkbox to copy, and a div-with-role reimplements space-to-toggle,
   the focus ring and the indeterminate state for no drawn gain. `accent-color`
   recolours the native control's fill, which is the whole change. */
.rd-drillf__check {
  display: flex;
  align-items: center;
  gap: 9px;
}
.rd-drillf__check input {
  width: 15px; height: 15px;
  margin: 0;
  accent-color: var(--rd-volt);
  flex: none;
}
.rd-drillf__check label {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg);
}

/* ── The repeatables: steps and targets ────────────────────────────────────
   ⚠️ THE DRAWING AND THIS PAGE DISAGREE ABOUT STRUCTURE, AND THE PAGE WINS.
   `rd-authoring-create.jsx` draws steps as READ-ONLY summary cards (`AStep`,
   :28) with editing in a bottom sheet (:118-131) — a phone wizard's shape. This
   is one desktop page with inline editable rows, and 04 Authoring is evidence,
   not authority: the form's behaviour is kept. What transfers is the row's
   VISUAL vocabulary, which the drawing does state — a bordered box, `padding:
   '10px 12px'`, `gap: 10`, the index in small mono `t.fg3` (:32-33). */
.rd-drillf__repeat-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 9px;
}

/* `+ Add step` was `text-blue-600` — the one hue the kit refuses to name (zero
   call sites in the whole corpus, the same measurement that keeps `Info` out of
   `RdBanner`). It is an accent action, so it takes the accent-as-text token. */
.rd-drillf__add {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-acc);
}
.rd-drillf__add:hover { text-decoration: underline; }

/* The drawn row box (:31) — square, because substrate §1.5 bans the radius the
   phone frames use, exactly as every other control on this page. */
.rd-drillf__item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  margin-bottom: 6px;
  border: 1px solid var(--rd-line);
  background: var(--rd-surf);
}

/* `...mono, fontSize: 10, color: t.fg3, paddingTop: 3` (:32). Tabular figures
   so a two-digit step does not shove the row's contents sideways. */
.rd-drillf__idx {
  flex: none;
  padding-top: 9px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10px;
  color: var(--rd-fg3);
  font-variant-numeric: tabular-nums;
}

.rd-drillf__item .rd-drillf__control { flex: 1 1 140px; min-width: 0; }
.rd-drillf__item .rd-drillf__control--num { flex: 0 0 84px; }

/* The reorder pair. Undrawn — the wizard has no reordering at all — and kept
   because it is live behaviour a re-skin may not remove. Held to the row's own
   idiom rather than given chrome of its own. */
.rd-drillf__reorder { display: flex; flex-direction: column; flex: none; }
.rd-drillf__reorder button {
  background: none;
  border: 0;
  padding: 0 2px;
  cursor: pointer;
  line-height: 1;
  font-size: 10px;
  color: var(--rd-fg3);
}
.rd-drillf__reorder button:hover:not(:disabled) { color: var(--rd-fg); }
.rd-drillf__reorder button:disabled { opacity: .3; cursor: default; }

.rd-drillf__remove {
  flex: none;
  align-self: center;
  background: none;
  border: 0;
  padding: 2px 4px;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-danger);
}
.rd-drillf__remove:hover { text-decoration: underline; }

/* The criterion row's terminating-fault box (WP5, the driving arm).

   ⚠️ SCOPED TO `__item`, so it changes nothing about the standalone `Active`
   checkbox this form has always drawn. Inside a repeat row the check is a
   fixed-width member beside a flexing label input, and without `flex: none` it
   would compete with that input for the row's width — the same reason
   `.rd-drillf__control--num` exists two rules up. Nothing about the check's own
   treatment is restated: it stays `.rd-drillf__check`'s. */
.rd-drillf__item .rd-drillf__check { flex: none; }

/* ── Zone overrides ────────────────────────────────────────────────────────
   The zone table IS drawn (rd-authoring-create.jsx:167-179, the `adv` branch):
   a per-zone flex row, `gap: 10`, the zone in small display type at `t.fg3`, the
   name in body at `t.fg2`, and a right-aligned MONO value box of fixed width.
   The drawing's own note says why it exists at all — "Edited in one place, and
   only here. The old sheet asked for these twice and never said which copy
   counted."

   ⚠️ THE DRAWING HAS THREE COLUMNS AND THIS TABLE HAS FOUR, DELIBERATELY. The
   drawn row edits the standard scoring directly; this one edits an OVERRIDE, so
   it also shows the default being overridden and whether the zone is a penalty.
   Those two columns are live information, and dropping them to match a drawing
   that is not solving the same problem would be a capability loss. Evidence, not
   authority — again. What transfers is the row's vocabulary, not its arity. */
.rd-drillf__zones { border: 1px solid var(--rd-line); }

.rd-drillf__zones-head,
.rd-drillf__zone-row {
  display: grid;
  grid-template-columns: 1fr 5.5rem 4.5rem 6.5rem;
  gap: 10px;
  align-items: center;
  padding: 8px 12px;
}

/* The header strip was `bg-gray-50` — a fixed light grey that is invisible
   against the dark theme's own surface. `--rd-surf2` is the token for exactly
   this: a second surface stepped off the first. */
.rd-drillf__zones-head {
  background: var(--rd-surf2);
  border-bottom: 1px solid var(--rd-line);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .11em;
  font-size: 9px; color: var(--rd-fg3);
}

.rd-drillf__zone-row + .rd-drillf__zone-row { border-top: 1px solid var(--rd-line); }
.rd-drillf__zone-row {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px; color: var(--rd-fg);
}

/* `...mono, fontSize: 12.5` and right-aligned in the drawing (:176). Tabular
   figures because these are a column of numbers to be compared down, not read
   across — a proportional 1 makes 11 and 15 different widths. */
.rd-drillf__zone-num {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-variant-numeric: tabular-nums;
  font-size: 12.5px;
  color: var(--rd-fg3);
}
.rd-drillf__zone-row .rd-drillf__control {
  padding: 6px 9px;
  text-align: right;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-variant-numeric: tabular-nums;
  font-size: 12.5px;
}

/* Instructional copy under a control. `--rd-fg3` is the meta step: this is a
   note ABOUT the field, not a value in it. */
.rd-drillf__note {
  margin: 0 0 9px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
  line-height: 1.45;
}

/* The dropped-override warning. Was raw amber hex on a fixed wash; both are
   tokens, and both themes define them. NOT `--rd-danger`: nothing has failed
   and nothing is lost on save — the profile changed and some overrides no
   longer apply, which is a caution about a consequence, not an error. */
.rd-drillf__warn {
  margin: 0 0 9px;
  padding: 7px 11px;
  border: 1px solid var(--rd-warn);
  background: var(--rd-warn-wash);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-warn);
  line-height: 1.45;
}

/* ── The foot row ──────────────────────────────────────────────────────────
   ⚠️ `.rd-drillf__actions` IS GONE, AND THE ROW IS NOT. It became
   `.rd-pro-acts` in `adm0-chrome.css` when James ruled (staging
   `/admin/drills/new`, 2026-08-27) that the primary act moves out of `ProTop`
   and down beside `Cancel`, where it scrolls with the fields. The geometry it
   carried survives verbatim in the shared rule — `space-between` so destructive
   sits away from ordinary, which was the one thing about it worth keeping.

   Deleted rather than left as an alias: this page is no longer the only form
   that ends this way, and a bespoke spelling of a shared row is how the next
   form comes to disagree with this one about where its save is.

   What stays HERE is the page's own quiet button, below — the row's members are
   still this page's, only the row is shared. */

/* The quiet button. NOT the chamfered `.rd-pro-top__btn`: measured on this
   branch, exactly one rule in the Pro family carries `clip-path`, and it is the
   primary action. The chamfer is this family's mark for "this is THE action",
   so spending it on a cancel would spend the signal that makes the save
   findable. Outlined, `--rd-ctrl`, no fill. */
.rd-drillf__btn {
  display: inline-block;
  padding: 9px 16px;
  border: 1px solid var(--rd-ctrl);
  background: none;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .06em;
  font-size: 11px;
  color: var(--rd-fg2);
  text-decoration: none;
}
.rd-drillf__btn:hover { color: var(--rd-fg); border-color: var(--rd-fg3); }
.rd-drillf__btn:disabled { opacity: .5; cursor: default; }

/* ── The two journeys below the foot row ───────────────────────────────────
   The stage diagram and the squad: a pair of links AWAY from the form to
   another screen about this same drill (`drill_form.rs` argues at both call
   sites why they are the same KIND of thing and why they are not form verbs).

   ⚠️ **THEY ARE ONE ROW NOW, AND THE ROW IS THE FORM'S OWN COLUMN** — James,
   NTOA rehearsal 2026-08-24, finding 3: on the Pro TABLET both rendered
   FULL-WIDTH. They wore the button's full-width `block` modifier
   (`display: block; width: 100%`), which is now DELETED rather than left
   unused — its own comment recorded the full width as "carried over rather
   than chosen … narrowing them would be a
   layout ruling this sweep has no standing to make". **That ruling has now been
   made, by the person who can make it**, so the carried-over value goes with it.

   ⚠️ **THE ROW IS UNDRAWN — flagged, not inferred.** A side-by-side pair of
   form actions appears NOWHERE in the corpus. `04 (Tablet) Authoring` draws its
   action row as ONE button plus a text Back (`rd-tablet-authoring.jsx:218-221`),
   `35 (Desktop) Pro Org Library` draws no form at all, and two docs argue
   AGAINST the shape in their own contexts (`32 (Desktop) Pro Events.html:57`,
   `14 (Mobile) People.html:189`) — both about a pair that is NOT a pair of
   equals (a forward act beside a backward one). These two are equals: two
   journeys, same weight, same destination class. So the shape is James's, and
   only the NUMBERS are borrowed.

   `padding: 0 32px` + `max-width: 720px` is not a third measure: it is exactly
   `.rd-drillf`'s box above it (`26px 32px` / `max-width: 720px`) and
   `.rd-drillm`'s below it (`22px 32px 26px` / `max-width: 720px`), so all three
   share one 656px content column and the row lines up with the field grid it
   sits under. 720 is the drawn Pro form width — `rd-shell-lab.jsx:147`
   (51 Shell Lab §02 Forms, `maxWidth: 720`), `rd-pro-events.jsx:321`,
   `rd-pro-locations.jsx:209`. 32 is the page gutter, cited in full in the
   retired-back-row note at the head of this part.

   `gap: 16px` is `.rd-drillf__row`'s own gap a few rules up, not a new value.

   ⚠️ **THE PHONE KEEPS THE STACK, AND NO MEDIA QUERY DOES IT.** `flex-wrap`
   with a basis does, on `.rd-drillf__row`'s measured reasoning ("wraps rather
   than crushing") — and it must be the wrap here, because `drill_form.rs` reads
   NO device class at all: one markup serves both surfaces, so a Rust breakpoint
   is not available to this page and a width query would be the second place the
   breakpoint is asked (the parts forbid that).

   The basis is **220**, and it is drawn: `rd-tablet-authoring.jsx:220` gives the
   authoring form's own action button `width: 220`. Used here as a WRAP
   THRESHOLD rather than a fixed width — the items grow past it. It lands on the
   right side of both surfaces by arithmetic, which is why that drawn number is
   usable and a rounder one would not be:
     * phone — 448px column (`device.rs`), 448 − 64 pad = 384 content;
       220 + 16 + 220 = 456 > 384, so the row wraps and each link is 100% of the
       line: the stack James kept, unchanged.
     * tablet — 1024 min viewport − 208 rail − 1 border = 815, capped by
       max-width to a 656 content column; 456 <= 656, so one row, each link 320. */
.rd-drillf__journeys {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  padding: 0 32px;
  max-width: 720px;
  margin-top: 4px;
}

/* GEOMETRY ONLY — the links keep `.rd-drillf__btn` whole. §5.5's invariant is
   why this is a descendant rule on the row rather than a second modifier on the
   button: restating the padding or the type here would let the two spellings of
   one control drift apart, which is exactly what `--block` was careful to avoid
   and what this rule inherits from it. */
.rd-drillf__journeys .rd-drillf__btn {
  flex: 1 1 220px;
  min-width: 0;
  text-align: center;
}

/* The vector stage diagram sits between the form and these two links, and it is
   the same page-root child they are: `drill_form.rs` wrapped it in Tailwind's
   `mb-4` and nothing else, so it TOO started at x=0 against the rail. It takes
   the page gutter and the form's measure for the same reason the row above
   does — leaving one lone element flush while its own "Draw a stage diagram"
   link is gutter-aligned would be a NEW misalignment introduced by fixing the
   other two. `margin-bottom` is `mb-4`'s own 1rem, carried across unchanged
   (`_legacy-base.css:60`); the spacing under the diagram is not what moved. */
.rd-drillf__diagram {
  margin-bottom: 16px;
  padding: 0 32px;
  max-width: 720px;
}

/* Danger takes the token as STROKE AND TEXT, never as a fill. Same construction
   as the status chip: one colour drives both, no filled block. A filled red
   button is a weight this page's single delete does not carry, and `--rd-danger`
   as a background has no `--rd-on-danger` to guarantee contrast against. */
.rd-drillf__btn--danger {
  border-color: var(--rd-danger);
  color: var(--rd-danger);
}
.rd-drillf__btn--danger:hover:not(:disabled) {
  color: var(--rd-danger);
  border-color: var(--rd-danger);
  background: var(--rd-danger-wash);
}

/* The delete TRIGGER is deliberately not a button shape. It opens a question,
   it does not destroy anything — the destructive control is the one that
   appears after it, and only that one wears the danger stroke. */
.rd-drillf__delete {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-danger);
}
.rd-drillf__delete:hover { text-decoration: underline; }

.rd-drillf__confirm { display: flex; align-items: center; gap: 10px; }
.rd-drillf__confirm-q {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-danger);
}

/* ── The distance-unit segmented control ───────────────────────────────────
   A page-local single-choice strip. NOT `ProChip`: the kit owner ruled that a
   chip is fill-when-selected only for INPUT and that ProChip never covers
   selectables — outlined-always is how this corpus distinguishes a report from
   a choice. Promotes to a primitive at the SECOND consumer; one consumer is a
   page-local control.

   FOUR AXES, and the fourth is why the test came first:
     selected  --rd-volt fill, --rd-on-volt text
     hover     stroke lifts, on the unselected only
     focus     a real focus ring, because these are keyboard targets
     UNSET     NOTHING is selected — a default is not a choice

   The unset axis is carried by `data-on`, which is written from the same
   predicate the test pins. A second copy of `current == wire` here would let
   the pin pass while the paint disagreed. */
.rd-drillf__seg {
  display: inline-flex;
  border: 1px solid var(--rd-ctrl);
}

.rd-drillf__seg-btn {
  appearance: none;
  background: none;
  border: 0;
  /* The divider between segments, and only BETWEEN — a border on every button
     would double up against the group's own frame. */
  border-left: 1px solid var(--rd-ctrl);
  padding: 8px 16px;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .06em;
  font-size: 11px;
  color: var(--rd-fg2);
}
.rd-drillf__seg-btn:first-child { border-left: 0; }

.rd-drillf__seg-btn:hover[data-on="0"] { color: var(--rd-fg); }

/* Square, per substrate §1.5 — the drawn `borderRadius` the phone frames use is
   banned here, and every other control on this page is already square. */
.rd-drillf__seg-btn[data-on="1"] {
  background: var(--rd-volt);
  color: var(--rd-on-volt);
}

/* The ring is drawn OUTSIDE, so it survives the filled face: an inset outline
   on the selected segment would sit on --rd-volt and vanish. */
.rd-drillf__seg-btn:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
  position: relative;
}

/* ── The image slots (`drill_media.rs`) ────────────────────────────────────
   UNDRAWN. The authoring wizard has no image management at all, so there is no
   frame to copy and nothing here is an interpretation of one: it is the drill
   form's own vocabulary applied to the one component that renders below it.
   Every value below already exists a few rules up, and the comments say which.

   It replaced `bg-white rounded-lg shadow p-6 mt-6` — the pre-kit card, wrong
   here for the reason `.rd-drillf` records: a fixed white in a themed console,
   with a drop shadow the Pro surface carries nowhere in the corpus. This
   component renders INSIDE `drill_form.rs`'s own `.rd-pro-page`, so it was
   never a page in its own right and never had a light ground to be left in.

   ⚠️ A HAIRLINE, NOT A CARD. `.rd-drillf` above it is a padded column on the
   page ground with no border of its own, and a bordered panel below a
   borderless form would read as a different KIND of thing rather than as the
   next part of the same page. The rule is what the card's edge was actually
   doing — separating two sections — and it costs one declaration instead of
   four. Same padding and same measure as the form, so the two columns line up. */
.rd-drillm {
  display: grid;
  gap: 14px;
  padding: 22px 32px 26px;
  max-width: 720px;
  border-top: 1px solid var(--rd-line);
}

/* The head and its note are ONE unit, hence the wrapper: at the container's
   14px they would read as two unrelated lines. Same reasoning as `.rd-drillf`'s
   gap owning the field spacing — the container spaces sections, not sentences. */
.rd-drillm__intro { margin: 0; }

.rd-drillm__head {
  margin: 0 0 5px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 75%; letter-spacing: .02em;
  font-size: 13px;
  color: var(--rd-fg);
}

/* `.rd-drillf__note`'s values, and deliberately not that class: this note has no
   control under it, so it owns no bottom margin — the grid gap does. */
.rd-drillm__note {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
  line-height: 1.45;
}

/* The ground the slots will stand on, while the fetch decides what stands
   there. The kit's 26px fallback sweep sizes itself and nothing else, so the
   centring is the caller's — this is the whole rule: put the mark in the middle
   of the space the slots are about to fill.

   The height is `.rd-drillm__slot`'s own box (a 128px preview plus its 12px
   padding, its label line and its gap), so the section does not collapse and
   then jump when the images land. */
.rd-drillm__wait {
  min-height: 196px;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* `grid-cols-1 sm:grid-cols-3` becomes a wrap, on `.rd-drillf__row`'s measured
   reasoning: a fixed three-column grid cannot wrap, and these cells hold a
   128px preview that has a floor below which it is not a preview. */
.rd-drillm__slots { display: flex; flex-wrap: wrap; gap: 16px; }

/* The slot box — `.rd-drillf__item`'s construction, stood upright. Square,
   because substrate §1.5 bans the radius the phone frames use. */
.rd-drillm__slot {
  flex: 1 1 180px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  border: 1px solid var(--rd-line);
  background: var(--rd-surf);
}

/* `.rd-drillf__label`'s type role WITHOUT its `margin-bottom`, for the reason
   that rule's own comment gives about `.rd-drillf__field`: the margin was
   correct under a block box and is a second gap inside a flex column. */
.rd-drillm__lab {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .16em;
  font-size: 10px;
  color: var(--rd-fg3);
}

/* `object-fit: contain` is load-bearing and carried over unchanged: a stage
   diagram cropped to fill its box is a diagram with its edges cut off.
   `--rd-surf2` is the recessed ground a transparent PNG shows through onto. */
.rd-drillm__img {
  width: 100%;
  height: 128px;
  object-fit: contain;
  background: var(--rd-surf2);
}

/* The empty slot keeps the filled one's exact height, so filling a slot does not
   make the row jump. */
.rd-drillm__empty {
  height: 128px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--rd-surf2);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* `.rd-drillf__remove`'s treatment — danger as TEXT, never as a fill — pinned to
   the start of the column rather than centred, which is where a stacked control
   reads from. */
.rd-drillm__del {
  align-self: flex-start;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-danger);
}
.rd-drillm__del:hover:not(:disabled) { text-decoration: underline; }
.rd-drillm__del:disabled { color: var(--rd-fg3); cursor: default; }

/* Upload was `bg-blue-600 text-white` — a filled block in the one hue the kit
   refuses to name (zero call sites in the corpus, the same measurement that
   keeps `Info` out of `RdBanner`). It is the same KIND of act as `+ Add step`
   one section up, so it takes that rule's answer: the accent as TEXT. */
.rd-drillm__up {
  align-self: flex-start;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-acc);

  /* The containing block for the input below, which is absolutely positioned.
     Without this the 1px input escapes to the nearest positioned ancestor and
     the focus ring rings a box the control is not inside. */
  position: relative;
}
.rd-drillm__up:hover { text-decoration: underline; }

/* ⚠️ THE RING IS ON THE LABEL, because the control the keyboard actually lands
   on is invisible. Ruled 2026-08-14: *"Visually hide it while leaving it
   focusable, keep the label as its visible surface, and put the focus ring on
   the label via :focus-within. The ring has to be visible."*

   `:focus-within` rather than `:focus-visible`, per the ruling and because
   `:focus-visible` on the LABEL would never match — a label is not focusable,
   so the pseudo-class has nothing to be true about. The consequence is stated
   rather than hidden: `:focus-within` has no pointer heuristic, so a mouse
   click on this label paints the ring too. That is the ruling's own trade —
   a ring that is sometimes redundant beats one a keyboard cannot see.

   The outline is `.rd-drillf__seg-btn:focus-visible`'s exactly, which is this
   page's ring. Safe as an `outline` here for `kit-shell.css`'s stated reason:
   this element carries no `clip-path`, and a clip would eat the stroke. */
.rd-drillm__up:focus-within {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* The real `<input type="file">`, kept rather than reimplemented — the same
   reasoning that keeps the form's one checkbox native. Its label is the visible
   control, and clicking a label opens the picker for the input it wraps.

   ⚠️ IT WAS `display: none`, WHICH IS THE ONE WAY TO HIDE IT THAT BREAKS IT.
   `display:none` (and `visibility:hidden`) take the input out of the tab order,
   so the only control here was mouse-only: a keyboard could reach the Remove
   button on a filled slot and nothing at all on an empty one.

   This is `.rd-swatch__input`'s idiom, verbatim, which is the pool's existing
   answer to the same problem and carries its own note — *"Visually hidden but
   STILL FOCUSABLE and still hit-testable by the label."* `opacity: 0` rather
   than a `clip-path`/`clip` recipe because that is the one already proven in
   this corpus, and a 1px transparent box needs no second concealment.
   `pointer-events: none` leaves the hit testing to the label, so a click lands
   in exactly one place. The ring it earns is the rule above. */
.rd-drillm__file {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  opacity: 0;
  pointer-events: none;
}
/* ==== ADM1-4 · docs/design/kit/ADM1-events.md §4 · owner: spine ==== */
/* 32 Pro Events, `ScreenNewEvent` (and its leading note).
   The form's own field chrome, page-local and deliberately NOT `RdTextField`.

   ⚠️ The kit's text field is drawn for Auth and every metric differs: box
   padding 15/16 vs 11/13, value 16px vs 13.5px, label 10px/.16em vs 9px/.11em
   — and it is CHAMFERED. Measured on this branch: 12 rules in this file declare
   `clip-path`, and exactly ONE of them is in the Pro family — `.rd-pro-top__btn`,
   the primary action. No Pro or event FIELD, box, row or container carries it.
   So the chamfer is this family's mark for "this is THE action", and putting it
   on input chrome would spend that signal on every text box on the screen.
   `dense` does not close the gap either; it answers "is this screen crowded?",
   not "which drawing is this".

   Promotes to an `RdProField` primitive the moment a SECOND Pro form needs it —
   a primitive with one consumer is premature, and an `adm1` boolean on a
   primitive whose size axis answers a different question is not the shape. */

/* ⚠️ **CORRECTED 2026-08-25.** This read: "The drawing's `{ flex: 1,
   minHeight: 0, position: 'relative' }` body (32 Pro Events, ScreenNewEvent)
   — the positioned container the form and the location sheet's scrim share,
   so the scrim covers the body and NOT the rail or the page head. Same three
   properties `.rd-ev-roster` carries for `.rd-ev-pick`, and for the same
   reason."

   Export 27 dropped `position: 'relative'` from this body in the drawing —
   it now lives on `ProShell` itself (`rd-pro-kit.jsx:326`) — and
   `.rd-ev-loc`'s scrim is `position: fixed` now (below in this file), so it
   no longer resolves against this element at all; the whole-surface contract
   is stated in words at `rd-pro-library.jsx:225-226`. `position: relative`
   stays on `.rd-ev-form-area` below as the reversal path, costing nothing.
   `overflow: auto` rather than the drawing's `hidden`: the frame has a fixed
   height and a real form does not, and hidden here would clip a long form
   instead of scrolling it. */
.rd-ev-form-area {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  position: relative;
}

.rd-ev-form {
  padding: 26px 32px;
  max-width: 720px;
}

/* `.rd-ev-form__loading` is GONE (flicker phase 4). It dressed a bare
   sentence that replaced the entire page; the waiting state now paints the head
   and puts the kit's treatment inside the same `.rd-ev-form-area` >
   `.rd-ev-form` body the loaded form uses, so it lands exactly where the form
   will. No caller left.

   And the treatment is no longer a sentence at all (43 Loading States.html:40):
   the body draws this form's own field geometry in skeleton ink. */

/* A field's shape while the event is on the wire. It is `.rd-ev-form__field`'s
   column plus only the separation the warm pair gets from
   `.rd-ev-form__label`'s `margin-bottom` — which the skeleton marks do not
   carry, being kit blocks rather than this screen's type. Nothing else: the
   skeleton ink is the only thing that should be visible here. */
.rd-ev-form__field--skel { display: grid; gap: 8px; }

/* `display: grid, gap: 18` (32 Pro Events, ScreenNewEvent). */
.rd-ev-form__grid { display: grid; gap: 18px; }

/* The drawn `gap: 16` row. Wraps rather than crushing: two 13.5px fields below
   ~380px would each be narrower than the date control the browser puts inside
   them. */
.rd-ev-form__row {
  display: flex; flex-wrap: wrap; gap: 16px;
}
.rd-ev-form__row .rd-ev-form__field { flex: 1 1 180px; min-width: 0; }

/* 9px display 700/78%/.11em in `--rd-fg3`, `marginBottom: 8`. Applied to the
   Type group's `div` as well as to real `label`s — the segmented control has
   two buttons and no single element for a `for=` to point at, so it names the
   group through `aria-labelledby` instead and this rule has to be element-
   agnostic. */
.rd-ev-form__label {
  display: block; margin-bottom: 8px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .11em;
  font-size: 9px; color: var(--rd-fg3);
}

/* `--rd-field` ground, 1px `--rd-ctrl`, `padding: '11px 13px'`, body 13.5px in
   `--rd-fg` (32 Pro Events, ScreenNewEvent's `box`). One rule for input, select
   and the description textarea, because the drawing draws one box. */
/* ⚠️ **Two classes, not one, and that is load-bearing.** The pre-kit Tailwind
   backfill styles bare inputs BY ATTRIBUTE —
   `input[type="text"], … , textarea, select { font-size: 1rem; border: 1px solid #d1d5db }`
   (style.css:98) — which is specificity (0,1,1) and beats a single class. A
   one-class rule left `input[type=text]` at 16px with a hardcoded `#d1d5db`
   border while every sibling field took 13.5px and `--rd-ctrl`: the Name and
   Where boxes silently opted out of the design system, in the default dark
   theme, and BOTH theme screenshots looked fine. Only sampling computed styles
   found it. `.rd-ev-form .rd-ev-form__box` is (0,2,0) and wins on specificity
   rather than on source order, which a later single-class rule would not. */
/* ⚠️ `.rd-ev-loc` is listed beside `.rd-ev-form` on every rule below, and it has
   to be: the sheet is a SIBLING of the form body (see `.rd-ev-form-area`), not a
   descendant, so a `.rd-ev-form …` selector does not reach it — and a
   single-class fallback would lose to the legacy attribute rules the comment
   above describes. Adding the second ancestor keeps one set of declarations for
   one drawn box, which is the point of the note. */
.rd-ev-form .rd-ev-form__box,
.rd-ev-loc .rd-ev-form__box {
  display: block; width: 100%; box-sizing: border-box;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  border-radius: 0;
  padding: 11px 13px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 400; font-size: 13.5px; color: var(--rd-fg);
  outline: none;
  appearance: none; -webkit-appearance: none;
}
.rd-ev-form .rd-ev-form__box:focus,
.rd-ev-loc .rd-ev-form__box:focus { border-color: var(--rd-acc); }
.rd-ev-form .rd-ev-form__box::placeholder,
.rd-ev-loc .rd-ev-form__box::placeholder { color: var(--rd-fg3); opacity: 1; }

/* The empty-state way in: the drawn `first` pick box — `t.surf2` ground,
   `t.line` border, `t.fg3` text — carrying the drawn caret on the right. It is a
   BUTTON rather than a disabled select, and `event_form.rs` states why at
   length: a disabled control would take the only route to the sheet with it. So
   it reads as the drawing's dimmed state and still presses. */
.rd-ev-form .rd-ev-form__loc-add {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  text-align: left;
  background: var(--rd-surf2);
  border-color: var(--rd-line);
  color: var(--rd-fg3);
  cursor: pointer;
}
.rd-ev-form .rd-ev-form__loc-add:focus-visible {
  outline: 2px solid var(--rd-acc); outline-offset: 2px;
}
.rd-ev-form__loc-caret { flex: none; font-size: 11px; color: var(--rd-fg3); }

/* The description's drawn `minHeight`, now on a real `textarea` — the wire
   grew `events.description` and the stub that used to wear this class is gone
   (`0061_event_description`).

   ⚠️ `resize: vertical`, not the browser default `both`. A textarea inside a
   fixed-width column that the operator can drag WIDER overflows the form body,
   and this form has a fixed height with nothing to scroll — the same constraint
   the Fee row's comment in `event_form.rs` records. Taller is free; wider is
   not. */
.rd-ev-form .rd-ev-form__box--area { min-height: 92px; resize: vertical; }

/* ── Type: the segmented control ─────────────────────────────────────────────
   Drawn 13 Aug (32 Pro Events, ScreenNewEvent), replacing a select: two cases
   and a required value, so there is nothing for a closed state to hide.

   The drawing's `box` with `flex: 1`, `textAlign: 'center'` and `fontSize:
   12.5` — half a point down from the 13.5px every other box carries, which is
   the drawing's own distinction between a value someone typed and a label
   someone picked. Selected takes the `t.fg` border and the `t.surf2` ground;
   unselected keeps `t.ctrl` and drops its text to `t.fg3`.

   ⚠️ Two classes for the same reason `.rd-ev-form__box` needs them, plus one
   more: `_legacy-base.css` styles bare `button` (`border: none; font-size:
   1rem`), so a segment must out-specify an element rule AND re-declare the
   border the reset removed. The `--on` modifier is (0,2,0) like the base and
   wins on source order, which is why it sits directly below it. */
.rd-ev-form__seg { display: flex; gap: 8px; }
.rd-ev-form .rd-ev-form__seg-opt,
.rd-ev-loc .rd-ev-form__seg-opt {
  flex: 1 1 0; min-width: 0; box-sizing: border-box;
  text-align: center;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  border-radius: 0;
  padding: 11px 13px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 400; font-size: 12.5px; color: var(--rd-fg3);
  cursor: pointer;
  appearance: none; -webkit-appearance: none;
}
.rd-ev-form .rd-ev-form__seg-opt--on,
.rd-ev-loc .rd-ev-form__seg-opt--on {
  border-color: var(--rd-fg);
  background: var(--rd-surf2);
  color: var(--rd-fg);
}
/* The drawing is static and has no focus state to copy. These are real buttons,
   so one is required: `:focus-visible` keeps it off a pointer click and on the
   keyboard, where colour alone would leave a tabbing operator with no idea
   which segment is under the cursor. */
.rd-ev-form .rd-ev-form__seg-opt:focus-visible,
.rd-ev-loc .rd-ev-form__seg-opt:focus-visible {
  outline: 2px solid var(--rd-acc); outline-offset: 2px;
}

/* The hint under a field — 10.5px in `--rd-fg3`, `marginTop: 6`,
   `lineHeight: 1.45` (32 Pro Events, ScreenNewEvent's `hint`).

   `__note` used to sit below this rule at 11.5px, carrying our own undrawn
   sentence under the description stub. The stub is gone — the wire carries the
   field — and it was that class's only site, so the rule went with it rather
   than staying as a face nothing wears. */
.rd-ev-form__hint {
  margin: 6px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px; color: var(--rd-fg3);
  line-height: 1.45;
}


/* Pinned foot copy: 11.5px, `lineHeight: 1.55`, `maxWidth: '62ch'`. */
.rd-ev-form__foot {
  margin-top: 22px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px; color: var(--rd-fg3);
  line-height: 1.55; max-width: 62ch;
}

.rd-ev-form__error {
  margin-bottom: 20px;
  border: 1px solid var(--rd-danger);
  padding: 11px 13px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px; color: var(--rd-danger);
}

/* Drill / course assignment — undrawn, and kept because it is live behaviour.
   Held to the screen's idiom rather than given chrome of its own. */
.rd-ev-form__assign {
  margin-top: 24px; padding-top: 20px;
  border-top: 1px solid var(--rd-line);
}
.rd-ev-form__assign-head {
  margin: 0 0 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .11em;
  font-size: 9px; color: var(--rd-fg3);
}
.rd-ev-form__assign-none {
  margin: 0 0 12px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px; color: var(--rd-fg3);
}
.rd-ev-form__assign-list { margin-bottom: 12px; }
.rd-ev-form__assign-row {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 11px; margin-bottom: 4px;
  background: var(--rd-field);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px; color: var(--rd-fg);
}
.rd-ev-form__assign-pos {
  flex: none; width: 22px; text-align: right;
  font-variant-numeric: tabular-nums; color: var(--rd-fg3);
}
.rd-ev-form__assign-name { flex: 1; min-width: 0; }
.rd-ev-form__assign-kind { font-size: 11px; color: var(--rd-fg3); }
.rd-ev-form__assign-move,
.rd-ev-form__assign-drop {
  flex: none; background: none; border: 0; cursor: pointer;
  font-size: 11px; color: var(--rd-fg3); padding: 2px 4px;
}
.rd-ev-form__assign-move:disabled { opacity: .3; cursor: default; }
.rd-ev-form__assign-move:hover:not(:disabled) { color: var(--rd-fg); }
.rd-ev-form__assign-drop { color: var(--rd-danger); }

/* ── The create-location sheet ───────────────────────────────────────────────
   32 Pro Events, ScreenNewEvent, the `adding` overlay. Drawn as a surface over
   the form rather than fields under the picker: nine fields inline would push
   the drills block and the visibility copy off a form body with nothing to
   scroll, and making a location is making a record.

   The drawn scrim is `rgba(6,6,6,.82)` and the panel is 600px against the
   roster picker's 520 — this one holds a nine-field form, that one holds a
   list.

   ⚠️ **`fixed`, NOT `absolute` — 2026-08-25.** This read "absolute inside
   `.rd-ev-form-area`, so the rail and the page head stay lit, exactly as
   `.rd-ev-pick` does on the roster tab", and it still tracks `.rd-ev-pick`
   exactly — both moved on the same day, for the same reason. `.rd-ev-form-area`
   is `flex: 1; min-height: 0; overflow: auto`, so under the fixed-rail scroll
   model an `inset: 0` scrim sized itself to that scroller and centred the panel
   on it instead of on the viewport. The mechanism, the measurement and the
   deviation from the drawn scrim scope are written down once, at `.rd-confirm`
   in adm1-5-1.css.

   `.rd-ev-loc__panel`'s `max-width: 100%` / `max-height: 100%` now resolve
   against the scrim's content box — the viewport less this rule's 20px padding —
   which is what they already meant against the form area. */
.rd-ev-loc {
  position: fixed; inset: 0; z-index: 40;
  background: rgba(6, 6, 6, .82);
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
}

.rd-ev-loc__panel {
  width: 600px;
  max-width: 100%;
  max-height: 100%;
  overflow-y: auto;
  background: var(--rd-surf);
  border: 1px solid var(--rd-line);
}

/* `padding: '18px 22px 14px'`, hairline under. */
.rd-ev-loc__head { padding: 18px 22px 14px; border-bottom: 1px solid var(--rd-line); }

.rd-ev-loc__title {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 75%; letter-spacing: .02em;
  font-size: 17px;
  color: var(--rd-fg);
}

.rd-ev-loc__sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
  margin-top: 7px;
}

/* `padding: '18px 22px 20px'`, `display: grid`, `gap: 14`. */
.rd-ev-loc__body { padding: 18px 22px 20px; display: grid; gap: 14px; }

/* The `— required` tail on the Name label: the drawing colours it `t.fg3`,
   which is the label's own colour, so this only stops a future label change
   dragging the qualifier with it. */
.rd-ev-loc__req { color: var(--rd-fg3); }

/* The two address lines: `display: grid`, `gap: 8`. */
.rd-ev-loc__addr { display: grid; gap: 8px; }

/* City / State / Postal / Country at the drawn `flex` ratios 2 / 1 / 1 / 1.2.
   Wraps rather than crushing, the same call `.rd-ev-form__row` makes. */
.rd-ev-loc__row { display: flex; flex-wrap: wrap; gap: 8px; }
.rd-ev-loc__cell--city { flex: 2 1 140px; min-width: 0; }
.rd-ev-loc__cell--state { flex: 1 1 70px; min-width: 0; }
.rd-ev-loc__cell--postal { flex: 1 1 70px; min-width: 0; }
.rd-ev-loc__cell--country { flex: 1.2 1 84px; min-width: 0; }

/* The consequence line and the coordinates line are both drawn at
   `maxWidth: '64ch'`. */
.rd-ev-loc .rd-ev-form__hint { max-width: 64ch; }

/* Ruling 2's missing case: where the segmented Public/Private choice would be, a
   personal context gets the `Private` chip alone and the sentence under it. The
   treatment is 38 Pro Locations' — that document draws the missing case on the
   record and this sheet is the same record's other door — at this sheet's own
   metrics. The row exists only to give the chip the segments' vertical rhythm;
   the sentence under it is the `__hint` above. */
.rd-ev-loc__vis { display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px; }

/* `display: flex`, `alignItems: center`, `gap: 16`. */
.rd-ev-loc__acts { display: flex; align-items: center; gap: 16px; }

/* volt, not acc — a FILL, per the kit's own rule.
   ⚠️ **The drawn `tcham(7)` is NOT reproduced, deliberately.** The drawing puts
   the same chamfer on this button and on the roster picker's `Add 2 Members`,
   and the shipped `.rd-ev-pick__add` carries no `clip-path` — so chamfering only
   this one would leave the two dialogs of this screen family with different
   primary actions for no stated reason, which is the same argument that keeps
   the Tab deferral matched rather than fixed here alone. If the chamfer comes
   back it comes back on both, in one change. */
.rd-ev-loc__save {
  flex: none;
  background: var(--rd-volt);
  color: var(--rd-on-volt);
  border: 0;
  padding: 10px 18px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 11px;
  cursor: pointer;
}
.rd-ev-loc__save:disabled { opacity: .5; cursor: default; }
.rd-ev-loc__save:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* Drawn as text beside the action, not as a second button-looking thing. */
.rd-ev-loc__cancel {
  flex: none;
  background: none; border: 0; padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
  cursor: pointer;
}
.rd-ev-loc__cancel:hover { color: var(--rd-fg); }
.rd-ev-loc__cancel:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }
/* ==== ADM-LIB · 35 Pro Org Library / rd-pro-library.jsx · owner: lane-lib ==== */

/* The Org Library's one table. NOT a `<table>` — the same flex-row idiom every
   other Pro list uses (.rd-ev-scores, ADM1 §7), with a header strip over it.

   Every geometry value below is read off ScreenLibrary (rd-pro-library.jsx:158-174):

     container   flex 1, minHeight 0, padding '18px 32px 32px'
     head        flex, gap 18, padding '0 0 10px', 1px --rd-line under
     head cell   pdisp(700, 78, '.11em'), fontSize 9, t.fg3
     row         flex, gap 18, align center, padding '14px 0', 1px --rd-line under
     name        flex 1, minWidth 0, pbody 14, t.fg
     made by     width 180, pbody 12.5, t.fg2
     visibility  width 130
     action      width 90, textAlign right, pbody 12

   The action cell is the drawn 90px. It held 176 while this page also carried
   Squad and the Delete flow — three controls do not fit 90 — and the comment
   there promised the width back "in the same commit that deletes them". R1
   deleted them; this is that commit. */

/* ---------------------------------------------------------------------------
   THE SEARCH (R2). UNDRAWN on this screen and NOT invented here: the Pro corpus
   draws this field twice — `rd-pro-roster.jsx:56` and `rd-pro-progress.jsx:145`,
   identically — as

     flex 1, maxWidth 340, background t.field, 1px solid t.ctrl,
     padding '9px 12px', pbody, fontSize 13

   and `rd-plat__input` already builds exactly that in this console. Copied from
   the drawn pair rather than from the neighbouring rule, so the two consoles
   stay one field rather than two that happen to match today.

   `padding: 18px 32px 0` was copied from `.rd-protabs` so the field sits on the
   same left edge as the tabs it sits above and the rows below them. ⚠️ The
   strip's own padding has since become `0 32px`, with its 18px of top pad moved
   onto `.rd-protabs__tab` so a tab's TAP AREA is the whole tab and not its
   label (James, NTOA rehearsal 2026-08-29). The 32px edge — the only half this
   rule was ever copying — is unchanged, and the 18 above the field is now this
   block's own value rather than an echo of the strip's. Row, not block:
   the max-width has to cap a flex child, and a bare `max-width` on a
   full-bleed input would leave the box aligned but the field unbounded.
   --------------------------------------------------------------------------- */
.rd-adm-lib__search {
  flex: none;
  display: flex;
  padding: 18px 32px 0;
}

/* ⚠️ THE CAP MOVED OUT ONE LEVEL, and it had to. `RdSearchField`'s wrapper IS
   `.rd-adm-lib__search`, so the flex CHILD of this row is the chamfered box the
   component mounts — an element with no class of ours to name. The 340 cap on
   the input would size a great-grandchild and leave the box full-bleed with a
   narrow value inside it, which is the same wrong element `.rd-find__wrap` and
   `.rd-mem-searchwrap` each moved a cap off. A universal child selector rather
   than a new class, because there is exactly one child and the component owns
   it. */
.rd-adm-lib__search > * {
  flex: 1;
  min-width: 0;
  max-width: 340px;
}

/* ⚠️ The box, its fill, its stroke, its padding, the placeholder colour step and
   the pre-kit backfill defence are ALL `au5.css`'s now
   (`docs/design/kit/A6-search-field.md`) — including the (0,0,2,0) compound
   this part used to carry its own copy of, which is anchored on the field's
   wrapper and so still outranks the backfill here.

   What survives is the one drawn deviation: 13px type, from the Pro corpus's
   own pair (`rd-pro-roster.jsx:56`, `rd-pro-progress.jsx:145`). This part is
   384 in order.txt against `au5.css`'s 317, so a single class here wins the
   tie. */
.rd-adm-lib__search-input {
  font-size: 13px;
}

.rd-adm-lib {
  flex: 1;
  min-height: 0;
  overflow: auto;
  padding: 18px 32px 32px;
  /* ⚠️ **CORRECTED 2026-08-25. THIS IS NO LONGER THE PUBLISH CONFIRM'S
     CONTAINING BLOCK, AND THE DECLARATION STAYS ANYWAY.**

     It read: "The scrim of RdConfirmModal is `position: absolute; inset: 0`,
     which resolves against the nearest POSITIONED ancestor. Without this the
     publish confirm escapes to the viewport and covers the rail — the caller
     contract that component states and cannot enforce from inside."

     That contract is retired. `.rd-confirm` is `position: fixed` now, and THIS
     ELEMENT is why: it is `overflow: auto` under the fixed-rail scroll model, so
     the scrim sized itself to the list region and centred the panel ~104px right
     and ~110px low of the viewport centre — James's NTOA finding, on this exact
     screen. The whole mechanism is written down once, at `.rd-confirm` in
     adm1-5-1.css.

     Kept rather than deleted: one declaration, no cost, and it is what would
     make an absolute scrim behave again if the ruling were reversed. What must
     not happen is the old contract being re-derived from a declaration that has
     stopped serving it — hence the correction rather than a quiet removal. */
  position: relative;
}

/* ⚠️ `.rd-adm-lib--empty` IS DELETED (2026-08-28, James — NTOA rehearsal), and
   the reason it existed is written down here so it is not re-derived.

   It read `display: flex; flex-direction: column`, and it was mounted on this
   pane for the `Empty` and `NoMatches` bodies only. `RdEmptyState` is `flex: 1`
   and centres both axes (adm1-3.css), so a flex column to claim gave that block
   the pane's whole free height and dropped its two lines to the vertical middle
   of the list area — half a screen below the tab strip.

   NO OTHER Pro list page gives it one. `.rd-adm-ev` (_legacy-events-list.css:67),
   `.rd-loc` (adm-loc.css:29) and `.rd-mem-body` (adm-mem.css:110) are all plain
   blocks, so on Events, Locations and Members the `flex: 1` is inert and the
   empty block hugs the top of the list area, right under the head and the
   filter row. Org Library was the only page that read differently, and it read
   differently because of this one rule.

   The declaration the modifier avoided is still avoided: the pane is NOT a flex
   column in any state, so the list's rows never gain `flex-shrink: 1` on the
   block axis. Deleting the modifier keeps that property and drops the
   divergence — one less state-conditional class on the pane, which is what
   `ruling_the_list_pane_is_one_class_in_every_state` (drills.rs) now pins. */

/* ⚠️ THE HEAD, THE ROW AND THE FOUR COLUMN CLASSES ARE GONE TO `ProTable`
   (batch-15 slice 9, `docs/design/kit/15-pro-table.md`). `.rd-adm-lib__head`,
   `.rd-adm-lib__head > span`, `.rd-adm-lib__row` and
   `.rd-adm-lib__c-name/-who/-vis/-act` are declared nowhere now and emitted
   nowhere: the strip is `.rd-ptable__head`, the row is `.rd-prow` at
   `RowDensity::Standard14`, and the four widths are the ONE `Vec<ProColumn>`
   `drills.rs::library_columns` hands to both bands.

   Two reasoning comments left with them and neither is lost:

   - The head-reuses-the-row's-classes ruling (and its `font: inherit` undo, and
     the `> span` specificity argument) is quoted in full on
     `library_columns`, which is now the single declaration it argued for.
   - The virtualization arithmetic — "50px = 14+14 padding + the tallest cell,
     the `.rd-prochip` at 9px + 4+4 padding + its 1px border (~21px) + the row's
     1px hairline; `auto` keeps the measured height" — is `kit-pro-table.css`
     §9's, and the 50 rides the mount as this table's own measurement.

   What stays below is what the ROWS put INSIDE those cells, which was never the
   table's: the name's link, the courses' fixed words, the Made by tone and the
   Publish control. */

/* The Made by tone. The drawing colours the `MadeBy` SPAN
   (`rd-pro-library.jsx:103`, `color: gone ? t.fg3 : t.fg2`) and gives the cell
   at `:169` no colour at all, so the tone rides an inner span here too — the
   primitive's `Meta` cell is `--rd-fg3` (15-pro-table.md §6.1) and owns the
   12.5px, the 180px and the ellipsis. One declaration, and it is the one the
   drawing puts on this element. */
.rd-adm-lib__who { color: var(--rd-fg2); }

/* The name is the console's only door to the drill form, so it is a link on a
   page whose drawn cell is plain text. It therefore reads as the row's own type
   and takes its affordance from hover + focus rather than from a standing accent
   colour — a column of volt names would make the accent mean "row" instead of
   "the one thing to act on". */
/* The Driving row's second line — the exercise family and how many rows its
   criterion grid asks for (WP5).

   ⚠️ ONE TAB'S ROWS ONLY, and no shooting row gains an element: `drill_row`
   renders this span exclusively for a drill carrying a driving arm, so the four
   pre-existing tabs keep the single-line row they have always drawn and their
   heights do not move. The Driving tab needs it because a library of five
   exercises all named `Serpentine` is otherwise unreadable, and the criteria
   count is the one number separating a one-row zero-fault gate from a four-row
   rubric.

   The treatment is `.rd-adm-lib__plain`'s, deliberately — the 12px `--rd-fg3`
   this page already uses for a fact stated under a value, rather than a sixth
   size. `display: block` is the only addition: the Name cell is one flex line,
   and a second inline span would sit beside the link instead of under it. */
.rd-adm-lib__sub {
  display: block;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
  margin-top: 2px;
}

.rd-adm-lib__link { color: inherit; text-decoration: none; }
.rd-adm-lib__link:hover { color: var(--rd-acc); }
.rd-adm-lib__link:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* Courses: the fixed value, said in as many words rather than drawn as a chip.
   rd-pro-library.jsx:130 — VisChip's null branch is `pbody, fontSize 12, t.fg3`,
   deliberately NOT a chip, because a chip in that column implies a set of values
   to choose between and courses have none. */
.rd-adm-lib__plain {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
}

/* One treatment for the trailing control. The cell that used to carry
   `display: flex; justify-content: flex-end` around it is `ProColumn`'s now:
   `width: 90, flex: 'none', textAlign: 'right'` is what the drawing itself
   declares on that cell (`rd-pro-library.jsx:174`), and with one inline-level
   child the right edge is the same edge. */
.rd-adm-lib__act {
  background: none; border: 0; padding: 0;
  cursor: pointer;
  text-decoration: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg2);
}
/* ⚠️ THE INSTRUCTOR'S PUBLISH IS DIMMED — `ScreenLibrary`'s action cell,
   `color: instructor ? t.fg3 : t.fg2`, drawn in the *An Instructor Is Refused,
   Not Kept In The Dark* frame. The step is the SAME `--rd-fg3` the disabled
   state and the foot note already spend; no new token, and no new value.

   ⚠️ Colour ONLY. It is not `:disabled` and must never become it: R6 rules that
   the instructor presses this control and is answered ("only an admin can
   publish to the Range Day library"), and a disabled button cannot be pressed.
   The hover above is deliberately left in reach for the same reason — the word
   is quieter, not inert. `drills.rs::act_class` is the one place that decides
   who wears it. */
.rd-adm-lib__act--dim { color: var(--rd-fg3); }

.rd-adm-lib__act:hover:not(:disabled) { color: var(--rd-fg); }
.rd-adm-lib__act:disabled { color: var(--rd-fg3); cursor: default; }
.rd-adm-lib__act:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* ⚠️ THE DANGER TREATMENT (`--danger`) AND THE INLINE QUESTION (`__act-q`) ARE
   GONE WITH THE CONTROLS THAT WORE THEM (R1). Nothing in this cell is
   destructive any more: the action slot holds Publish, and Delete lives at the
   foot of the drill form where the corpus draws it. Left behind, the two rules
   would be a standing invitation to put a red word back in a management row —
   the exact thing the ruling took out. The reasoning they carried is not lost,
   only relocated: `_legacy-pre-kit.css` and `adm-drillf.css` state it where the
   surviving delete lives. */

/* The foot note. `pbody, fontSize 11.5, marginTop 18, lineHeight 1.55,
   maxWidth '68ch'` (:110-111). */
.rd-adm-lib__foot {
  margin-top: 18px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
  line-height: 1.55;
  max-width: 68ch;
}

/* The Courses foot (`.rd-adm-lib__foot--warn`, the drawing's `color: t.warn`
   at :110) was DELETED 2026-08-30 on James's ruling — the sentence it coloured
   explained an absent visibility setting in engineer's voice. The Pro arc now
   spends --rd-warn nowhere again; see drills.rs's foot-note comment. */

.rd-adm-lib__error {
  margin-bottom: 18px;
  border: 1px solid var(--rd-danger);
  padding: 11px 13px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-danger);
}

/* Load more and `Showing X of N`, on one line (R2). A flex row rather than the
   old `text-align: center`, because the row now has two children and one of them
   outlives the other: on the last page the token is gone and the count is the
   only child, which centres by itself under `justify-content`. */
.rd-adm-lib__more {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 18px 0;
}

/* The count reads as META, not as a control beside a control. Same step as the
   foot note (11.5 / --rd-fg3): it describes the list, it is not part of it. */
.rd-adm-lib__count {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
}

.rd-adm-lib__more-btn {
  background: none;
  border: 1px solid var(--rd-line);
  padding: 8px 16px;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg2);
}
.rd-adm-lib__more-btn:hover:not(:disabled) { color: var(--rd-fg); border-color: var(--rd-ctrl); }
.rd-adm-lib__more-btn:disabled { color: var(--rd-fg3); cursor: default; }
.rd-adm-lib__more-btn:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }
/* ==== ADM-LOC · 38 Pro Locations / rd-pro-locations.jsx · owner: lane-loc ==== */

/* ---------------------------------------------------------------------------
   The org's saved ranges: one list, one record.

   Drawn in `38 Pro Locations` — Pro · Locations, Pro · Locations · empty,
   Pro · Locations · the record, · no address, · new, · nothing uses it,
   · delete confirm, · public range.

   ⚠️ A NEW PART rather than more of ADM1-4, even though the record's field
   chrome is nearly the events sheet's. Two documents draw two destinations, and
   a section file holding both would be the one place in the system still saying
   the location record belongs to the event form. The values below are read off
   `ScreenLocation`, not copied from `.rd-ev-loc` — where they agree, they agree
   because both drawings agree.

   ⚠️ NOT `RdTextField`, for `adm1-4.css`'s reason, restated because it is the
   first question a reader asks: the kit's text field is drawn for Auth and every
   metric differs (box padding 15/16 vs 11/13, value 16px vs 13.5px, label
   10px/.16em vs 9px/.11em) and it is CHAMFERED, which in this family is the mark
   for "this is THE action" and would be spent on every box on the screen.
   --------------------------------------------------------------------------- */

/* ── The list ────────────────────────────────────────────────────────────────
   `{ flex: 1, minHeight: 0, padding: '18px 32px 32px' }` (ScreenLocations).
   `overflow: auto` rather than the drawing's `hidden`: the frame has a fixed
   height and a real org's list does not, and hidden here would clip rows
   instead of scrolling them. */
.rd-loc {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 18px 32px 32px;
}

/* UNDRAWN — the corpus draws no error state on this screen. Follows
   `.rd-adm-ev__error`'s placement: above the rows, never instead of them, so a
   failed refresh does not blank a list already on screen. */
.rd-loc__error {
  margin-bottom: 16px;
  border: 1px solid var(--rd-danger);
  padding: 11px 13px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-danger);
}

/* ── The table is the kit's ──────────────────────────────────────────────────
   BATCH 15 SLICE 10: `.rd-loc__head`, `.rd-loc__row`, `.rd-loc__row--arch`, the
   four `.rd-loc__c-*` columns and the head's type-undo rule are DELETED. The
   list is `ProTable` / `ProRow` (`docs/design/kit/15-pro-table.md`,
   `styles/kit-pro-table.css`), driven by one `ProColumn` list in
   `pages/admin/locations.rs::columns`.

   The rulings those rules carried did not go with them. Three are quoted
   verbatim INTO `kit-pro-table.css` out of this file — the "NOT a `<table>`"
   argument (§banner), the archived row's name-only dim (§8) and the head strip's
   `baseline` variant (§2) — and the fourth, "the header cells inherit the
   strip's type; only the geometry is per-column", is now structural: the width
   is the column's and the type is the cell's role class, so there is one
   declaration of each and no `font: inherit` undo to lose.

   WHAT REMAINS BELOW is the two drawn values the kit's roles do not carry. Both
   are inner spans inside kit cells — `drills.rs`'s `.rd-adm-lib__who` precedent
   — and both exist because spec 15 §6.1 LISTS the spread and RULES NONE OF IT
   (Q5, open). Adopting the kit's mode would be this lane answering a routed
   designer question by changing a shipped pixel. */

/* `...pbody, fontSize: 14.5, color: t.fg` (`rd-pro-locations.jsx:89`). The kit's
   `Name` role is 14 — the corpus mode of 14 / 14.5 / 13.5, none of them ruled.
   Size ONLY: the colour is the role's `--rd-fg`, and leaving it there is what
   lets `.rd-prow--archived .rd-prow__cell--name`'s `--rd-fg2` reach this text by
   inheritance. The ellipsis triple is the role's too, and it works from the
   cell, which is the box with the width. */
.rd-loc__name {
  font-size: 14.5px;
}

/* `textAlign: 'right', fontSize: 12.5, color: t.fg2`
   (`rd-pro-locations.jsx:91`). The kit's `Meta` role is `--rd-fg3`, with
   `--rd-fg2` listed beside it in spec 15 §3 and unruled. Colour ONLY: the size
   is the role's 12.5, the right edge is the column's `CellAlign::Right`, and
   `tabular-nums` — so a column of counts lines up on the digit — is on every
   kit cell already (`kit-pro-table.css` §4), which is a strictly wider
   guarantee than the deleted rule made. */
.rd-loc__used {
  color: var(--rd-fg2);
}

/* The pinned foot copy: 11.5px, `marginTop: 18`, `lineHeight: 1.55`,
   `maxWidth: '76ch'` — widened from 70ch with v0.2, which is the drawing's own
   value and follows its longer sentence. */
.rd-loc__foot {
  margin: 18px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
  line-height: 1.55;
  max-width: 76ch;
}

/* ── Empty ───────────────────────────────────────────────────────────────────
   Centred in the body, `maxWidth: 460`, `padding: 32`. */
.rd-loc__empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 32px;
}

.rd-loc__empty-head {
  max-width: 460px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 75%;
  letter-spacing: .02em;
  font-size: 20px;
  color: var(--rd-fg);
}

.rd-loc__empty-body {
  max-width: 460px;
  margin-top: 12px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg3);
  line-height: 1.6;
}

/* `marginTop: 22`. The button itself is `.rd-pro-top__btn` — the drawn box is
   the same volt fill, the same `10px 18px` and the same 7px chamfer as the page
   head's action, so it is the same control rather than a second spelling. */
.rd-loc__empty-act { margin-top: 22px; }

/* ── The record ──────────────────────────────────────────────────────────────
   `{ flex: 1, minHeight: 0, position: 'relative' }` (ScreenLocation).

   ⚠️ **CORRECTED 2026-08-25.** This read: "`position: relative` is load-bearing,
   not tidiness: it is the containing block for the delete confirm's absolute
   scrim. `RdConfirmModal`'s own docs state the contract it cannot enforce from
   inside — with no positioned ancestor the scrim escapes to the viewport and
   covers the rail too."

   `.rd-confirm` is `position: fixed` now (adm1-5-1.css: under the fixed-rail
   scroll model an absolute scrim centred its panel on the scrolling region
   instead of on the viewport). This element is `overflow: auto`, so it was one
   of the regions doing that. The declaration stays — it costs nothing and it is
   the reversal path — but it is no longer the confirm's containing block, and
   the retired contract must not be re-derived from it. The `position: 'relative'`
   the drawing itself declares (ScreenLocation, above) is unaffected. */
.rd-loc-rec-area {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  position: relative;
}

/* `padding: '24px 32px'`, `maxWidth: 720`. */
.rd-loc-rec {
  padding: 24px 32px;
  max-width: 720px;
}

/* UNDRAWN: loading, load failure and not-found. Rendered inside the kit page
   rather than bare, so opening a record does not flash the legacy island's
   fixed light ground before the dark screen arrives. */
.rd-loc-rec__msg {
  padding: 24px 32px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg3);
}
.rd-loc-rec__msg--error { color: var(--rd-danger); }

/* UNDRAWN, and it is where the server's own refusal lands when it arrives
   after the press rather than before it — "Location is referenced by events and
   cannot be deleted". Above the record, never instead of it. */
.rd-loc-rec__error {
  margin-bottom: 20px;
  border: 1px solid var(--rd-danger);
  padding: 11px 13px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-danger);
}

/* `display: grid`, `gap: 16`. */
.rd-loc-rec__grid { display: grid; gap: 16px; }

.rd-loc-rec__field { min-width: 0; }

/* 9px display 700/78%/.11em in --rd-fg3, `marginBottom: 8`. Element-agnostic:
   the Visibility block has no control for a `for=` to point at, so its head is a
   `div` wearing the same class. */
.rd-loc-rec__label {
  display: block;
  margin-bottom: 8px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .11em;
  font-size: 9px;
  color: var(--rd-fg3);
}

/* The `— required` tail on the Name label. The drawing colours it t.fg3, which
   is the label's own colour, so this only stops a future label change dragging
   the qualifier with it. */
.rd-loc-rec__req { color: var(--rd-fg3); }

/* --rd-field ground, 1px --rd-ctrl, `padding: '11px 13px'`, body 13.5px in
   --rd-fg (ScreenLocation's `box`).

   ⚠️ TWO CLASSES, NOT ONE, and that is load-bearing — `adm1-4.css` found it the
   expensive way. The pre-kit Tailwind backfill styles bare inputs BY ATTRIBUTE
   (`input[type="text"], … { font-size: 1rem; border: 1px solid #d1d5db }`),
   which is specificity (0,1,1) and beats a single class: a one-class rule left
   the text boxes at 16px with a hardcoded border while every sibling took
   13.5px and --rd-ctrl, in the default dark theme, with both theme screenshots
   looking fine. `.rd-loc-rec .rd-loc-rec__box` is (0,2,0) and wins on
   specificity rather than on source order. */
.rd-loc-rec .rd-loc-rec__box {
  display: block;
  width: 100%;
  box-sizing: border-box;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  border-radius: 0;
  padding: 11px 13px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 400;
  font-size: 13.5px;
  color: var(--rd-fg);
  outline: none;
  appearance: none;
  -webkit-appearance: none;
}
.rd-loc-rec .rd-loc-rec__box:focus { border-color: var(--rd-acc); }
.rd-loc-rec .rd-loc-rec__box::placeholder { color: var(--rd-fg3); opacity: 1; }

/* The read-only value. The drawing renders every box of the public record as a
   static div at `opacity: .75` — a value you can read and not reach, rather
   than a disabled input, which would claim there is a control switched off.
   `min-height` keeps an empty field the same height as a filled one, so the
   record does not shrink where it has nothing to say. */
.rd-loc-rec .rd-loc-rec__box--ro {
  opacity: .75;
  min-height: 41px;
  white-space: pre-wrap;
  word-break: break-word;
}

/* The two address lines: `display: grid`, `gap: 8`. */
.rd-loc-rec__addr { display: grid; gap: 8px; }

/* City / State / Postal / Country at the drawn flex ratios 2 / 1 / 1 / 1.2.
   Wraps rather than crushing, the same call `.rd-ev-form__row` makes: four
   13.5px boxes below ~420px would each be narrower than their own label. */
.rd-loc-rec__row { display: flex; flex-wrap: wrap; gap: 8px; }
.rd-loc-rec__cell { min-width: 0; }
.rd-loc-rec__cell--city { flex: 2 1 140px; }
.rd-loc-rec__cell--state { flex: 1 1 70px; }
.rd-loc-rec__cell--postal { flex: 1 1 70px; }
.rd-loc-rec__cell--country { flex: 1.2 1 84px; }

/* The hint under a field — 10.5px in --rd-fg3, `marginTop: 6`,
   `lineHeight: 1.45`. The rename sentence is the one that matters and it is the
   designer's copy at the designer's size. */
.rd-loc-rec__hint {
  margin: 6px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  line-height: 1.45;
}

/* UNDRAWN — the name bound, refused before the wire. Same slot as the hint, in
   the danger colour, because it is a condition rather than a fact. */
.rd-loc-rec__field-error {
  margin: 6px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-danger);
  line-height: 1.45;
}

/* Visibility, v0.2: the chip (or the choice) and the sentence UNDER it.
   ⚠️ The sentence used to sit BESIDE the chip on one baseline; the redraw puts
   it below at `hint` size with `marginTop: 9`, because on a new record it is the
   consequence line for a control rather than a caption for a read-back, and a
   consequence belongs under the thing it is about. */
.rd-loc-rec__vis {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
}

.rd-loc-rec__vis-why {
  margin: 9px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  line-height: 1.45;
  max-width: 66ch;
}

/* The SECOND line, under the segments only — *Promote And Demote*: "a second,
   quieter line under the segments, present only where the choice is live".
   The drawing spreads `hint` and overrides exactly two properties on it:
   `marginTop: 5` and `color: t.fg2`. So this is `__vis-why` with those two
   moved and nothing else — the smaller gap binds it to the line above it as a
   pair rather than to the block below, and fg2 is a step LOUDER than fg3
   because it is the consequence the reader has not asked for yet. */
.rd-loc-rec__vis-cost {
  margin: 5px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg2);
  line-height: 1.45;
  max-width: 66ch;
}

/* ── The visibility choice ───────────────────────────────────────────────────
   `display: flex`, `gap: 8` — two options, not a toggle (ScreenLocation's
   `fresh && !personal` arm).

   ⚠️ TWO CALLERS NOW, and only one of them is drawn. The pair is the create
   path's, as drawn; it is reused unchanged on a SAVED record this org may still
   publish, which is James's 14 Aug yes on the flip applied to the drawn
   treatment (`location_editor.rs`, `VisibilitySlot::Change`). No frame shows it
   there yet, so nothing about these rules is per-caller — when the frame lands,
   it changes this file or it does not.

   ⚠️ NOT `.rd-ev-form__seg-opt`, and the two drawings are why: the sheet's
   segments are body-size boxes at `11px 13px` that split the row (`flex: 1`),
   these are label-size chips at `8px 14px` that take their own width. Sharing
   them would tidy one drawing into the other — the call this file already makes
   for the record's text boxes. */
.rd-loc-rec__seg { display: flex; flex-wrap: wrap; gap: 8px; }

.rd-loc-rec .rd-loc-rec__seg-opt {
  box-sizing: border-box;
  background: none;
  border: 1px solid var(--rd-ctrl);
  border-radius: 0;
  padding: 8px 14px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 10.5px;
  color: var(--rd-fg3);
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
}
.rd-loc-rec .rd-loc-rec__seg-opt--on {
  border-color: var(--rd-fg);
  color: var(--rd-fg);
}
/* The drawing is static and has no focus state to copy. These are real buttons,
   so one is required — `adm1-4.css`'s reasoning at the sheet's own segments. */
.rd-loc-rec .rd-loc-rec__seg-opt:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* ── The record's foot: archive, then delete ─────────────────────────────────
   `marginTop: 26`, `paddingTop: 20`, 1px --rd-line over, `display: grid`,
   `gap: 18`.

   ⚠️ RENAMED FROM `.rd-loc-rec__danger` WITH v0.2, because it stopped being a
   danger block: it now holds TWO acts and the calmer one is on top (*"archive in
   the ordinary text tone, delete in the danger tone"*). A class called `danger`
   wrapping an ordinary-tone control is the kind of name that talks a later
   reader into painting it red.

   ⚠️ A BLOCK, not the baseline row `.rd-ev-danger` uses. The two screens draw
   this slot differently and the difference is the drawing's: the event puts its
   label and its sentence on one line, this one stacks the sentence UNDER the
   control in every state. Matching them would be tidying one drawing to look
   like another. */
.rd-loc-rec__foot {
  margin-top: 26px;
  padding-top: 20px;
  border-top: 1px solid var(--rd-line);
  display: grid;
  gap: 18px;
}

/* Archive: 13px in --rd-fg, the ORDINARY text tone. A text link like the delete
   below it, and deliberately not a button — but with none of the danger colour,
   because *"the foot of a record is not where a page should be recommending
   anything"* and archive is the act an operator wants far more often. */
.rd-loc-rec__arch-link {
  display: inline-block;
  background: none;
  border: 0;
  padding: 0 0 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg);
  border-bottom: 1px solid var(--rd-ctrl);
  cursor: pointer;
}
.rd-loc-rec__arch-link:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }
.rd-loc-rec__arch-link[disabled] { color: var(--rd-fg3); cursor: default; }

/* Un-archive, in the ACCENT tone — *"rather than the danger tone, because
   putting a range back is not a destructive act."* */
.rd-loc-rec__arch-link--undo {
  color: var(--rd-acc);
  border-bottom-color: var(--rd-acc);
}

/* The offered case: 13px in --rd-danger. A text link, not a button — the
   drawing's ranking, the same one the event delete takes. */
.rd-loc-rec__danger-link {
  display: inline-block;
  background: none;
  border: 0;
  padding: 0 0 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-danger);
  border-bottom: 1px solid var(--rd-danger);
  cursor: pointer;
}
.rd-loc-rec__danger-link:focus-visible { outline: 2px solid var(--rd-danger); outline-offset: 2px; }

/* The refused case's head: display 800/78%/.06em at 10.5px in --rd-fg2, with no
   underline, no danger colour and no cursor. It is a statement, not a control
   that has been switched off — the whole argument of this frame is that a greyed
   button with no sentence beside it is how a refusal becomes a support ticket. */
.rd-loc-rec__danger-head {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 10.5px;
  color: var(--rd-fg2);
}

/* The line under either one. The drawing gives the offered case `maxWidth: 64ch`
   at 10.5px and the refused cases `maxWidth: 68ch` at 12px; 12px/68ch is taken
   for both, because the offered sentence is the shorter of the three and never
   reaches the limit, and two sizes for one slot would make the delete block
   change weight with its own state. */
.rd-loc-rec__danger-why {
  margin: 8px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
  line-height: 1.55;
  max-width: 68ch;
}

/* ── The phone — 46 (Mobile) Pro Destinations row 03 ─────────────────────────
   `ScreenProLocationsM` (`components/rd-pro-mobile-pages.jsx:77`), framed once
   in `46 (Mobile) Pro Destinations.html` row 03. Component:
   `apps/web/src/pages/admin/locations_mobile.rs`, which carries every ruling;
   only the values live here.

   ⚠️ THE ROW STACK IS NOT IN THIS FILE, AND ITS ABSENCE IS THE POINT.
   `adm-mob.css` (owner: lane-drills) declares doc 46's shared primitive —
   `.rd-prom-list`, `.rd-prom-row`, `.rd-prom-row__main`, `.rd-prom-row__name`,
   `.rd-prom-row__sub` — for the whole document, and this screen MOUNTS those
   classes. An earlier draft of this block declared a second stack under
   `.rd-locm__*`; it went the day `adm-mob.css` landed, the same yield
   `adm-lib-mob.css` records. "Decided Here" #2 is ONE drawn rule, and two
   spellings of one rule is how two screens start disagreeing about a padding
   nobody meant to change.

   ⚠️ NO NEW PART, AND THE REASON IS ORDER RATHER THAN TIDINESS. `adm-lib-mob.css`
   is its own part because it must sit AFTER `adm-lib.css` to re-lay two desktop
   gutters at equal specificity. This screen needs no such override: at phone
   width it mounts `.rd-proml` as its pane instead of `.rd-loc`, so nothing here
   competes with a rule declared elsewhere, and every selector below is either a
   fresh name or a (0,0,2,0) descendant that wins on specificity from wherever it
   sits. With no ordering constraint there is nothing for a separate part to buy,
   and `adm-loc.css` is the part that owns drawing 38 at every width.

   ⚠️ NO MODIFIER ON A BASE THIS FILE DOES NOT DECLARE. `adm-loc.css` sits at
   order position 371 and `adm-mob.css` at 439, so an equal-specificity modifier
   written here against one of their classes would LOSE. The ownership word
   therefore declares its own base beside its modifier rather than hanging
   `--ours` off `.rd-prom-owner`, whose treatment it otherwise matches exactly.
   If a third destination wants that word, it is `adm-mob.css`'s to promote under
   a neutral name — not this file's to reach forward for.

   ⚠️ NO `@media` IN THIS BLOCK. The breakpoint is asked ONCE, in Rust
   (`device::use_device_class`, in `admin/layout.rs` for the shell and in
   `admin/locations.rs` for this rendering), and these rules paint what those two
   lines mounted. The same call `adm-mob.css`, `adm-lib-mob.css` and
   `adm0-mobile.css` all make.

   ⚠️ NO NEW TOKENS. Every colour below is one the sheet already declares.

   ⚠️ THE TYPE FLOORS, applied not transcribed (`01 Foundations` §05, "Two
   floors, one exemption"): tracked uppercase never below 9.5px, mixed-case never
   below 11px. The drawing's 8.5px right-edge words ship at 9.5 — the same
   correction `.rd-prom__org-role` and `.rd-promd__vis` took — and its 10.5px
   archived line at 11. Marked at each site.

   ANATOMY:

     [ .rd-proml — the pane, where the desktop mounts .rd-loc ]
       [ … .rd-loc__error, borrowed: width-agnostic and already right … ]
       [ … .rd-prom-list, borrowed from adm-mob.css — the stack … ]
         [ … .rd-row, the KIT's RdRowLink (kit-row.css) … ]
           [ .rd-row__main → __name (+ --past) / __sub ]
         [ .rd-proml__edge — the desktop's LAST TWO columns, stacked,
           mounted into the kit row's .rd-row__trail (spec 16 §9.1) ]
           [ .rd-proml__used ]
           [ .rd-proml__arch-mark ] [ .rd-proml__own (+ --ours) ]
       [ .rd-proml__arch-head — the drawn count, heading the archived rows ]
       [ … .rd-loc__foot, borrowed … ]
   --------------------------------------------------------------------------- */

/* The pane, where the desktop mounts `.rd-loc`. Everything that rule does except
   its `padding: 18px 32px 32px` — the desktop console's page gutter, which the
   phone shell already supplies as its own 18px (`.rd-prom`). A `.rd-prom .rd-loc`
   override would have been the other way to write this, and it is not taken here
   because this pane also drops the desktop's horizontal padding entirely rather
   than re-laying it: one fresh name says that plainly, and needs no note about
   which part must sit after which. */
.rd-proml {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 0 0 24px;
}

/* ── The archived row's quiet name is the KIT's ──────────────────────────────
   BATCH 15 SLICE 10: `.rd-proml__row--arch .rd-prom-row__name` is DELETED, and
   so is the `row_class_mobile` that spelled the modifier. The row is
   `RdRowLink`, and the quiet name is `NameTone::Past` — one `--rd-fg2` on the
   name line and nothing else, expressly never an opacity on the row
   (`styles/kit-row.css` §`.rd-row__name--past`).

   That rule shipped with a flag on it: *"NOT `.rd-prom-row__name--past`,
   although the treatment is identical … The two should merge under a neutral
   name when a third destination wants a quiet row name — that is
   `adm-mob.css`'s call, and this is the flag for it."* The kit is the third
   caller and took the merge. The colour is settled; the WORD is not, and the
   row builder carries the question.
   --------------------------------------------------------------------------- */

/* The right edge. `rd-pro-mobile-pages.jsx:88` draws ONE line here; the
   ownership word is a second, kept because *38 Pro Locations* rules that the
   list says whose a range is (see `locations_mobile.rs`'s head). So this is a
   column where every other destination has a single mark, and it is therefore
   NOT `.rd-prom-row__chip` (adm-mob.css), which is a centred flex box built to
   hold one real chip.

   `flex: none` so the verdicts never compress — the left cell is the one that
   gives way, which is the whole no-horizontal-scroll claim. */
.rd-proml__edge {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 3px;
  text-align: right;
}

/* `pdisp(700, 78, '.1em')` at 8.5px, `color: t.fg3` — always quiet, on the
   drawing's own instruction: a usage count is a fact, never a verdict.

   ⚠️ 9.5px, NOT THE DRAWN 8.5 — the condensed-uppercase floor. */
.rd-proml__used {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  text-transform: uppercase;
  font-size: 9.5px;
  color: var(--rd-fg3);
  white-space: nowrap;
}

/* The ownership word — UNDRAWN on the phone and carried past the frame, because
   *38 Pro Locations* rules that "public ranges are listed, and the chip says
   whose they are". Treatment matches `.rd-prom-owner` (adm-mob.css) exactly; see
   this block's banner for why it is declared rather than borrowed.

   ⚠️ QUIET IS THE BASE AND `--ours` ADDS, which is `.rd-prom-role--staff`'s
   shape and `members.rs::role_mark_class`'s ratchet: the modifier is ADDED to a
   base rather than swapped for a second one, so a row that loses its modifier
   falls to the understatement rather than to a claim. The desktop's own test is
   `tone={ours(l) ? 'plain' : 'dim'}` — one test, not four.

   ⚠️ NOT the accent, and not --rd-danger. Another org's published range is a
   fact, not a problem — `ProTone::Dim` is the kit's "true, and no longer in
   play". */
.rd-proml__own {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  text-transform: uppercase;
  font-size: 9.5px;
  color: var(--rd-fg3);
  white-space: nowrap;
}

.rd-proml__own--ours {
  color: var(--rd-fg2);
}

/* The `Archived` mark, BEFORE the ownership word — the desktop's drawn order
   (`{l.arch && <ProChip tone="dim">Archived</ProChip>}` then the source chip). A
   bordered micro-word rather than a third bare one, so the row's state reads as
   a mark and not as a third fact about the range. */
.rd-proml__arch-mark {
  border: 1px solid var(--rd-line);
  padding: 1px 5px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  text-transform: uppercase;
  font-size: 9.5px;
  color: var(--rd-fg3);
  white-space: nowrap;
}

/* `rd-pro-mobile-pages.jsx:92` — `tbody` at 10.5px, `t.fg3`, `marginTop: 10`.
   The drawn sentence, HEADING the archived rows rather than standing in for
   them (see the component).

   ⚠️ 11px, NOT THE DRAWN 10.5 — the mixed-case half of the type floor. `tbody`
   sets no `text-transform`, so this is body copy. The same call `adm-mob.css`
   made for `.rd-prom-count`, which is the same line on another screen. */
.rd-proml__arch-head {
  margin-top: 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.35;
  color: var(--rd-fg3);
}

/* The archived stack sits CLOSER to its heading than the heading sits to the
   live list above it, or the sentence reads as a foot on the wrong block. The
   shared list's own `margin-top: 14px` is the drawn gap between the title row
   and the FIRST stack, and it is not the gap between a label and the rows it
   labels. An adjacent-sibling rule rather than a `--arch` modifier class: it
   names nothing new, and at (0,0,2,0) it wins from this part's position whatever
   the order ends up being. */
.rd-proml__arch-head + .rd-prom-list {
  margin-top: 6px;
}

/* The empty state is the desktop's block — same copy, same door, same gate. Only
   its `padding: 32px` is re-laid: the phone shell already carries an 18px
   gutter, and 32 more each side leaves a 460px-capped paragraph 384px of a
   448px screen. (0,0,2,0), so it wins wherever this part sits. */
.rd-proml .rd-loc__empty {
  padding: 24px 0;
}
/* ==== ADM-PROG · 37 Pro Shooter Progress · owner: lane-lib ==== */

/* ---------------------------------------------------------------------------
   What the org's people already shot. Three screens, one section file.

   Drawn in `37 Pro Shooter Progress` — Pro · Shooter Progress, Pro · One
   shooter (both frames), Pro · One standard across the org, and
   Pro · Shooter Progress · empty.

   ⚠️ ONE PART FOR THREE SCREENS, deliberately, where ADM-MEM split from ADM2
   for the opposite reason. The members split followed two DOCUMENTS drawing two
   destinations; these three frames are one document drawing one destination
   reached three ways, and they share the row rhythm, the column head and the
   warn rail. A file per frame would put the same four declarations in three
   places and let them drift.

   ⚠️ NO `<table>` ANYWHERE, and the drawing is the reason: every list here is
   flex rows under a header strip. Same idiom as ADM1's scores list and the Org
   Library — which is exactly why the two desktop lists are `ProTable` /
   `ProRow` since batch 15 and their geometry is no longer written here.

   ⚠️ THE STRIP IS NO LONGER HIDDEN FROM ASSISTIVE TECH. It was, and the reason
   was that a bare link row's four cells read as four unrelated fragments, so
   each row hand-wrote a sentence instead. The primitive emits `role="table"` /
   `columnheader` / `row` / `cell`, so the association is real and the strip is
   readable; the hand-written row sentence went with the reason for it.

   The geometry is the drawing's, and two thirds of it now lives in Rust: the
   org list's 140 / 90 / 120 with the name taking what is left is
   `progress_columns()`, and the standard screen's two-shape right edge is
   `standard_columns()`. What is still here: the shooter screen's 380px right
   column beside a flexible left one at a 34px gap, and the standard screen's
   860px cap.
   --------------------------------------------------------------------------- */

.rd-prog { padding: 0 0 24px; }

/* Banners own their padding AND their bottom gap now (`p1.css`, THE SLOT
   RULING 2026-08-28) — what is left at the call site is the gap ABOVE and the
   32px console gutter, because `.rd-prog { padding: 0 0 24px }` carries no
   horizontal padding of its own and this is the edge `.rd-prog-list` sits on.
   The 16 that used to be written here is the token. */
.rd-prog__banner { margin: var(--rd-banner-gap) 32px 0; }

/* ---------------------------------------------------------------------------
   Search and the three chips.
   --------------------------------------------------------------------------- */
/* `.rd-prog-bar` is GONE — the desktop strip is `ProFilterBar` and
   `.rd-pfilter--band` carries the identical `16px 32px 14px`, byte for byte.
   `.rd-prog-filters` went with it; the CHIP classes below did not, because the
   phone arm's own strip still draws them. */

/* Search leads, at the drawn 320px — and the 320 is the SLOT's now
   (`.rd-pfilter__field--w320`), along with the `flex: 1 1 240px` this class used
   to carry.

   ⚠️ What survives is the one thing the slot cannot supply. `.rd-search` is
   `display: block` (`au5.css:168`), so as a flex child of `.rd-pfilter__field`
   it takes `flex: 0 1 auto` and sizes to its input — stopping well short of the
   cap the slot sets. `flex: 1` is what makes the field fill the slot it was
   given; `min-width: 0` is what lets it shrink rather than pushing the chips off
   the row. */
.rd-prog-searchwrap {
  flex: 1;
  min-width: 0;
}

/* ⚠️ The box is the kit's now (Q2, `A6-search-field.md` §10): fill, stroke,
   padding, placeholder colour and focus all come from `au5.css`. What survives
   is the console's own 13px type, a step down from the kit field's 16 —
   `au5.css` is part 317 and this is 395, so a single class here wins the tie. */
.rd-prog-search {
  font-size: 13px;
}

/* A FILTER, not a status. The accent on the selected chip is settled on the
   members screen: "the one you are on" is the same fact `live` names on a
   status axis, so the two uses agree rather than compete.

   ⚠️ THE PHONE ARM IS THE ONLY CONSUMER LEFT, and that is why this survives the
   Progress migration while `.rd-prog-bar` and `.rd-prog-filters` do not. The
   desktop chips are `.rd-pfilter__chip` now — the same six declarations, in the
   kit — but `ProFilterBar` is desktop-only by spec 17 §13, so `filter_chip_class`
   still answers for `.rd-progm__chips`. Deleting these would leave the phone
   chips unstyled, which is exactly the silent no-op of #209.

   ⚠️ **TWO LAYERS, AND THIS ARM WAS MISSED BY #1363.** That PR converted the
   Members phone strips (`.rd-prom-tab`, `.rd-prom-chip`, `.rd-promd__kind`) and
   `ProChip`; this chip is the Progress phone strip and drew the same square box
   for the same reason. `48 Kit Rules` — *"a stroked chamfer must be two
   layers"*; `01 Foundations`:85. The construction, the 13px control cut and why
   a `border` under a `clip-path` cannot draw a diagonal are all in
   `kit-pro-filter-bar.css` §3's banner — this is the same chip at the other
   width, and the two may not diverge. */
.rd-prog-filter {
  position: relative;
  isolation: isolate;
  border: 0;
  background: var(--rd-line);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--rd-chamfer-control)), calc(100% - var(--rd-chamfer-control)) 100%, 0 100%);
  color: var(--rd-fg3);
  /* The drawn `5px 9px` plus the 1px the border used to occupy. */
  padding: 6px 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .08em;
  font-size: 9px;
  cursor: pointer;
}
/* The surface, inset by the stroke and cut at `inner_chamfer_px(13, 1)`. Opaque,
   or the outer layer's stroke colour floods the chip. */
.rd-prog-filter::before {
  content: "";
  position: absolute;
  inset: 1px;
  z-index: -1;
  background: var(--rd-hollow-fill);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 12.41px), calc(100% - 12.41px) 100%, 0 100%);
}
.rd-prog-filter--on { background: var(--rd-acc); color: var(--rd-fg); }
/* An inset ring on the SURFACE layer: `clip-path` clips an `outline` away, and
   an element-level inset shadow paints under the surface. */
.rd-prog-filter:focus-visible { outline: none; }
.rd-prog-filter:focus-visible::before { box-shadow: inset 0 0 0 2px var(--rd-acc); }

/* ---------------------------------------------------------------------------
   The org list: `ProTable` + `ProRow`, and what is left of this page's own ink.

   ⚠️ THE FOUR WIDTHS ARE GONE FROM HERE, and that is the migration. This block
   used to open by explaining why they were written twice — "The strip and the
   rows carry the SAME four widths… written twice rather than shared through a
   common class because the strip's cells are labels and the rows' are values
   with their own type and tone". Both halves are answered now and neither is
   answered here: `progress_columns()` declares the four widths ONCE and both
   bands read them, and `ProColumn::role` types the two bands separately off that
   one geometry — which is the thing a shared CSS class could not do and the
   reason the duplication existed.

   `.rd-prog-head`, `.rd-prog-head__*`, `.rd-prog-row` (with its hover ground and
   inset ring) and `.rd-prog-name` are DELETED: `.rd-ptable__head`,
   `.rd-ptable__hcell`, `.rd-prow--link` and `.rd-prow__cell--name` each carry the
   identical declarations. What survives below is only the TONE the drawing
   colours per cell, which no role can carry because it is per-row state.
   --------------------------------------------------------------------------- */

/* The 32px page gutter, which the primitive deliberately does not own: "It
   declares no width, no padding and no ground… every drawn table takes its
   padding from the pane that mounts it." This wrapper is that pane. It also now
   holds the HEAD, which used to take the same 32px as a margin of its own. */
.rd-prog-list { padding: 0 32px; }

/* Last run — the cell is `CellRole::Meta` now: 12.5px in the body face, with
   `--rd-fg3` and the ellipsis. What survives is the WARM tone, because the
   drawing's cell is `color: cold ? t.fg3 : t.fg2` (rd-pro-progress.jsx:144) and
   Meta's own tone is the cold half. `--cold` restates fg3 rather than dropping
   to the cell's default: a modifier that renders as the absence of its base is
   invisible the moment the base moves. */
.rd-prog-last { color: var(--rd-fg2); }
.rd-prog-last--cold { color: var(--rd-fg3); }

/* Runs — the cell is `CellRole::Numeric`, which IS the drawing's `mono`:
   Archivo 800 / stretch 75% / tabular at 13px (rd-train-kit.jsx:72), a type role
   and not a monospace family.

   ⚠️ THIS CORRECTS A DRIFT RATHER THAN PRESERVING ONE. The deleted rule set this
   cell in 'JetBrains Mono', which no drawing on this screen asks for and which
   the kit's own CellRole doc calls out by name. The column changes face, and
   that is the intended effect of adopting the role.

   Zero is quieter — a fact about the row, not an alarm; the drawing's
   `color: p.runs ? t.fg : t.fg3` (:145). */
.rd-prog-runs { color: inherit; }
.rd-prog-runs--zero { color: var(--rd-fg3); }

/* Declared — `CellRole::Meta`, NOT `Numeric`, and the kit quotes this very cell
   on it: "Declared. NOT mono… `37` rules this cell into the body face because it
   is 'a sentence with two numbers in it', `2 of 3`". The tabular figures are the
   `.rd-prow__cell` base's. The accent lands only on a finished column. */
.rd-prog-declared { color: var(--rd-fg2); }
.rd-prog-declared--all { color: var(--rd-acc); }

/* ---------------------------------------------------------------------------
   Load More, and the foot lines.
   --------------------------------------------------------------------------- */
.rd-prog-more { padding: 16px 32px 0; }

.rd-prog-more__btn {
  background: none;
  border: 1.5px solid var(--rd-ctrl);
  color: var(--rd-fg);
  padding: 8px 16px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 10.5px;
  cursor: pointer;
}
.rd-prog-more__btn:disabled { color: var(--rd-fg3); border-color: var(--rd-line); cursor: default; }
.rd-prog-more__btn:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 3px; }

/* The foot carries a claim about the whole page, so it is measured rather than
   ragged: 70ch is the drawing's own cap on this line. */
.rd-prog-foot {
  margin: 16px 32px 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
  line-height: 1.55;
  max-width: 70ch;
}

/* Where a list would be, a sentence — the house treatment, reused here for the
   two columns of the shooter screen that can each be empty on their own. */
.rd-prog-none {
  margin-top: 4px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg3);
  line-height: 1.5;
  max-width: 64ch;
}

/* ---------------------------------------------------------------------------
   One shooter — which is also Member Detail.
   --------------------------------------------------------------------------- */
.rd-prog-shooter { padding: 0 0 28px; }

/* Two columns at the drawn 34px gap. They stack under a narrow console rather
   than scrolling sideways: the right column is a fixed 380 and the left needs
   `min-width: 0` or one long drill name refuses to shrink and takes the layout
   with it. */
.rd-prog-cols {
  display: flex;
  gap: 34px;
  flex-wrap: wrap;
  padding: 22px 32px 0;
}

.rd-prog-runs-col { flex: 1 1 380px; min-width: 0; }
.rd-prog-qual-col { width: 380px; flex: none; }

/* The two column heads. Same treatment as the org list's header strip, one step
   larger, because here it is the only label the column gets. */
.rd-prog-colhead {
  margin-bottom: 12px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* A column's foot sits inside the column, so it takes the column's edges rather
   than the page's. */
.rd-prog-runs-col .rd-prog-foot { margin: 14px 0 0; }

.rd-prog-run {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 0;
  border-bottom: 1px solid var(--rd-line);
}

.rd-prog-run__main { flex: 1; min-width: 0; }

.rd-prog-run__drill {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The event, when we know its name. Rendered only when there is one, so no
   space is reserved for it. */
.rd-prog-run__event {
  margin-top: 4px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

.rd-prog-run__when {
  width: 100px;
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--rd-fg3);
}

/* Hit factor — the drawing's `mono` role, same as Runs above and the same
   correction: Archivo 800 / stretch 75% / tabular, not a monospace family.
   The deleted rule set this cell in 'JetBrains Mono', which no drawing on
   this screen asks for — the fix the Runs cell already took, missed here. */
.rd-prog-run__hf {
  width: 70px;
  flex: none;
  text-align: right;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 14px;
  color: var(--rd-fg);
}

/* A run that carries a DECLARED VERDICT spends this cell on the verdict rather
   than on an em-dash — `58 (Mobile) Qualifications`, frame 4: "The verdict
   leads on qualifier rows; ordinary drills keep their number. Same row,
   different headline."

   Wider than the 70px number because a chip is not a number, and no wider than
   the widest of the two chips: the UNDECLARED state is deliberately not drawn
   in this cell (see `run_row`), so there is no third, longer word to fit.

   The number's weight and stretch are dropped because the cell no longer holds
   a number — `.rd-prochip` brings its own type. Nothing here paints a colour:
   `verdict_chip` brings the ruled ink with it (the `rd-prog-chip--warn` rule
   below), so there is exactly one place FAIL's tone is decided. It wins over
   the base rule on source order, both selectors being one class deep.

   ⚠️ THAT NAME IS WRITTEN WITHOUT ITS LEADING DOT ON PURPOSE, and this comment
   is too. `progress.rs::the_failed_chip_is_the_kit_chip_in_the_warn_ink` pins
   the warn ink by splitting the ASSEMBLED sheet on the chip's SELECTOR — the
   name with a dot in front of it — and asserting on the declaration block that
   follows the FIRST occurrence. A selector-form mention in prose above the rule
   therefore hands the pin a comment to read instead of the rule, and the test
   fails naming a comment. Anywhere in this file, refer to a rule a test slices
   for by its bare class name. */
.rd-prog-run__hf--verdict {
  width: 92px;
  font-weight: 400;
  font-stretch: 100%;
  font-size: 12px;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   Qualifications, as declared.

   The panel word was a RULING over an earlier drawing that said "Assessments":
   a curated Assessment is a scored thing in this product and never carries a
   declared state, so the two could not share a word on the one surface that
   shows both. The 13 Aug corpus adopted it. Nothing about the geometry changed
   with the word.
   --------------------------------------------------------------------------- */

/* The FAILED verdict chip — the third drawn state, on both this panel and the
   standard view.

   ⚠️ **Warn, deliberately not danger.** The drawing: "Warn rather than a new
   red, because this is the same class of fact as the page's own caveat and a
   red chip would read as an error in the app." It is the same ink as this
   page's two warn rails below.

   The kit's chip has four tones and no warn one, and adding a fifth is a spine
   decision that would reach every Pro surface. So this is the composition
   `.rd-tdl-chip--warn` already ships on the training side: the element keeps
   `.rd-prochip` for the whole geometry and this rule contributes only the ink,
   so a change to the Pro chip's scale still reaches the Failed chip. It wins on
   SOURCE ORDER — pro-kit.css is an earlier part (order.txt) and both selectors
   are one class deep. */
/* ⚠️ `background`, NOT `border-color` — the chip's stroke is its OUTER LAYER's
   fill now, because a `border` under a `clip-path` renders the chamfer's
   diagonal bare (`pro-kit.css`'s chip block; `48 Kit Rules`). `border-color`
   here would be inert and the Failed chip would keep the neutral stroke, which
   is the quiet half of the failure this spelling avoids. The ink is still
   `color`, which rides the outer and inherits into the face. */
.rd-prog-chip--warn { color: var(--rd-warn); background: var(--rd-warn); }
.rd-prog-qual {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 0;
  border-bottom: 1px solid var(--rd-line);
  text-decoration: none;
  color: inherit;
}
.rd-prog-qual:hover { background: var(--rd-surf2); }
.rd-prog-qual:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: -2px; }

.rd-prog-qual__name {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rd-prog-qual__name--none { color: var(--rd-fg3); }

/* ⚠️ 112, NOT the drawn 92 — the face got longer, 2026-08-27. The cell drew
   `8 Aug` and now draws the ruled full date `Sat 8 Aug 2026` (James, 27 Aug:
   the declaration dates on this screen are role 2, per *48 Kit Rules*, Display
   Roles).

   ⚠️ **112 IS A JUDGMENT, NOT A MEASUREMENT — read this before "correcting" it
   back.** The worst face this cell can hold is `Wed 30 Sep 2026`, which comes
   to roughly 85px in Archivo at 11.5px: the drawn 92 probably fitted. The
   number moved anyway, because this cell has no `white-space: nowrap` and no
   ellipsis, so the failure mode of being wrong is not a clipped year — it is a
   WRAP, which takes the row's height with it and breaks the rhythm of every
   row beside it. A headless measure of the same string on the FALLBACK stack
   (system-ui, i.e. Archivo not yet loaded) came out at 92px at 11.5px and
   100px at 12.5px — exactly at and over the old width, which is the
   font-swap wrap this headroom exists to absorb. */
.rd-prog-qual__when {
  width: 112px;
  flex: none;
  text-align: right;
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 11.5px;
  color: var(--rd-fg3);
}

/* One span across BOTH the chip's slot and the date's — 112 + 66. The 66 is the
   drawing's chip slot and is untouched; the 112 is the widened date beside it
   (see above), and the sum moves with it BECAUSE the two row shapes have to end
   on the same edge. Leaving 158 here would have left the undeclared rows short
   of the declared ones by exactly the year. Two empty boxes where a chip and a
   date would go is a row that looks like it failed to load. */
.rd-prog-qual__none {
  width: 178px;
  flex: none;
  text-align: right;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
}

/* The warn rail. `--rd-warn` as a 2px left edge and nothing else: the note is
   not a warning about a failure, it is the limit of what the column above it
   can claim, and a filled banner would rank it with an error. */
.rd-prog-warn {
  margin-top: 18px;
  border-left: 2px solid var(--rd-warn);
  padding-left: 14px;
}

.rd-prog-warn__body {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg2);
  line-height: 1.55;
}

/* ---------------------------------------------------------------------------
   One standard, across the org.
   --------------------------------------------------------------------------- */
.rd-prog-std { padding: 22px 32px 32px; max-width: 860px; }

/* The head's action. Volt fill, chamfer 7 — every primary action in Pro. Volt
   and not `--rd-acc`, because in light mode `acc` is the deep warm brown reserved
   for accent TEXT and puts black on 2.3:1 as a fill. */
.rd-prog-export {
  background: var(--rd-volt);
  color: var(--rd-on-volt);
  border: 0;
  padding: 10px 18px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 11.5px;
  cursor: pointer;
  clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
}
.rd-prog-export:disabled { background: var(--rd-surf2); color: var(--rd-fg3); cursor: default; }
.rd-prog-export:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 3px; }

/* The thesis. FIRST on the screen, on the same warn rail as the shooter panel's
   note, at the drawing's 16px inset and 24px clearance. Two lines and two
   sizes: the claim, then what it rests on. */
.rd-prog-thesis {
  margin-bottom: 24px;
  border-left: 2px solid var(--rd-warn);
  padding-left: 16px;
}

.rd-prog-thesis__lead {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
  line-height: 1.6;
}

.rd-prog-thesis__body {
  margin-top: 6px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg3);
  line-height: 1.6;
  max-width: 66ch;
}

/* `.rd-prog-std__list` and `.rd-prog-std__row` are DELETED. The list is the
   `ProTable` itself — flex column, no head — and the row is `ProRow` under
   `.rd-ptable--dens-13`, which carries the drawn `gap: 18` and `padding: 13px 0`
   and the hairline. No hover and no focus ring: this row does not navigate, and
   "a hover ground under a row nothing happens to is a promise the row does not
   keep." */

/* The name cell is `CellRole::Name` — 14px in the body face, `--rd-fg`,
   ellipsis, `flex: 1; min-width: 0` — so all that survives here is the drawing's
   `color: when ? t.fg : t.fg3` (rd-pro-progress.jsx:247). The base inherits the
   cell rather than restating it, so the two cannot drift; the modifier is the
   only thing this page still decides. */
.rd-prog-std__name { color: inherit; }
.rd-prog-std__name--none { color: var(--rd-fg3); }

/* The declared row's right edge, INSIDE the one `Slot` cell. The 18px is the
   drawn distance between the chip and the date — they were siblings in the row
   (`rd-pro-progress.jsx:249`, under the row's own `gap: 18` at `:246`) — and a `Slot` cell's own
   gap is the kit's 6, drawn for the Locations chip pair. So the pair takes a
   wrapper and the cell sees one child, rather than the fold silently retuning 18
   to 6. */
.rd-prog-std__edge {
  display: flex;
  align-items: center;
  gap: 18px;
}

/* The drawn 120 STAYS, though this cell's face also grew to the ruled full date
   on 2026-08-27 (`Sat 8 Aug 2026`; James's role 2 ruling — see
   `.rd-prog-qual__when` above, which had to move). 120 at 12.5px Archivo holds
   the longest form of that face, so the drawn number is still the right number
   and the `120 + 74` sum the two row shapes end on is undisturbed. Recorded
   rather than left silent: the next reader will want to know this cell was
   looked at and kept, not missed. */
.rd-prog-std__when {
  width: 120px;
  flex: none;
  text-align: right;
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 12.5px;
  color: var(--rd-fg2);
}

/* The undeclared row's span across the chip slot and the date: 120 + 74, the
   drawing's own sum on this screen. It keeps its measured width where
   `.rd-prog-std__when` keeps its 120: the right-edge COLUMN is `ColumnWidth::Auto`
   — as wide as its own child and no wider — so these two numbers are what make
   the two row shapes end on the same edge. Auto is safe here for the reason the
   primitive states: this table has no head strip, which is the one place an Auto
   column can drift off its label. */
.rd-prog-std__none {
  width: 194px;
  flex: none;
  text-align: right;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg3);
}

/* The standard screen's own Load More and foot take the block's edges, not the
   page's — the block already carries the 32px inset. */
.rd-prog-std .rd-prog-more { padding: 16px 0 0; }

/* Rendered only when the export walk actually stopped short. Undrawn, and it
   exists because a capped walk that says nothing when it hits the cap is worse
   than no cap at all. */
.rd-prog-partial {
  margin-top: 14px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-warn);
  line-height: 1.5;
  max-width: 66ch;
}
/* ==== ADM-PROM · 46 (Mobile) Pro Destinations rows 04–05 · owner: lane-track7 ==== */

/* ---------------------------------------------------------------------------
   The Pro destinations at PHONE width, on the 45 shell.

   Drawn in `46 (Mobile) Pro Destinations` — row 04 Shooter Progress here, row
   05 Org Settings appended below by the settings slice. Both sit under the
   permanent org chrome `ADM0-mobile` paints (`.rd-prom`), so nothing here
   declares a shell, a gutter or a header: the page's own `ProTop` is re-laid by
   `.rd-prom .rd-pro-top*` and this file paints only what hangs under it.

   ⚠️ THE RULES ARE THE DESKTOP DOCUMENTS'. `37 (Desktop) Pro Shooter Progress`
   and `33 (Desktop) Pro Org Settings` remain the source of what a screen SAYS;
   `46` translates the layout and nothing else. Where the two disagree about a
   word, the desktop wins — which is why the search box below still carries the
   shipped placeholder and the foot still carries the pinned declared note.

   ⚠️ NO `@media` IN THIS FILE. The breakpoint is asked ONCE, in Rust
   (`device::use_device_class`), and these rules paint what it mounted — the
   rule `adm0-mobile.css` and `s16-tablet.css` both state: a second statement of
   the threshold in CSS drifts against the Rust one and the two disagree at
   exactly the boundary width.

   ⚠️ NO NEW TOKENS. Every value below is an existing ADM0 token.

   ⚠️ TWO TYPE FLOORS, applied not transcribed (`01 Foundations` §05, "Two
   floors, one exemption"): mixed-case text never goes below 11px, condensed
   uppercase labels with tracking may go to 9.5px. The frame's 10.5px mixed-case
   sub-line and footnote therefore ship at 11px. Marked at each site.

   ANATOMY (row 04):

     [ .rd-progm — the body under the page's own ProTop ]
       [ RdBanner — bare, it owns its padding ]
       [ .rd-progm__bar — .rd-progm__search over .rd-progm__chips ]
       [ .rd-progm-list — one KIT ROW per member: RdRowLink, kit-row.css ]
         [ .rd-row__main — .rd-row__name over .rd-row__sub(--warn) ]
         [ .rd-row__trail — holding this file's .rd-progm-row__count ]
       [ .rd-prog-more — BORROWED from adm-prog.css, unchanged ]
       [ .rd-progm__foot ]
   --------------------------------------------------------------------------- */

.rd-progm { padding: 4px 0 24px; }

/* ---------------------------------------------------------------------------
   04.1 Search, then the chips.

   The frame gives its search `margin: '14px 0 10px'` and leads the screen with
   it; the chips are `37`'s and the frame is silent about them, so they stack
   under the box rather than competing with it for a 448px row. Wrapping is the
   desktop bar's own behaviour, kept.
   --------------------------------------------------------------------------- */
.rd-progm__bar {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 14px 0 10px;
}

/* The frame's search: field ground, line stroke, `padding: '10px 12px'` at
   12.5px. Full width, because at this width there is nothing to share the row
   with — the desktop's 320px cap is a statement about a 1280px toolbar. */
/* ⚠️ The box is the kit's now (`A6-search-field.md`): fill, stroke, padding,
   placeholder colour and focus all come from `au5.css`, and the wrapper is
   already full-width because it is a block. What survives is this frame's own
   12.5px type. */
.rd-progm__search {
  font-size: 12.5px;
}

/* The chips themselves are `.rd-prog-filter*` from ADM-PROG, mounted unchanged:
   one filter, one treatment, both widths. This declares only the strip. */
.rd-progm__chips { display: flex; gap: 6px; flex-wrap: wrap; }

/* ---------------------------------------------------------------------------
   04.2 The rows.

   `display: grid; gap: 7` is the frame's own list geometry — a stack of
   separate surfaces, NOT the desktop's hairline-divided run of rows. The
   difference is the point: at 448px a card edge reads as a row boundary where a
   1px rule does not.

   ⚠️ THE ROW ITSELF IS GONE FROM THIS FILE, and its deletion is what spec 16
   §9.2 asks for. `.rd-progm-row` was a SECOND DECLARATION of `mrow` — the same
   flex, centre, gap 12, `padding: 12px 14px`, `var(--rd-surf)`,
   `text-decoration: none`, `color: inherit` as `.rd-prom-row`
   (`adm-mob.css:75-93`) — and it had drifted from it in two places, both of
   which the spec rules DEFECTS rather than variants:

     · NO CHAMFER. The row is drawn `...tcham(10)`
       (`rd-pro-mobile-pages.jsx:110`) like its four siblings and this one was
       square. `48 Kit Rules.html:33` — "A new rounded control is a defect, not
       a variant" — cuts the same way for a square one.
     · A HOVER, `background: var(--rd-surf2)`, on the reasoning that a link
       takes the house affordances. Sound for `.rd-prog-row`, the 1280px console
       row it was quoting; carried across a form factor where a thumb never
       leaves the surface and `:hover` sticks. §10 routes the question to the
       designer and rules what ships meanwhile: no hover.

   The `outline: 2px solid` focus ring went with them, and the three were ONE
   bug: a `clip-path` clips an outline away (§6.2), so the outline only appeared
   to work BECAUSE the chamfer was missing. `kit-row.css` carries the polygon
   and the inset `box-shadow` ring that a clipped row actually needs.

   The name and sub-line are `.rd-row__name` / `.rd-row__sub` now, and the
   sub-line WRAPS where this file clipped it (§10.1, also routed to the
   designer): the drawing declares no `white-space` at all, and a sub-line of
   facts joined by middots loses one silently to an ellipsis. The name keeps its
   clip, and the asymmetry is the argument.

   What is left below is the one thing that is this SCREEN's and not the row's.
   --------------------------------------------------------------------------- */
.rd-progm-list { display: grid; gap: 7px; }

/* Declared. NOT mono, and the frame's own mono is overruled: `37` rules this
   cell into the body face because it is "a sentence with two numbers in it",
   `2 of 3`, and a rule carries to phone where a frame carries only layout. So
   the face, the tabular figures and the tone are `.rd-prog-declared`'s; only
   the size follows the frame.

   ⚠️ THE ONE RULE THAT SURVIVES THE MIGRATION, and it survives BY NAME. Spec 16
   §9.1 lists this class among the five per-screen trailing treatments that are
   "mounted into `.rd-row__trail`" rather than folded into a `RowTrailing`, and
   §7.1 quotes THIS comment as the reason: fold it in and doc 37's ruling on
   `2 of 3` is deleted by a tidy that looks like consistency. `RdRowLink` hands
   it a `trail` slot; the span below is what fills it.

   `flex: none` is kept although `.rd-row__trail` already declares it: the slot
   is a flex box and this span is its child, so the child's own basis is not the
   parent's. It is the same value for a different element. */
.rd-progm-row__count {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--rd-fg2);
}

/* Every one of the org's qualifications declared — the accent the desktop cell
   takes, on the same `declared_is_complete` answer. */
.rd-progm-row__count[data-complete="true"] { color: var(--rd-acc); }

/* ---------------------------------------------------------------------------
   04.3 The foot.

   Carries `DECLARED_NOTE` verbatim, so it is a paragraph and not a caption:
   measured at 70ch like its desktop twin, and floored to 11px. The frame's own
   `marginTop: 10` sets the distance.
   --------------------------------------------------------------------------- */
.rd-progm__foot {
  margin: 10px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  line-height: 1.55;
  max-width: 70ch;
}

/* The Load More block is ADM-PROG's `.rd-prog-more*`, mounted unchanged; its
   32px side padding is a desktop gutter this shell does not want, and that is
   the one thing overridden. */
.rd-progm .rd-prog-more { padding: 14px 0 0; }

/* ---- 05 Org Settings ---- */

/*
   `rd-pro-mobile-pages.jsx` — `ScreenProSettingsM`. 33's sections stacked: a
   caps micro-label, a surface card of rows, and the danger affordance bare at
   the foot. Owner-only sections are ABSENT for a non-owner rather than marked,
   which is a mounting decision and lives in `org_settings.rs::phone_sections` —
   there is deliberately no rule here for a locked or greyed section, because
   there is no such state to paint.
*/
.rd-setm { padding: 0 0 24px; }

/* `Sect` — `marginTop: 18`. */
.rd-setm-sec { margin-top: 18px; }

/* The caps micro-label. Drawn `pdisp(700, 78, '.14em')` at 8.5px, floored to
   9.5: it is tracked uppercase, so 9.5 is its floor. */
.rd-setm-sec__title {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .14em;
  font-size: 9.5px; color: var(--rd-fg3);
  margin-bottom: 8px;
}

/* The card. `background: t.surf, tcham(10), padding: '2px 14px'` — the 2px is
   the row rhythm's, not the card's: each row carries 11px of its own. */
.rd-setm-card {
  background: var(--rd-surf);
  padding: 2px 14px;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%);
}

/*
   A row. `R` — `padding: '11px 0'`, a hairline under everything but the last,
   the action on the right edge.

   A GRID rather than the drawing's flex, and the reason is the sub-line: the
   drawn row has a key over a value and no third line, and the two rows that
   need one (Discoverable, Owner) must put it under the value WITHOUT pushing
   the action out of vertical centre. Two columns and a spanning action say that
   once; a flex row would need a wrapper element per row to say it at all.
*/
.rd-setm-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: 12px;
  align-items: center;
  padding: 11px 0;
  border-bottom: 1px solid var(--rd-line);
}
.rd-setm-row--last { border-bottom: none; }

/* Drawn `pdisp(700, 78, '.1em')` at 8.5px — floored to 9.5, same exemption. */
.rd-setm-row__key {
  grid-column: 1;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .1em;
  font-size: 9.5px; color: var(--rd-fg3);
}

/* `tbody, fontSize: 12.5, marginTop: 3`. */
.rd-setm-row__val {
  grid-column: 1;
  min-width: 0;
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px; color: var(--rd-fg);
}

/* Undrawn at this width and carried from 33, where the copy is ruled: the
   sentence that stops `discoverable` reading as `joinable`, and the one that
   says ownership is separate from administering. The desktop's own sub-line
   values, at the desktop's size — 11.5px clears the mixed-case floor. */
.rd-setm-row__sub {
  grid-column: 1;
  margin-top: 5px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px; color: var(--rd-fg3);
  line-height: 1.5;
}

/* The action spans the row's rows, so it stays centred against a key + value +
   sub stack rather than sitting beside the first line of it. */
.rd-setm-row__act {
  grid-column: 2;
  grid-row: 1 / -1;
  align-self: center;
  justify-self: end;
  text-align: right;
}

/*
   The editing state is the one UNDRAWN thing on this screen — `46` lists
   "settings edit states" under Open — so the SHIPPED editor renders and is
   re-laid here rather than re-invented. The desktop row holds its key in a
   168px column beside the field and its Cancel/Save; at 448px that leaves the
   field about 220px, so the three stack instead. Everything else about the box
   — the accent border, the fill, the Cancel/Save ranking — is untouched.
*/
.rd-setm-card .rd-set-row--editing {
  display: block;
  padding: 11px 0;
}
.rd-setm-card .rd-set-row__key--editing {
  display: block;
  width: auto;
  padding-top: 0;
}
.rd-setm-card .rd-set-row__editacts { padding-top: 10px; }

/* The inert note is right-aligned body copy on the desktop, where it sits in a
   wide action column. Here it is the whole width of the row's action cell, so
   it wraps to the left like the sentence it is. */
.rd-setm-row__act .rd-set-row__inert { text-align: left; }

/*
   "Close this org", bare and below everything — `46`, row 05: "Close this org
   keeps its danger treatment and its distance from everything else". The
   distance is this margin and the absence of a card; the treatment is
   `.rd-set-end__act`, unchanged from the desktop, because it is 33's rule.
*/
.rd-setm__danger { margin-top: 20px; }
/* ==== ADM-RDAY · 36 Pro Actual Range Day · owner: lane-lib ==== */

/* ---------------------------------------------------------------------------
   Squads & Stages — the one range-day surface that is a DESK job.

   Drawn in `36 Pro Actual Range Day`, screen `ScreenSquadBuilder`: the only
   desktop frame of that document, and the only one that goes back inside the
   admin rail. Everything else in the drawing is a tablet held in one hand; the
   builder is the night before, on a laptop, and the drawing says so in its own
   words — *"Squads get made the night before, on a laptop"*.

   ⚠️ IT IS A TAB SINCE 14 AUG, NOT A PAGE. The ruling moved the builder into
   the event's tab strip — *"a view of the event, not an action on it"* — so
   `.rd-rday` is a TAB BODY sitting where `.rd-ev-roster` and `.rd-ev-scores`
   sit, and it carries the same three structural properties they do. Two rules
   arrived with the fold (`.rd-rday-title`, `.rd-rday__split`) and one left with
   it (`.rd-rday-door`, the quiet Overview link, deleted rather than orphaned).
   `.rd-rday-save` stays and still dresses the Save button — that button is
   `event_detail.rs`'s `ProTop` action now, so the class is named in that file
   and no longer in `squads.rs`.

   ⚠️ PHASE B WILL EXTEND THIS SAME PART, not add a second one. The rest of
   `36 Pro Actual Range Day` — the RO landing, the squad board, the RO scoring
   header, the offline states and standings — is the TABLET family of the same
   document, drawn at 1194 x 834. One document, one destination reached several
   ways, so it takes one section file: the same call `adm-prog.css` records for
   its three frames, and the opposite of the ADM2/ADM-MEM split, which followed
   two documents drawing two destinations. When the tablet frames land they
   append below, under their own sub-heading, and the shared vocabulary here
   (the column, the chip, the quiet label) is what they will be built from.

   ⚠️ THE GEOMETRY IS THE DRAWING'S, and it is worth naming because the numbers
   look arbitrary otherwise: a 300px fixed left column beside a flexible right
   one at a 28px gap, `22px 32px 28px` of page padding, equal squad columns at a
   12px gap, 13px 12px 15px inside a column, and chips at 8px 9px with 4px
   between them. Type: the two section labels are the drawn 9.5px uppercase at
   78% stretch, a squad name is 12.5px at 800, a chip is 12px body.

   ⚠️ THE ASSIGNMENT MECHANIC IS UNDRAWN — AND BLESSED, 14 AUG. The drawing
   shows the RESULT — who is in which squad — and no control for getting there.
   Every interactive rule below (`--on`, `.rd-rday-ord`, `.rd-rday-place`,
   `.rd-rday-add`, `.rd-rday-remove`, the two inputs) dresses a control this lane
   invented, and each is marked where it is declared. The ruling on all of them:
   *"The interaction set the build invented is blessed as drawn there. Click a
   chip to select, a `Move here` target at a column's foot, arrow keys for
   shooting order, no drag."* They stay the quietest treatment that still reads
   as a control, because the drawn screen's whole character is a dense static
   board.

   ⚠️ ONE ADDITION WAS ASKED FOR: the selected chip must say where it is going,
   not only that it is selected. `.rd-rday-chip__moving` is that cue and
   `.rd-rday-place` now carries a label naming its column, which is why the chip
   became a flex box with `.rd-rday-chip__name` taking the truncation.

   ⚠️ CHIPS CARRY A `--rd-ctrl` BOUNDARY THAT THE DRAWING DOES NOT DRAW, and
   that is the one visual deviation taken on purpose. The drawn chip is a static
   `div` on `--rd-surf2`; ours is a `button`. `_legacy-tokens.css` rules that
   anywhere `--rd-line` or `--rd-chip` is the SOLE indicator that something is
   interactive is a bug (WCAG 1.4.11, the boundary of a component wants 3:1), so
   a chip that became a control had to gain the control token with it. The
   selected state is NOT colour alone either — the chip carries `aria-pressed`.

   No border-radius anywhere: the Pro surfaces are square, and the only cut
   corner in the family is `.rd-pro-top__btn`'s chamfer, which this part
   borrows rather than redeclares.
   --------------------------------------------------------------------------- */

/* The TAB BODY: the screen's title over the two columns. `flex: 1`,
   `min-height: 0` and `overflow: auto` are the three properties every tab on
   this screen carries (`.rd-ev-roster`, `.rd-ev-scores`, `.rd-ev-overview`) —
   the strip and the head stay put and the body scrolls under them. It already
   had all three as a page body, which is why the fold changed the direction and
   not the box: the drawn 32px sides are the same 32px those tabs use, and the
   22/28 vertical pair is the drawing's own and is kept over their 18/32. */
.rd-rday {
  flex: 1;
  min-height: 0;
  overflow: auto;
  display: flex;
  flex-direction: column;
  padding: 22px 32px 28px;
}

/* DRAWN — `ScreenSquadBuilder`'s own title, which the fold moved out of
   `ProTop` and into the body rather than deleting. The tab reads `Squads`
   because the screen still says `Squads & Stages`; that is the designer's own
   reason for the short label. The drawn head face, stepped down so it reads as
   the screen's name under the event's name rather than competing with it. */
.rd-rday-title {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .04em;
  font-size: 13px;
  color: var(--rd-fg);
  margin-bottom: 16px;
}

/* The cold board's ground — the 26px no-claim fallback sweep, centred on
   nothing (`43 Loading States.html:41`, `rd-loading.jsx:183`). This screen's
   loaded body is a stages column beside a board of squad columns, and both
   counts belong to the fetch, so no skeleton shape can be claimed here; the
   sweep claims nothing and the title above it stands real. UNDRAWN as a
   wrapper: the drawing centres the mark and does not name the box. */
.rd-rday__wait {
  flex: none;
  display: flex;
  justify-content: center;
  padding: 48px 0;
}

/* The drawn two-column split, at the drawn 28px gap. It was `.rd-rday` itself
   until the title needed a line of its own above it. */
.rd-rday__split {
  flex: none;
  display: flex;
  gap: 28px;
  align-items: flex-start;
}

/* ---------------------------------------------------------------------------
   Left: the stages, at the drawn 300px.
   --------------------------------------------------------------------------- */
.rd-rday__stages {
  width: 300px;
  flex: none;
}

.rd-rday__board {
  flex: 1;
  min-width: 0;
}

/* Both section labels — "Stages, in order" and "Squads". */
.rd-rday-label {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

.rd-rday__stages .rd-rday-label { display: block; margin-bottom: 12px; }

.rd-rday-stage {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 12px;
  background: var(--rd-surf);
  margin-bottom: 6px;
}

/* The drawn `mono` is NOT a monospace family — it is Archivo at 800/75% with
   tabular figures, so a column of indices aligns without changing typeface. */
.rd-rday-stage__n {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--rd-fg3);
}

.rd-rday-stage__name {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ⚠️ UNDRAWN — ours. The drawn column carries a `⋮` per row and this page does
   not render it (stages are edited on the event form; see the page's own
   comment). A control removed without a word in its place is a gap; this note
   is the word. */
.rd-rday-note {
  margin-top: 14px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--rd-fg3);
  max-width: 44ch;
}

.rd-rday-link {
  color: var(--rd-acc);
  text-decoration: none;
  border-bottom: 1px solid var(--rd-line);
}
.rd-rday-link:hover { color: var(--rd-fg); border-bottom-color: var(--rd-ctrl); }
.rd-rday-link:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* "No stages yet." and "Nobody unassigned." — the quiet in-column absence. */
.rd-rday-none {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
  padding: 8px 0 2px;
}

/* ---------------------------------------------------------------------------
   Right: the squads, the pool, and the counts.
   --------------------------------------------------------------------------- */
.rd-rday-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  margin-bottom: 12px;
}

/* "24 enrolled · 8 unassigned", right-aligned as drawn. */
.rd-rday-count {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
}

.rd-rday-cols {
  display: flex;
  gap: 12px;
  align-items: flex-start;
}

/* Equal columns, one per squad. `min-width: 0` is the load-bearing line: a flex
   child defaults to `min-width: auto` and refuses to shrink under a long name,
   which is how six squads push the pool off the row. */
.rd-rday-col {
  flex: 1;
  min-width: 0;
  border: 1px solid var(--rd-line);
  background: var(--rd-surf);
  padding: 13px 12px 15px;
}

/* The pool. Dashed and unfilled, as drawn — it is not a squad, and the border
   is the only thing on the board that says so. */
.rd-rday-col--pool {
  border: 1px dashed var(--rd-ctrl);
  background: none;
}

.rd-rday-col__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 11px;
}

.rd-rday-col__title {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .04em;
  font-size: 12.5px;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rd-rday-col__count {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  flex: none;
}

/* ⚠️ UNDRAWN — ours. The drawn squad head is a static name and a static bay;
   editing them is invented, so the inputs are dressed to READ as the drawn text
   until they are focused. The name keeps the drawn 12.5px/800 display face and
   the bay the drawn 11px body face; both take a `--rd-field` ground and a
   `--rd-ctrl` boundary, because a control's boundary is what says it is one. */
.rd-rday-name,
.rd-rday-bay {
  font-family: 'Archivo', system-ui, sans-serif;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  padding: 5px 7px;
  min-width: 0;
}
.rd-rday-name {
  flex: 1;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .04em;
  font-size: 12.5px;
  color: var(--rd-fg);
}
.rd-rday-bay {
  width: 84px;
  flex: none;
  font-size: 11px;
  color: var(--rd-fg3);
}
.rd-rday-name::placeholder,
.rd-rday-bay::placeholder { color: var(--rd-fg3); }
.rd-rday-name:focus,
.rd-rday-bay:focus { border-color: var(--rd-acc); outline: none; }

/* ---------------------------------------------------------------------------
   The chips.
   --------------------------------------------------------------------------- */

/* A chip and, when it is the selected one, its two order controls. */
.rd-rday-chip-row {
  display: flex;
  align-items: stretch;
  gap: 3px;
  margin-bottom: 4px;
}

/* ⚠️ UNDRAWN AS A CONTROL. The fill, the padding and the type are the drawing's
   static chip; the boundary and the focus ring are ours, and the banner above
   says why a chip that became a button had to gain them. */
.rd-rday-chip {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 6px;
  text-align: left;
  padding: 8px 9px;
  background: var(--rd-surf2);
  border: 1px solid var(--rd-ctrl);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg2);
  cursor: pointer;
}

/* The name carries the truncation the chip used to carry itself. It moved when
   the chip gained a second child: with `text-overflow` on the button, a long
   name pushes the moving cue out of the box it is supposed to be labelling. */
.rd-rday-chip__name {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ⚠️ UNDRAWN — the 14 Aug addition, and it is deliberately a WORD. *"An
   operator who selects a chip and looks away needs the board to still be
   telling them what will happen"*, so the state is not carried by the accent
   wash alone (1.4.1) — and because it sits inside the chip's own button, it is
   part of the accessible name rather than a visual-only flourish. Accent, at
   the smallest display size on the board, and `flex: none` so it is the name
   that gives way and never the state. */
.rd-rday-chip__moving {
  flex: none;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .1em;
  font-size: 9px;
  color: var(--rd-acc);
}
.rd-rday-chip:hover { color: var(--rd-fg); }
.rd-rday-chip:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* The pool's chips are the quieter drawn pair — `--rd-chip` under `--rd-fg3`. */
.rd-rday-chip--pool {
  background: var(--rd-chip);
  color: var(--rd-fg3);
}

/* Selected. Accent wash plus an accent boundary — and `aria-pressed` on the
   element itself, so the state is not carried by colour alone. */
.rd-rday-chip--on {
  background: var(--rd-acc-wash);
  border-color: var(--rd-acc);
  color: var(--rd-fg);
}

/* ⚠️ UNDRAWN — ours. Shooting order within a squad, offered only on the
   selected chip. Square, quiet, and sized to the chip's own height rather than
   to a touch target: this is the desk screen of the document, and the tablet
   family that needs 68px is Phase B. */
.rd-rday-ord {
  flex: none;
  width: 22px;
  background: var(--rd-surf2);
  border: 1px solid var(--rd-ctrl);
  color: var(--rd-fg2);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
}
.rd-rday-ord:hover:enabled { color: var(--rd-fg); border-color: var(--rd-fg3); }
.rd-rday-ord:disabled { color: var(--rd-line); border-color: var(--rd-line); cursor: default; }
.rd-rday-ord:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* ⚠️ UNDRAWN — ours, blessed 14 Aug. The placement target. A column cannot
   itself be the button (its chips are buttons, and nesting them is invalid), so
   the target is a control at the column's foot that appears only while a chip is
   selected. Its label names the column it moves to rather than reading `Move
   here`, so the sizing has to survive a squad name: the label wraps inside the
   button rather than the button growing, which is what `width: 100%` and the
   column's own `min-width: 0` already gave it. */
.rd-rday-place {
  display: block;
  width: 100%;
  margin-top: 9px;
  padding: 7px 8px;
  background: none;
  border: 1px dashed var(--rd-ctrl);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-fg3);
  cursor: pointer;
}
.rd-rday-place:hover { color: var(--rd-acc); border-color: var(--rd-acc); }
.rd-rday-place:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* ⚠️ UNDRAWN — ours. Offered only on a squad with nobody in it, so this is
   never a destructive control in disguise: full-replace semantics mean removing
   an empty column deletes a row that holds no memberships. */
.rd-rday-remove {
  display: block;
  margin-top: 9px;
  background: none;
  border: 0;
  padding: 0 0 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  border-bottom: 1px solid var(--rd-line);
  cursor: pointer;
}
.rd-rday-remove:hover { color: var(--rd-danger); border-bottom-color: var(--rd-danger); }
.rd-rday-remove:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* ⚠️ UNDRAWN — ours. A narrow column-shaped door after the last squad. Narrow
   rather than equal-width, so adding one never re-proportions the board. */
.rd-rday-add {
  flex: none;
  align-self: stretch;
  width: 108px;
  background: none;
  border: 1px dashed var(--rd-ctrl);
  padding: 13px 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-fg3);
  cursor: pointer;
}
.rd-rday-add:hover { color: var(--rd-acc); border-color: var(--rd-acc); }
.rd-rday-add:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* ⚠️ UNDRAWN — ours. The empty board, in the space the squads would take.
   `RdEmptyState` centres inside whatever box it is given and takes `flex: 1` to
   claim that box's height, so it needs a flex box with a height to claim — this
   is that box. */
.rd-rday-empty {
  flex: 1;
  min-width: 0;
  align-self: stretch;
  min-height: 180px;
  display: flex;
  border: 1px solid var(--rd-line);
}

/* The drawn foot copy: the sentence that is also the server's refusal. */
.rd-rday-foot {
  margin-top: 16px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--rd-fg3);
  max-width: 66ch;
}

/* ---------------------------------------------------------------------------
   One class that dresses a component owned elsewhere.
   --------------------------------------------------------------------------- */

/* ⚠️ UNDRAWN — ours. `.rd-pro-top__btn` (ADM1, `_legacy-events-list.css`) has no
   disabled state because no caller had ever needed one; Save is disabled until
   the board is dirty. Declared here as a SECOND class rather than as a modifier
   of that block, because this part does not own it and must not override any of
   its declarations — this adds one selector that block never had.

   Since the 14 Aug fold the button it dresses is mounted by `event_detail.rs`,
   in the event's own `ProTop`, and the dirty state crosses the boundary as
   `squads::SaveState`. The rule stays here with the rest of the builder: it
   exists for this screen's Save and nothing else uses it. */
.rd-rday-save:disabled {
  background: var(--rd-chip);
  color: var(--rd-fg3);
  cursor: default;
}

/* `.rd-rday-door` WAS HERE — the quiet `Squads & Stages` link on the event's
   Overview. Deleted 14 Aug with the link itself: the entry door is a tab now,
   and a rule kept for a class nothing names is exactly the dead weight
   `rangeday check css` exists to find. */

/* ===========================================================================
   — TABLET FAMILY —
   ===========================================================================

   The rest of `36 Pro Actual Range Day`: `ScreenRODay`, `ScreenSquadBoard` and
   `ScreenStandings`, drawn at 1194 × 834. The promise the banner at the top of
   this file made — *"When the tablet frames land they append below, under their
   own sub-heading"* — kept here rather than in a second part file. One document,
   one section.

   ⚠️ THESE SCREENS ARE IN THE SHOOTER APP, ruled 14 Aug. The part name still
   reads ADM, which is now a statement about the DOCUMENT and not about the app:
   the builder above is a console tab and everything below it is shooter-side,
   and splitting the file along that seam would put one drawing in two places
   for a reason the drawing itself calls a split of time rather than of role.

   ⚠️ TOUCH TARGETS ARE 68px, NOT 44, and the drawing argues it rather than
   asserting it: *"The mobile minimum assumes a bare finger indoors on a phone
   you are looking at. This is a gloved thumb, in sun, on a tablet, between
   shooters — and the cost of a mis-tap is a wrong score on somebody's record."*
   So the bar's exit, the Today rows and the squad cards are all at or above it.

   ⚠️ THE SCORING SCREEN'S CONTROLS ARE NOT SCALED HERE, and that is a refusal
   rather than an omission. `ScreenROScore` is the SHOOTER's board with a header
   on it, and its pads, its time field and its `Save & Next` are `.rd-board__*`
   and `.rd-btn` — declared in `s09-board.css` (owner: 1-track3, since
   2026-08-21) and `_legacy-kit-chamfer-button.css`, which is UNOWNED and
   DELETE-ONLY: no lane may add to it, and a `.rd-rday-app` descendant override
   of the board's row rule here would be this part overriding another's
   declarations, which the note at the foot of the builder section above already
   forbids for one class. Reported to design rather than worked around.

   ⚠️ THAT SENTENCE USED TO SPELL THE SELECTOR, AND THE SPELLING WAS A HOLE.
   `check css` resolves every class the page sources use by searching the
   ASSEMBLED sheet as text, so this comment — concatenated into `style.css` like
   any other byte — was the only occurrence of that class name once the board's
   real rules moved to `s09-board.css`. Measured, not reasoned: with the part
   removed the sweep reported 49 missing classes and NOT that one, because this
   line answered for it. `_legacy-pre-kit.css` wrote the same warning at its own
   moved rule; this is the second instance, and it is why the pointer left in
   `_legacy-kit-components-and-screens.css` names the file and no selector.

   ⚠️ THE FIRST HALF OF THAT CITATION MOVED, AND THE REFUSAL DID NOT. The board
   rules were in `_legacy-kit-components-and-screens.css` when this was written,
   so the argument then ran "no lane may add to it" — a fact about the FILE. It
   is now an owned part, and the refusal survives on the other half of the same
   sentence, which was always the load-bearing one: overriding another owner's
   declarations from here is the thing forbidden, and s09-board.css is another
   owner's. Corrected in place by that part's author rather than left standing,
   because a citation that names the wrong file is how the next reader concludes
   the constraint is gone.

   ⚠️ NO WIDTH BREAKPOINT. The kit carries only hover queries and this section
   adds none: the family is TABLET-FIRST by the drawing's own exception ruling —
   *"it is drawn at iPad landscape rather than scaled down from 1280"* — so the
   geometry below is the 1194 layout stated plainly, and it degrades by flexing
   rather than by switching. The one place a narrower device would need a rule is
   the three-column squad grid, and `auto-fit` answers that without a query.

   ⚠️ NO BORDER-RADIUS, as everywhere in this file — and the drawn cut corners
   on the Today rows and the squad cards are not one. `tcham(9)` is a CHAMFER,
   which `RdChamferBox` composes out of two clipped layers; a `border-radius`
   here would be a rounded corner wearing the drawing's number. So those two
   surfaces are a bare `<button>` around that primitive, and the classes below
   split accordingly: the `--host` class is the button reset that carries the
   press and the focus ring, and the `__inner` class is the layout the primitive
   hands it. Neither declares a border — the chamfer IS the border, painted by
   the box's `stroke`, which is also how the finished card takes its accent.
   --------------------------------------------------------------------------- */

/* The frame. `100dvh`, never `100vh`: on a mobile browser `100vh` is the
   URL-bar-collapsed height, so the bar's 68px exit would sit below the fold
   until you scrolled — the same reasoning `.rd-shell` states, and the reason
   this family can own its own root without repeating that mistake. */
.rd-rday-app {
  height: 100dvh;
  width: 100%;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--rd-bg);
  font-family: 'Archivo', system-ui, sans-serif;
}

/* Everything under the bar. The drawn page padding is `28px 32px` on the Today
   list and `26px 32px` on the two boards; 26 is taken for both, because the
   difference is a drawing artifact and a family that shifts 2px between screens
   reads as a bug. */
.rd-rday-page {
  flex: 1;
  min-height: 0;
  overflow: auto;
  padding: 26px 32px 32px;
}

/* ⚠️ UNDRAWN — ours, and it is a cap on OUR container rather than on a shared
   component. The standings body (`.rd-stand`) is the shooter's, built for a
   276px column, and the drawn Pro row is four columns at 1194. This lane may not
   restyle a body two surfaces mount, so the column it sits in takes a width
   instead. FLAGGED: the drawn Pro geometry is a real difference and closing it
   belongs to that component. */
.rd-rday-page--column { max-width: 760px; }

/* ---------------------------------------------------------------------------
   The bar — the drawing's `MDBar`.
   --------------------------------------------------------------------------- */

/* Drawn: `14px 22px`, a 16px gap, a `--rd-line` rule beneath and `--rd-surf`
   under it. `flex: none` so the page below owns the scroll. */
.rd-rday-bar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px 22px;
  border-bottom: 1px solid var(--rd-line);
  background: var(--rd-surf);
}

.rd-rday-bar__id { flex: none; min-width: 0; }

/* The event's name — the drawn 14px display face at 800/78%. */
.rd-rday-bar__title {
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .03em;
  font-size: 14px;
  color: var(--rd-fg);
}

/* `T-Rex Arms · Range Officer`. Half of what marks the RO's official capacity —
   the org mark beside it is the other half — so it is quiet but never absent. */
.rd-rday-bar__role {
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .1em;
  font-size: 9px;
  color: var(--rd-fg3);
  margin-top: 4px;
}

/* Where you are — `Squad 1 · Bay 3`, `Standings`. Accent, behind a divider. */
.rd-rday-bar__where {
  flex: none;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .06em;
  font-size: 12px;
  color: var(--rd-acc);
  border-left: 1px solid var(--rd-line);
  padding-left: 16px;
}

.rd-rday-bar__gap { flex: 1; }

.rd-rday-bar__net {
  flex: none;
  display: flex;
  align-items: center;
  gap: 9px;
}

/* The drawn 8px square. Square, not a disc — nothing in Pro is round.

   ⚠️ THREE DOTS, ONE RULE — DRAWN 17 Aug with the bar's third state: *"volt when
   everything is on the server, quiet when work is in hand and moving, amber when
   it cannot move. An RO reads the colour before the words."* `--quiet` is the
   third, and it is the one we did not have: an online tablet with a queue still
   draining used to wear the volt dot, which asserts the opposite of what is
   true. Tertiary rather than a fourth colour, because the state it paints is at
   rest — nothing failed and nothing needs doing. */
.rd-rday-bar__dot {
  width: 8px;
  height: 8px;
  flex: none;
  background: var(--rd-acc);
}
.rd-rday-bar__dot--quiet { background: var(--rd-fg3); }
.rd-rday-bar__dot--warn { background: var(--rd-warn); }

/* ⚠️ THE STATE IS A WORD, and the drawing insists on it: *"The bar reads
   'Saved', not a cloud icon… that deserves a word rather than a symbol they have
   to interpret while holding a timer."* The dot beside it is decoration, which
   is why the word is never omitted and the dot is `aria-hidden`. The word stays
   tertiary across both at-rest states and only the dot moves between them —
   drawn that way, and it is what keeps the colour rule a glance rather than a
   second thing to read. */
.rd-rday-bar__state {
  font-size: 12px;
  color: var(--rd-fg3);
  white-space: nowrap;
}
.rd-rday-bar__state--warn { color: var(--rd-warn); }

/* ⚠️ 68px, AND THE DRAWING CORRECTED ITSELF TO GET HERE: *"It was 14px tall —
   the smallest thing on a screen that argues 44 is too small, and the one
   control whose whole point is that it does not change with the device."* The
   negative margins are the drawn ones: they let a 68px control sit inside a bar
   whose own padding is 14, without the bar growing to 96. */
.rd-rday-bar__back {
  flex: none;
  display: flex;
  align-items: center;
  gap: 8px;
  height: 68px;
  margin: -14px 0;
  padding: 0 4px 0 16px;
  border: 0;
  border-left: 1px solid var(--rd-line);
  background: none;
  font-family: inherit;
  cursor: pointer;
}
.rd-rday-bar__arrow { color: var(--rd-acc); font-size: 14px; }
.rd-rday-bar__exit { font-size: 14px; color: var(--rd-fg2); }
.rd-rday-bar__back:hover .rd-rday-bar__exit { color: var(--rd-fg); }
.rd-rday-bar__back:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: -2px; }

/* ---------------------------------------------------------------------------
   Head rows, shared by the two boards.
   --------------------------------------------------------------------------- */
.rd-rday-head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-bottom: 6px;
}

/* Drawn at 26px on Today and 24px on the two boards; one value, for the reason
   the page padding takes one. */
.rd-rday-h1 {
  font-weight: 800;
  font-stretch: 72%;
  text-transform: uppercase;
  letter-spacing: .02em;
  font-size: 25px;
  line-height: 1;
  color: var(--rd-fg);
  margin: 0;
}

/* `5 stages`, beside the heading and on its baseline. */
.rd-rday-head__count { font-size: 13px; color: var(--rd-fg3); }

.rd-rday-head__gap { flex: 1; }

/* `Sat 8 Aug` — the drawn date under `Today`. */
.rd-rday-when {
  font-size: 13px;
  color: var(--rd-fg3);
  margin-bottom: 26px;
}

/* ⚠️ DRAWN 17 Aug — the cross-link between the day's two boards, and the reason
   it was ours first is now the drawing's own: *"ScreenStandings had no drawn door
   anywhere, which made a whole drawn screen unreachable from the surface an RO
   stands in front of."* It lands as *"quiet accent text at the end of the head
   line, mirrored — text rather than a button because it is a switch of view, not
   an act on the day."* Type is the drawn 700 / 78% / 10px at `.08em`, which is
   the one value that moved: we shipped `.1em`.

   ⚠️ THE UNDERLINE IS STILL OURS, and it is the kit outranking the drawing
   rather than a disagreement with it. Accent text alone is colour carrying the
   whole of *this is a control*, which the kit refuses — the same call, on the
   same document, that the drawing already blessed for the builder's shooter
   chips: *"a chip tone as the sole indicator of interactivity is a 1.4.11 failure
   by the kit's own rule. One token, and the kit outranks the drawing."* The 68px
   height is ours too, for the reason every target on this family is 68. */
.rd-rday-jump {
  flex: none;
  height: 68px;
  padding: 0 4px;
  border: 0;
  background: none;
  font-family: inherit;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .08em;
  font-size: 10px;
  color: var(--rd-acc);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.rd-rday-jump:hover { color: var(--rd-fg); }
.rd-rday-jump:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: -2px; }

/* ---------------------------------------------------------------------------
   Shared states: loading, failure, and the honest empties.
   --------------------------------------------------------------------------- */
.rd-rday-load {
  font-size: 13px;
  color: var(--rd-fg3);
  padding: 8px 0;
}

/* A transport failure, above whatever is already on screen and never instead of
   it — `admin/events.rs`'s rule, and the standings board's: a squad at a bay
   that loses signal must not lose the board it was reading. */
.rd-rday-fail {
  border-left: 2px solid var(--rd-danger);
  padding: 4px 0 4px 16px;
  margin-bottom: 18px;
  font-size: 13px;
  line-height: 1.55;
  color: var(--rd-fg2);
  max-width: 66ch;
}

/* ⚠️ BLESSED AS SHIPPED, 17 Aug — the four states the drawing never reaches: a
   Today list with nothing on it, a cold open with no signal and no cache, an
   account with no staff role, and a role that could not be confirmed. Ruled as
   part of the ten interim marks and kept word for word — *"in every case the
   sentence names a fact I would otherwise have had to reconstruct."* The frame
   is ours and unremarked: deliberately not `RdEmptyState`, because that
   primitive centres in a box and draws its title in the phone kit's display
   face, and these sit at the top of a 1194 page where a centred block reads as
   a modal. */
.rd-rday-state { padding: 10px 0 0; max-width: 62ch; }
.rd-rday-state__title {
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .04em;
  font-size: 15px;
  color: var(--rd-fg);
  margin-bottom: 8px;
}
.rd-rday-state__body { font-size: 13px; line-height: 1.6; color: var(--rd-fg3); }

/* ⚠️ UNDRAWN — ours. An event with an empty card, said in `event_detail.rs`'s
   own words so the product explains a drill-less event once. */
.rd-rday-note {
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--rd-fg3);
  max-width: 66ch;
  margin-bottom: 18px;
}

/* ---------------------------------------------------------------------------
   1 — Today. `ScreenRODay`.
   --------------------------------------------------------------------------- */
.rd-rday-rows { display: flex; flex-direction: column; gap: 12px; }

/* The press. A bare button around the chamfered box: no ground and no boundary
   of its own, because both belong to the box inside it. */
.rd-rday-row {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}
.rd-rday-row:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* Drawn `22px 20px` with a 20px gap. The min-height is ours and it is the 68px
   rule applied to a row whose drawn padding already clears it — stated so a
   shorter row can never be introduced by a copy change. */
.rd-rday-row__inner {
  display: flex;
  align-items: center;
  gap: 20px;
  min-height: 68px;
  padding: 22px 20px;
}

/* ⚠️ THE BAY TAKES THE SLOT THE TIME USED TO HOLD, corrected 14 Aug. The drawn
   96px column stays; what fills it is real data now. */
.rd-rday-row__bay {
  width: 96px;
  flex: none;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .04em;
  font-size: 15px;
  color: var(--rd-acc);
}

.rd-rday-row__copy { flex: 1; min-width: 0; display: block; }
.rd-rday-row__name {
  display: block;
  font-size: 17px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rd-rday-row__sub {
  display: block;
  font-size: 12.5px;
  color: var(--rd-fg3);
  margin-top: 5px;
}
.rd-rday-row__go { flex: none; font-size: 15px; color: var(--rd-fg3); }

/* The drawn foot sentence. */
.rd-rday-foot {
  font-size: 12px;
  line-height: 1.55;
  color: var(--rd-fg3);
  max-width: 58ch;
  margin-top: 20px;
}

/* ---------------------------------------------------------------------------
   2 — The squad board. `ScreenSquadBoard`.
   --------------------------------------------------------------------------- */

/* Drawn as `repeat(3, 1fr)` at a 16px gap. `auto-fit` with a floor instead: it
   IS three columns at the drawn 1194 and it is the one place a narrower tablet
   would otherwise need a width query — which this section has ruled itself out
   of. A fourth squad wraps rather than squeezing five names into 240px. */
.rd-rday-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 16px;
}

/* The press, on the same split as the Today row: the whole card is the door
   into the squad's next stage, and the box inside it owns the paint.

   ⚠️ Drawn: an `--rd-acc` boundary when every stage is complete. That lives on
   the box's `stroke` rather than in a modifier here, and it is NOT colour alone
   — the card's own line reads `All stages scored` beneath it. */
.rd-rday-card {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}
.rd-rday-card:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* ⚠️ A CARD WITH NOTHING LEFT TO SCORE IS NOT A CONTROL. The accent boundary
   already says the squad is finished; making it still press-able would promise
   a stage that does not exist. Same rule the grip on the shooter board follows
   — *"a handle on a fixed row is a control that does nothing."* */
.rd-rday-card:disabled { cursor: default; }

/* Drawn `20px 18px`, `min-height: 168`. */
.rd-rday-card__inner {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  min-height: 168px;
  padding: 20px 18px;
}

.rd-rday-card__name {
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .04em;
  font-size: 17px;
  color: var(--rd-fg);
}
.rd-rday-card__sub {
  font-size: 12.5px;
  color: var(--rd-fg3);
  margin-top: 6px;
}
.rd-rday-card__gap { flex: 1; min-height: 12px; }

/* The glance, not the answer — the drawing's own words for the strip. 5px tall
   segments at a 5px gap, one per stage on the event's card. `aria-hidden`,
   because the line under it says the same thing in words. */
.rd-rday-strip {
  display: flex;
  gap: 5px;
  margin-bottom: 12px;
}
.rd-rday-seg {
  flex: 1;
  height: 5px;
  background: var(--rd-chip);
}
.rd-rday-seg--on { background: var(--rd-acc); }

/* ⚠️ EVERY CARD NAMES A STAGE, including the one that has not started —
   *"'Not started' told an RO nothing they could act on."* */
.rd-rday-card__next { font-size: 13px; color: var(--rd-fg2); }

/* The drawn offline block: a warn rule, a lead and a sentence. It offers
   nothing to press, and that is the design — *"there is nothing useful a person
   can do about a hillside."* */
.rd-rday-off {
  margin-top: 24px;
  border-left: 2px solid var(--rd-warn);
  padding-left: 16px;
}
.rd-rday-off__lead { font-size: 14px; color: var(--rd-fg); }
.rd-rday-off__body {
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--rd-fg3);
  max-width: 62ch;
  margin-top: 6px;
}
/* ==== ADM-MOB · 46 (Mobile) Pro Destinations — the row stack · owner: lane-drills ==== */
/*
   Drawn in `rd-pro-mobile.jsx` (`ScreenProEvents`) under
   `46 (Mobile) Pro Destinations.html`. Components:
   apps/web/src/pages/admin/events.rs, which carries the vocabulary ruling and
   the enrolled-count divergence. Only the values live here.

   ⚠️ THIS IS THE DESTINATIONS PART, NOT THE SHELL'S. `adm0-mobile.css`
   (owner: lane-3, doc 45) owns the phone SHELL — `.rd-prom`, `.rd-prom__chrome`,
   `.rd-prom__drawer`, `.rd-prom__sheet` — and its anatomy comment names
   `.rd-prom__body` as "the Outlet, and the page's own ProTop inside it". What
   renders INSIDE that body is a different document (46) and a different lane, so
   this is a SIBLING part rather than a block appended to theirs. The two name
   spaces are disjoint as tokens: `rd-prom` is a BEM block whose elements are
   `rd-prom__*`; `rd-prom-list`, `rd-prom-owner` and `rd-prom-role` are their own
   blocks and neither file declares or overrides a selector the other names.
   (`rd-prom-row` was the third such block and is deleted — §2 below.)

   ANATOMY:

     [ .rd-prom-list — the row stack that replaces the desktop table ]
       [ RdRowLink — kit-row.css; the row and everything inside it ]

   ⚠️ NO `@media` IN THIS FILE. The breakpoint is asked ONCE, in Rust
   (`device::use_device_class`), and these rules paint what it mounted — the same
   call `adm0-mobile.css` and `s16-tablet.css` make, for the reason their order
   notes give: a second statement of the threshold in CSS drifts against the Rust
   one and the two disagree at exactly the boundary width.

   ⚠️ NO NEW TOKENS. Every colour below is one the sheet already declares.

   ⚠️ ONE TYPE FLOOR, applied not transcribed. `01 Foundations` §05 Type Scale,
   "Two floors, one exemption": mixed-case text never goes below 11px. The
   drawing's 10.5px mixed-case sub-line ships at 11. Marked at the site. The same
   call `adm0-mobile.css` made for its 10px personal note.
*/

/* ---------------------------------------------------------------------------
   1. The list container the phone arm mounts inside `.rd-adm-ev`.
   --------------------------------------------------------------------------- */
/* ⚠️ ONE OVERRIDE, AND IT IS WHY THIS PART SITS WHERE IT SITS. `.rd-adm-ev` is
   declared in `_legacy-events-list.css` with `padding: 4px 32px 32px` — the
   desktop console's 32px page gutter. The phone shell already carries its own
   18px gutter (`.rd-prom`), so the desktop value would double it and cost the
   row 64px of a 448px screen. Scoped under `.rd-prom` so the desktop list is
   untouched; (0,0,2,0) against the pool's (0,0,1,0), which wins on specificity
   alone, and this part sits after that file so it keeps winning if the
   specificity ever levels. */
.rd-prom .rd-adm-ev {
  padding: 0 0 24px;
}

/* `ScreenProEvents` — `display: grid, gap: 8, marginTop: 14`. */
.rd-prom-list {
  display: grid;
  gap: 8px;
  margin-top: 14px;
}

/* ---------------------------------------------------------------------------
   2. The row is GONE FROM THIS FILE — batch 15 slice 13, the strand sweep.
   --------------------------------------------------------------------------- */
/* `.rd-prom-row`, `__main`, `__name`, `__name--past` and `__sub` are DELETED.
   They were the four-destination row idiom — Events, Members, Library,
   Locations and Progress all mounted them at phone width — and every one of
   those five screens now mounts `RdRow` / `RdRowLink` instead (batch 15 slices
   7-11). `kit-row.css`'s `.rd-row`, `__main`, `__name` and `__sub` carry the
   declarations verbatim; `drills_mobile.rs`'s `ruling_` tests diff the two
   rule-for-rule, which is where the "moved, not rewritten" claim is kept
   honest.

   ⚠️ TWO OF THE FIVE DIED EARLIER AND ARE RECORDED HERE RATHER THAN IN A
   COMMIT MESSAGE. `.rd-prom-row__chip` went in slice 7 (its three declarations
   — `flex: none; display: flex; align-items: center` — are `.rd-row__trail`'s,
   and the events row was its only emitter), and `.rd-prom-row__name--past`
   went dead in the same slice for the same reason; it was left standing then
   because that slice's file set stopped at the rule above it, flagged as a
   strand for this sweep. Both are swept here with the rest.

   ⚠️ THE RULINGS THEY CARRIED DID NOT DIE WITH THEM, and that is why this
   epitaph is longer than a deletion note. Three of them are now the kit's, and
   each is quoted where it landed rather than paraphrased:

     · the chamfer and the INSET ring — `clip-path` clips an `outline` away, so
       a chamfered row cannot use the house `outline` and takes
       `box-shadow: inset 0 0 0 2px var(--rd-acc)` instead. `kit-row.css` §3
       carries the polygon and the ring, quoting this file.
     · ONLY THE NAME steps down for a past event, never an opacity on the row —
       an opacity multiplier dims fg2 and fg3 with it and puts a 9px chip at
       2.9:1. `kit-row.css` §5 carries the warning verbatim, with this file
       named as its source.
     · the sub-line WRAPS rather than clipping, because it carries three facts
       joined by middots and an ellipsis drops one silently. `kit-row.css` §5
       carries the wrap, and Progress's opposite call (an ellipsis) is the
       visible change slice 11 recorded in `16-row-taxonomy.md` §10.

   ⚠️ `.rd-prom-list` STAYS AND IS NOT PART OF THIS SWEEP. The stack is not the
   row: it is `display: grid; gap: 8` with a `margin-top`, it has five live
   emitters (`events.rs:331`, `locations_mobile.rs:181,193`, `drills.rs:1010`,
   `members.rs:2378`), and the kit ships no list primitive for it to fold into.
   The same goes for `.rd-prom .rd-adm-ev` and `.rd-prom .rd-mem-body` above and
   below — those are the phone shell's gutter overrides, not row geometry, and
   the desktop classes they scope are still emitted.

   ⚠️ WHAT IS LEFT BELOW is per-screen, and the sweep deliberately did not take
   it: the Members search field, the two right-edge marks (`.rd-prom-owner`,
   `.rd-prom-role`, `--staff`) and the Library/Locations trailing slots are
   fills for `.rd-row__trail`, which is a slot precisely so a screen can keep a
   treatment the primitive must not learn. `16-row-taxonomy.md` §9.1 lists them
   by name. */

/* ---------------------------------------------------------------------------
   3. Members — `ScreenProMembersM`, in `rd-pro-mobile-pages.jsx`.
   --------------------------------------------------------------------------- */
/* The row idiom is shared verbatim, and since the sweep above it is shared
   through the KIT: `.rd-prom-list` here, `RdRowLink` for the row itself. What
   Members adds is the search field that leads it and the two right-edge marks
   that fill `.rd-row__trail`. (The count line under it was `.rd-prom-count` and
   is `RdRowNotice` now — slice 8; the note at the foot of this file says why.)

   ⚠️ ONE RECONCILED VALUE. `ScreenProMembersM` sets the stack `gap: 7` where
   `ScreenProEvents` sets `gap: 8`; both are rows of one document and one row
   idiom, and a 1px difference between two screens of the same pass is the
   drawing disagreeing with itself rather than a distinction to build. The 8 is
   kept for both. Recorded rather than silently averaged. */

/* ⚠️ TWO OVERRIDES OF THE DESKTOP GUTTER, and they are why this part sits after
   `adm-mem.css`. That file declares `.rd-mem-body { padding: 0 32px }` and
   `.rd-mem__banner { margin: 16px 32px 0 }` — the desktop console's 32px page
   gutter. The phone shell already carries its own 18px (`.rd-prom`), so the
   desktop values would double it and cost the row 64px of a 448px screen.
   Scoped under `.rd-prom` so the desktop list is untouched; (0,0,2,0) against
   the pool's (0,0,1,0), which wins on specificity alone. Same call, same
   wording, as `.rd-prom .rd-adm-ev` above. */
/* ⚠️ **CORRECTED 2026-08-25.** This read: "`position: relative` IS
   LOAD-BEARING AND IT IS THE DESKTOP'S OWN REASON. `RdConfirmModal`'s scrim
   is absolutely positioned and resolves against the nearest positioned
   ancestor; without a positioned pane the scrim escapes to the viewport and
   covers the phone shell's chrome — the tab bar and the title row — instead
   of the list it is asking about."

   `.rd-confirm` is `position: fixed` now (adm1-5-1.css) and does not resolve
   against this element at all any more. `adm-mem.css` keeps the matching
   declaration on the desktop pane as a reversal path, not a live contract —
   see its own 2026-08-25 correction. It arrived here with the removal
   confirm (capability parity) and is not a gutter value; kept for the same
   reason its desktop twin is kept. */
.rd-prom .rd-mem-body {
  padding: 0;
  position: relative;
}
.rd-prom .rd-mem__banner {
  margin: var(--rd-banner-gap) 0 0;
}

/* ⚠️ THE ACT KEEPS ITS PALETTE AND TAKES THE DRAWN GEOMETRY. `ProMobileScreen`
   draws the page act as `minHeight: 40, padding: '0 14px'` with a 1.5px accent
   OUTLINE and accent text; `.rd-mem-invite` is a volt fill, and its own note in
   `adm-mem.css` says why — in light mode `--rd-acc` is the deep warm brown reserved
   for accent TEXT and puts black on 2.3:1 as a fill. Pro's primary action is
   volt everywhere in the product, which is a rule rather than a layout, so
   `46`'s own rule 1 keeps it: the geometry here is the drawing's, the palette is
   the product's. The 40px minimum is the tap target and it is the half that has
   to travel. */
.rd-prom .rd-mem-invite {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding: 0 14px;
}

/* `MSearch` — `TOut` with `stroke: t.line`, `fill: t.field`, `r: 9`,
   `margin: '14px 0 10px'`, `inner: { padding: '10px 12px' }`, hint at
   `tbody` 12.5px `t.fg3`.

   A real `input` rather than the drawing's static outline: this box filters, and
   the desktop's `.rd-mem-search` is an input already. The chamfer is the drawn
   `r: 9`, transcribed as the same `clip-path` polygon the row above uses. */
/* ⚠️ THE CHAMFER IS A COMPONENT NOW, NOT A CLIP-PATH. `r: 9` used to be
   transcribed here as a `clip-path: polygon(…)` — the drawn geometry written a
   SECOND time, in a form that eats the focus outline and therefore needed an
   inset box-shadow to fake one back. `RdSearchField` mounts `RdChamferBox` at
   `chamfer: 9`, which draws a real stroke layer, so the accent stroke IS the
   focus indicator and the fake comes out with the clip.

   The drawn margin and the drawn type size stay here: the wrapper carries the
   `14px 0 10px`, the input carries the 12.5px. Everything else — fill, stroke,
   padding, placeholder colour, focus — is `au5.css`'s. */
.rd-prom-searchwrap {
  margin: 14px 0 10px;
}

.rd-prom-search {
  font-size: 12.5px;
}

/* ⚠️ THE SEARCH LEADS, SO IT OWNS THE GAP TO THE LIST. `.rd-prom-list` carries
   `margin-top: 14` for the Events screen, where the stack follows the title row
   directly; on Members the search field sits between them and closes on the
   drawn 10. A general sibling rather than an adjacent one because the branch
   between them renders as a fragment, so the two are siblings without being
   adjacent in the markup.

   ⚠️ ON THE WRAPPER, not the input: the input is inside the field's own tree
   now, so it is no longer a sibling of the list at all and this selector would
   silently stop matching. */
.rd-prom-searchwrap ~ .rd-prom-list {
  margin-top: 0;
}

/* ---------------------------------------------------------------------------
   3.1 The right edge: the owner mark, then the role.
   --------------------------------------------------------------------------- */
/* Both are `pdisp(700, 78, '.1em')` at 8.5px in the drawing — condensed
   uppercase with tracking, so `01 Foundations` §05's OTHER floor applies and
   9.5px is the bottom. Shipped at 9.5. Same call `adm0-mobile.css` made for its
   own 8.5px tracked labels.

   `flex: none` so neither mark compresses: the left cell is the one that gives
   way, which is the whole no-horizontal-scroll claim. */
/* ⚠️ TWO RULES, NOT ONE GROUPED SELECTOR. `rangeday check css`'s
   class-resolution pass looks for a declaration the class OPENS — `.token {` —
   so a token that only ever appears in the middle of a selector list resolves to
   nothing and the gate reads a live class as unlisted. Written out rather than
   grouped for that reason alone; the two blocks are identical on purpose. */
/* ⚠️ `margin-inline-end: 12px` IS THE ROW'S OWN GAP, MOVED — NOT A NEW NUMBER.
   Both marks used to be direct children of `.rd-prom-row`, so the row's
   `gap: 12` sat between them. They are one `trail` slot now
   (`16-row-taxonomy.md` §9.1), and `.rd-row__trail` is a flex box with NO gap —
   the row's 12 still separates the left cell from the edge, but inside the edge
   the two marks would butt together. It rides the OWNER because the owner is the
   conditional mark and always precedes the role, so a row without one needs no
   spacing at all. */
.rd-prom-owner {
  flex: none;
  margin-inline-end: 12px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  text-transform: uppercase;
  font-size: 9.5px;
  white-space: nowrap;
  color: var(--rd-fg3);
}

.rd-prom-role {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  text-transform: uppercase;
  font-size: 9.5px;
  white-space: nowrap;
  color: var(--rd-fg3);
}

/* ⚠️ QUIET IS THE BASE AND STAFF ADDS THE ACCENT — the drawing's own
   `role === 'Member' ? t.fg3 : t.acc`, written with the quiet side as the
   fall-through so the modifier is ADDED to a base rather than swapped for a
   second one. `role_mark_class` in `members.rs` states the ratchet that requires
   that shape.

   ⚠️ AND `Owner` NEVER TAKES IT. `34 Pro Members`: ownership is a fact about a
   person, not a state they are currently in, and the drawing paints the mark
   `t.fg3`. The owner's row carries two marks and only the role half can light. */
.rd-prom-role--staff {
  color: var(--rd-acc);
}

/* ⚠️ `.rd-prom-count` IS GONE — IT IS `RdRowNotice` NOW (batch 15 slice 8), and
   `16-row-taxonomy.md` §9.1 sends it there by name: "`.rd-prom-count`
   (`adm-mob.css:294-300`) is the Members foot and folds into `RdRowNotice`
   instead." It is one of the eight drawn feet the primitive was surveyed from
   (`rd-pro-mobile-pages.jsx:38`, `Showing 6 of {ORG_MEMBERS}`), and unlike the
   two marks above it carries no ruling the primitive cannot express.

   Three declarations differ and not one of them can show on this sentence: the
   kit's `margin-top: 10px`, `font-size: 11px` and `--rd-fg3` are this rule's own
   values, and its `max-width: 70ch`, `line-height: 1.55` and
   `text-wrap: pretty` are inert on one short line that never wraps. */

/* ---------------------------------------------------------------------------
   3.2 The Members / Invited strip — the desktop's tab band, as chips.
   --------------------------------------------------------------------------- */
/* ⚠️ NOT A NEW TREATMENT, AND NOT `ProTabs` EITHER. The desktop strip is a 32px
   band with a rule under it, drawn for a head with 1100px to spend. The phone
   already has one drawn answer for "the desktop's tabs, at this width" — the
   Library's kind chips (`ScreenProLibraryM`), which `adm-lib-mob.css` ships as
   `.rd-promd__kind`. Every declaration below is that chip's, transcribed: the
   drawn `display: flex, gap: 8, margin: '14px 0 10px'` strip, the
   `padding: '7px 12px'` chip with its 1.5px `t.ctrl` border and `tcham(8)`
   chamfer, `pdisp(800, 78, '.08em')` at 9.5px on `t.fg2`, and the accent pair
   for the lit one.

   ⚠️ TWO SPELLINGS OF ONE CHIP AND NOT ONE SHARED CLASS, DELIBERATELY. The
   obvious tidy is to name `.rd-promd__kind` here. It is refused for the reason
   that part's own header gives about the row it deleted: `rd-promd` is the
   LIBRARY block, and a Members screen reaching into it makes a Library restyle
   a Members regression. If a third destination wants this chip it becomes a kit
   primitive with a spec; two is the number at which the corpus says to wait.

   ⚠️ THE `--on` PAIR IS BASE + MODIFIER, never two classes swapped, so a state
   change cannot re-base the element — `the_branching_classes_are_whole_and_
   distinct` asserts `on.starts_with(off)` and `member_tab_chips` writes both
   literals out whole for #467.

   ⚠️ **THE STROKE IS TWO LAYERS, AND THE ONE-LAYER SPELLING WAS THE 26 AUG
   STAGING DEFECT.** James, `/admin/members` at phone width: the chips drew an
   EMPTY GAP at the cut — the corner removed and no edge across it. Both strips
   below, and the Library chip they were transcribed from, put a
   `border: 1.5px solid` UNDER a `clip-path`, and the clip removes the corner
   INCLUDING the border drawn on it. `48 Kit Rules`: *"a stroked chamfer must be
   two layers"*; `01 Foundations`:85; `chamfer_box.rs`'s header derives it, and
   `pro-kit.css`'s chip block carries the family's full note.

   The construction, identical in §3.2 and §3.3 and in `adm-lib-mob.css` §2:

     the ELEMENT   the outer layer, which IS the stroke — a box in the stroke
                   colour, clipped at the cut, its padding carrying the 1.5px
                   the border used to occupy, so the box is the same box.
     ::before      the surface, inset by the stroke width and clipped at
                   `inner_chamfer_px(13, 1.5)` = 12.12, which is what keeps the
                   stroke's thickness UNIFORM along the diagonal.

   A pseudo-element rather than a face span: the label is the button's own text
   child, and `z-index: -1` under `isolation: isolate` paints the surface at
   step 2 of the chip's stacking context — above the element's own background
   and below the inline label at step 5. `kit-tab-chip.css` §4 already draws its
   focus ring as a clipped layer the same way.

   ⚠️ **THE CUT IS 13, NOT 8.** `01 Foundations` §03, re-ruled 26 Aug 2026: 13 on
   controls, 14 on containers. Named through `--rd-chamfer-control` so it moves
   with `CONTROL_CHAMFER_PX` and every other control in the kit. **NO width query
   anywhere may re-cut a chip** — the tablet arm showed square corners for
   exactly that kind of divergence, and `the_chip_corner_is_width_independent`
   fails the sheet if one appears. */
.rd-prom-tabs {
  display: flex;
  gap: 8px;
  margin: 14px 0 10px;
}

.rd-prom-tab {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 0;
  background: var(--rd-ctrl);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--rd-chamfer-control)), calc(100% - var(--rd-chamfer-control)) 100%, 0 100%);
  /* The drawn `7px 12px` plus the 1.5px the border used to occupy. */
  padding: 8.5px 13.5px;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  font-size: 9.5px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--rd-fg2);
}

/* The surface. `--rd-hollow-fill` is the kit's outlined-control ground
   (`Fill::Hollow`), and it must be OPAQUE: a transparent surface lets the outer
   layer's stroke colour flood the chip. */
.rd-prom-tab::before {
  content: "";
  position: absolute;
  inset: 1.5px;
  z-index: -1;
  background: var(--rd-hollow-fill);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 12.12px), calc(100% - 12.12px) 100%, 0 100%);
}

/* The lit chip takes the accent as its STROKE, which is now the outer layer's
   fill. The treatment is unchanged; only the property spelling it moved. */
.rd-prom-tab--on {
  background: var(--rd-acc);
  color: var(--rd-acc);
}

/* ⚠️ AN INSET RING, NOT THE HOUSE `outline`: `clip-path` clips an `outline`
   away, so an outlined ring would draw nothing here. Same substitution
   `kit-tab-chip.css` §4 states and `adm-lib-mob.css` ships on the chip this one
   is transcribed from.

   ⚠️ IT RIDES THE SURFACE LAYER, NOT THE ELEMENT. An `inset` `box-shadow` paints
   with the element's own background — step 1 — which is the stroke layer, under
   the surface. Left on the element the ring would be painted over and focus
   would show nothing at all. */
.rd-prom-tab:focus-visible {
  outline: none;
}

.rd-prom-tab:focus-visible::before {
  box-shadow: inset 0 0 0 2px var(--rd-acc);
}

/* The count the desktop band carries (`ProTab::counted`). Tabular figures so
   the two chips do not change width as the numbers do, and the quiet step down
   so the WORD is what is read first. */
.rd-prom-tab__n {
  font-variant-numeric: tabular-nums;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   3.3 The role filter — the desktop bar's chips, at phone width.
   --------------------------------------------------------------------------- */
/* ⚠️ THE SAME DRAWN CHIP AS §3.2 AND A SECOND CLASS FAMILY ANYWAY. The two
   strips sit one above the other and answer different questions — which list am
   I on, which roles am I seeing — so a restyle of one must not silently restyle
   the other. The declarations are identical today and are allowed to diverge;
   what may not diverge is the drawn chip they both came from.

   ⚠️ THEY WRAP, AND NO SCROLLER IS BUILT. Five chips at `padding: 7px 12px`
   come to about two rows on a 412px body. A horizontal scroller fits them on
   one line and hides the last two behind a gesture with no affordance, on the
   control whose whole purpose is answering "who are my instructors" at a
   glance. `row-gap` matches the column gap so the second row reads as part of
   one strip. Undrawn either way — ADM-MOB2 §8.3 routes it. */
.rd-prom-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 10px;
}

/* The same two-layer stroke as §3.2 — the construction, the 13px cut and the
   reason a `border` under a `clip-path` cannot draw one are all in §3.2's
   banner. */
.rd-prom-chip {
  position: relative;
  isolation: isolate;
  border: 0;
  background: var(--rd-ctrl);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--rd-chamfer-control)), calc(100% - var(--rd-chamfer-control)) 100%, 0 100%);
  /* The drawn `7px 12px` plus the 1.5px the border used to occupy. */
  padding: 8.5px 13.5px;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  font-size: 9.5px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--rd-fg2);
}

.rd-prom-chip::before {
  content: "";
  position: absolute;
  inset: 1.5px;
  z-index: -1;
  background: var(--rd-hollow-fill);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 12.12px), calc(100% - 12.12px) 100%, 0 100%);
}

.rd-prom-chip--on {
  background: var(--rd-acc);
  color: var(--rd-acc);
}

/* The inset ring again, on the surface layer — `clip-path` clips an `outline`
   away and an element-level inset shadow would paint under the surface.
   §3.2's note. */
.rd-prom-chip:focus-visible {
  outline: none;
}

.rd-prom-chip:focus-visible::before {
  box-shadow: inset 0 0 0 2px var(--rd-acc);
}

/* ---------------------------------------------------------------------------
   3.4 The role sheet — the desktop's role menu, as a bottom sheet.
   --------------------------------------------------------------------------- */
/* ⚠️ THE DESKTOP MENU CANNOT TRAVEL, AND THE REASON IS GEOMETRY RATHER THAN
   TASTE. `.rd-mem-menu` is `position: absolute` inside a 170px role cell, and
   this width has no such cell — the role is one line of a right-edge stack.
   The phone's drawn answer for "pick one of a short closed list" is the bottom
   sheet the shell already ships (`.rd-prom__sheet`, the org picker in
   `adm0-mobile.css`), so this is that shape.

   ⚠️ SCOPED TO THIS SCREEN AND NOT REACHING INTO `.rd-prom__sheet`. That token
   is the SHELL's BEM element, owned by doc 45 and another lane; a Members
   screen naming it makes a shell restyle a Members regression, which is the
   same refusal §3.2 states about the Library's chip. The geometry below is the
   sheet's and the values are ones this sheet already declares. */
.rd-prom-sheet {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

/* A real `button`, sized to the whole surface — see the component for why it is
   not a `div` with an onclick. It paints the scrim and nothing else. */
.rd-prom-sheet__scrim {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, .55);
  border: 0;
  padding: 0;
  cursor: pointer;
}

.rd-prom-sheet__panel {
  position: relative;
  background: var(--rd-surf);
  border-top: 1px solid var(--rd-line);
  padding: 8px 18px var(--rd-sheet-clear);
}

/* ⚠️ THE BOTTOM WAS `calc(18px + env(safe-area-inset-bottom))` AND IS NOW THE
   KIT'S TOKEN. `48 Kit Rules.html`, Shell Geometry, James 25 Aug: *"Nothing
   tappable sits in the home-indicator zone … every bottom sheet ends with 40px
   under its last act"*. `--rd-sheet-clear` is `max(40px,
   env(safe-area-inset-bottom))`, declared in `kit-shell.css` §0 because the
   rule's own sentence puts the clearance in the kit so no screen has to
   remember it.

   Two things changed and both are corrections, not restyling: 18 -> 40, and
   `calc(+)` -> `max()`. The `calc` ADDED the device inset to the floor, so a
   notched phone got 18 + 34 = 52 where the ruled answer is "the floor, unless
   the device asks for more" — the shape every other clearance in this codebase
   already used (`.rd-tab-bar`, `.rd-prom__foot`).

   Taking the token does not reach into `.rd-prom__sheet`, which the note above
   refuses: both parts CONSUME a kit token, neither names the other's class. */

/* ⚠️ 56px, WHICH IS THE TAP TARGET AND NOT A LIST HEIGHT. `01 Foundations`
   floors a touch control at 44; a committed choice in a sheet the reader opened
   on purpose gets the roomier 56 the shell's own picker rows use. Left-aligned
   because these are options, not acts. */
.rd-prom-sheet__item {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 56px;
  padding: 0 4px;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 14px;
  text-align: left;
  color: var(--rd-fg);
}

/* The current role, as the desktop menu marks it — the accent, ADDED. */
.rd-prom-sheet__item--on {
  color: var(--rd-acc);
}

.rd-prom-sheet__item:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* The refusal line — the desktop menu's own footer, at the body floor of 11. */
.rd-prom-sheet__note {
  margin: 10px 0 0;
  font-size: 11px;
  line-height: 1.55;
  color: var(--rd-fg3);
}

/* A plain-text cancel, which is what a sheet with no Save needs: picking a role
   IS the act, so the only other thing a reader can want is out. */
.rd-prom-sheet__cancel {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 44px;
  margin-top: 6px;
  background: none;
  border: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg3);
}

.rd-prom-sheet__cancel:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* ---------------------------------------------------------------------------
   3.5 The page act, floating — 51 Shell Lab, Lists anatomy row 01.
   --------------------------------------------------------------------------- */
/* "Rows with a scannable right edge, filters above, one page act. On the phone
   the act floats bottom-right over the faded list end; on the tablet it holds
   the header."

   ⚠️ THE PALETTE IS `.rd-mem-invite`'S AND THE PLACEMENT IS THE LAB'S. Pro's
   primary action is volt everywhere in the product, which is a rule rather than
   a layout, so `46` rule 1 keeps it — the same call `.rd-prom .rd-mem-invite`
   above makes for the header form this replaces. What changed is where it sits.

   ⚠️ LOCAL TO MEMBERS. Every other Pro phone destination still holds its act in
   the header, and unifying them is a change to ADM0's title row in another
   lane's part. Routed as a designer question (ADM-MOB2 §8.4) rather than taken
   here — a float that only some screens have is a smaller inconsistency than a
   shell edit made from a Members PR.

   ⚠️ **THE FADE WAS A `::before` AND IT IS A SIBLING NODE NOW — the 2026-08-28
   defect.** This block used to read: *"THE FADE IS A `::before` AND NOT A
   SECOND NODE, so a screen cannot end up with the gradient and no act, or the
   act and no gradient. It is `pointer-events: none` and sits under the act's
   own stacking context, so it paints the list end and intercepts nothing."*

   The last clause was wrong in the way that matters: the pseudo sat INSIDE the
   act's stacking context, not under it. James found the Invite act painted over
   by its own gradient on staging `/admin/members` at phone width. Two separate
   mechanisms, either fatal on its own —

     - the `clip-path` below establishes a containing block for `position:
       fixed` descendants and clips them, so a full-width bottom-anchored
       pseudo was confined to this button's 44px box and never reached the list
       end at all;
     - `z-index: 30` makes this element a stacking context, and inside one a
       `z-index: -1` child paints ABOVE the context element's own background —
       so the app-ground gradient landed on the volt fill.

   A pseudo-element of a clipped, stacking act cannot be a full-bleed fade. The
   fade is `.rd-prom-fade` below, a real node, and the invariant the old note
   cared about is kept in the MARKUP instead: `members.rs` mounts the pair
   inside one `if can_manage && !org_is_closed` block, so neither can appear
   without the other.

   ⚠️ THE TWO LAYERS ARE THE KIT'S TOKENS NOW, `kit-shell.css` §0e. *"Acts are
   always above list fades"* (James, 2026-08-28) is a product rule, so the
   ordering is declared once for the whole sheet and no part writes a bare
   number for it. `--rd-z-act` IS the 30 this rule already carried. */
.rd-prom-fab {
  position: fixed;
  right: 18px;
  bottom: calc(18px + env(safe-area-inset-bottom));
  z-index: var(--rd-z-act);
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 18px;
  background: var(--rd-volt);
  color: var(--rd-on-volt);
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  text-transform: uppercase;
  font-size: 11.5px;
  text-decoration: none;
  clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
}

/* The faded list end the act floats over — the same 96px ramp of app ground the
   `::before` above described, as an element that can actually span the frame.

   The name ends in `-fade`, so the kit's §0e belt matches it without a second
   class in the markup, and this rule restates the two properties that belt
   guarantees anyway: a fade that swallowed a tap would take the row under it
   with it, and a fade above the act is the defect this replaced. */
.rd-prom-fade {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  height: 96px;
  z-index: var(--rd-z-fade);
  pointer-events: none;
  background: linear-gradient(to top, var(--rd-app-bg), transparent);
}

/* `clip-path` clips an `outline` away — the inset ring again, §3.2's note. */
.rd-prom-fab:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--rd-on-volt);
}

/* ⚠️ THE NAME IS THE DOOR AND A 13px LINE IS NOT A TAP TARGET. The row was an
   `RdRowLink` — the whole surface navigating — until it gained the acts a
   button cannot carry inside an anchor. `RdRowProps::name_to` makes the NAME
   the anchor instead, and `01 Foundations` floors a touch control at 44, so the
   name line claims it. It is a `block` in `kit-row.css`, so the height is free:
   nothing else moves. Scoped under `.rd-prom` — the desktop row's name is a
   pointer target and keeps its own line box. Designer question 7. */
.rd-prom .rd-row__name {
  display: flex;
  align-items: center;
  min-height: 44px;
}
/* ==== ADM-LIB-MOB · 46 (Mobile) Pro Destinations row 02 — the Org Library on the row stack · owner: lane-3 ==== */
/*
   Drawn in `components/rd-pro-mobile-pages.jsx:46` (`ScreenProLibraryM`) under
   `46 (Mobile) Pro Destinations.html` row 02. Component:
   apps/web/src/pages/admin/drills_mobile.rs, which carries every ruling; only
   the values live here.

   ⚠️ THE ROW IS NOT IN THIS FILE, AND ITS ABSENCE IS THE POINT.
   Batch 15, slice 9: the row is `kit-row.css`'s `.rd-row*` (`RdRow`, intent
   `Destination`, spec docs/design/kit/16-row-taxonomy.md). It was
   `adm-mob.css`'s `.rd-prom-row*` before that, and the two rules are
   declaration-identical — the same eight declarations of the designer's own
   `mrow` — so the migration moves no pixel. An even earlier draft of this part
   declared a THIRD spelling under `.rd-promd__*`; it went the day `adm-mob.css`
   landed. "Decided Here" #2 is one drawn rule ("tables become row stacks, and
   the row keeps the table's columns"), and two spellings of one rule is how two
   screens start disagreeing about a padding nobody meant to change.

   What is left is what row 02 draws and no other destination does:

     [ .rd-promd__kinds — the desktop tab strip, as chips ]
       [ .rd-promd__kind (+ --on) ]
     [ … .rd-prom-list (adm-mob.css) / RdRow → .rd-row* (kit-row.css) … ]
       [ .rd-row__trail — the kit's slot ]
         [ .rd-promd__edge — the row's own gap 12, restored ]
           [ .rd-promd__vis (+ --live) — the right-edge visibility word ]
           [ .rd-promd__act — the row's one action, Publish ]

   ⚠️ TWO OVERRIDES, AND THEY ARE WHY THIS PART SITS WHERE IT SITS. `.rd-adm-lib`
   and `.rd-adm-lib__search` are declared in `adm-lib.css` with the desktop
   console's 32px page gutter; the phone shell already carries its own 18px
   (`.rd-prom`), so the desktop values would double it. Scoped under `.rd-prom`
   so the desktop page is untouched; (0,0,2,0) against that part's (0,0,1,0),
   which wins on specificity alone, and this part sits after it so it keeps
   winning if the specificity ever levels. The idiom is `adm-mob.css`'s own
   `.rd-prom .rd-adm-ev`, one entry up.

   ⚠️ THE PANE CLASS IS THE DESKTOP'S, DELIBERATELY. The list pane stays
   `.rd-adm-lib` at both widths rather than gaining a phone twin, because one of
   that rule's declarations is load-bearing here regardless of width: the
   `--empty` modifier that gives `RdEmptyState` the flex column it centres in.
   Only the gutter differs, so only the gutter is re-laid.

   ⚠️ **CORRECTED 2026-08-25.** `position: relative` used to be the other
   load-bearing declaration too: "which the publish confirm's `inset: 0` scrim
   resolves against (without it the panel escapes to the viewport and covers
   the chrome)." `.rd-confirm` is `position: fixed` now (adm1-5-1.css) and
   does not resolve against `.rd-adm-lib` at all any more — export 27 also
   moves the drawing's own `position: 'relative'` off every Pro content pane
   and onto `ProShell` (`rd-pro-kit.jsx:326`), matching this. `.rd-adm-lib`
   keeps `position: relative` (adm-lib.css) as the reversal path, same as its
   desktop context, not because this phone pane needs it.

   ⚠️ NO `@media` IN THIS FILE. The breakpoint is asked ONCE, in Rust
   (`device::use_device_class` — in `admin/layout.rs` for the shell and in
   `admin/drills.rs` for this rendering), and these rules paint what those two
   lines mounted. The same call `adm-mob.css`, `adm-prom.css` and
   `adm0-mobile.css` all make.

   ⚠️ NO NEW TOKENS. Every colour below is one the sheet already declares.

   ⚠️ THE TYPE FLOORS, applied not transcribed (`01 Foundations` §05, "Two
   floors, one exemption"): tracked uppercase never below 9.5px, mixed-case never
   below 11px. The drawing's 8.5px right-edge word ships at 9.5 — the same
   correction `.rd-prom__org-role` took — and the row's mixed-case act at 11.
   Marked at both sites.
*/

/* ---------------------------------------------------------------------------
   1. The two borrowed rules, re-laid — the console gutters this shell supplies.
   --------------------------------------------------------------------------- */
.rd-prom .rd-adm-lib {
  padding: 0 0 24px;
}

/* The drawn `MSearch` margin is `'14px 0 10px'` (rd-pro-mobile-pages.jsx:13),
   which is also the drawn chip row's, so the field and the chips stack on the
   same rhythm whichever is on screen.

   The field itself is KEPT at this width although doc 46 draws none: doc 35
   rules "search sits above the tabs because it searches the library rather than
   the tab", and a frame's silence is not a repeal. See the component. */
.rd-prom .rd-adm-lib__search {
  padding: 14px 0 10px;
}

/* ---------------------------------------------------------------------------
   2. The kind chips — the desktop's tab strip, at phone width.
   --------------------------------------------------------------------------- */
/* rd-pro-mobile-pages.jsx:51 — `display: flex, gap: 8, margin: '14px 0 10px'`. */
.rd-promd__kinds {
  display: flex;
  gap: 8px;
  margin: 14px 0 10px;
  /* ⚠️ WRAP + `flex: none` ON THE CHIP — ADDED WITH THE FOURTH TAB (batch 17
     item 5), and the pair is one decision rather than two declarations.

     The drawn row is three chips and fits one line at every phone width. Four
     — DRILLS · QUALIFICATIONS · COURSES · DRY FIRE — does not, at 320px, and
     the default flex behaviour for the overflow is the worst of the three
     available: `flex-shrink: 1` COMPRESSES the chips, so QUALIFICATIONS
     squashes its own box and the letter-spacing the drawing specifies gets
     eaten by an ellipsis. A reader is then looking at a tab whose word is cut
     off, with nothing saying a fourth exists.

     So: the chips keep their drawn size (`flex: none`) and the ROW gives way
     instead. A second line costs 30px of a screen that has it, and every tab
     stays readable and pressable. The alternative, `overflow-x: auto`, hides
     the fourth chip behind a scroll with no affordance at this size — the
     tab a reader does not know is there is the one thing this item exists to
     surface. Flagged in the PR body as a phone-width question for the
     designer; doc 46 draws three chips and has no opinion about four. */
  flex-wrap: wrap;
}

/* rd-pro-mobile-pages.jsx:53 — `padding: '7px 12px'`, `1.5px solid t.ctrl`,
   `tcham(8)`, `pdisp(800, 78, '.08em')` at 9.5px, `color: t.fg2`.

   `tcham(n)` is the corpus helper at `rd-train-kit.jsx` — the bottom-right
   chamfer, transcribed as the same `clip-path` polygon `kit-tab-chip.css` and
   `adm-mob.css` already ship.

   A `button` with its own reset, because it is one: the chip selects the tab.
   The drawn frame renders a `div` per chip only because a frame has no state to
   change.

   ⚠️ **THE STROKE IS TWO LAYERS NOW, AND THE ONE-LAYER SPELLING WAS A BUG.**
   This rule used to put `border: 1.5px solid` UNDER the `clip-path`, and the
   clip removes the corner INCLUDING the border drawn on it — so the cut
   rendered as an EMPTY GAP with no edge across it. `48 Kit Rules`: *"a stroked
   chamfer must be two layers"*; `01 Foundations`:85; the mechanism is stated at
   length in `chamfer_box.rs`'s header and in `pro-kit.css`'s chip block, which
   carries the full note for the family. James saw it on `/admin/members` at
   phone width, 26 Aug, and this chip is the one the Members strips were
   transcribed FROM, so it had the same defect for the same reason.

   The element is now the OUTER layer — a box in the stroke colour, clipped at
   the cut, whose padding carries the stroke's 1.5px on every side — and
   `::before` is the surface, clipped at `inner_chamfer_px(13, 1.5)` = 12.12 so
   the stroke keeps a uniform thickness along the diagonal. A pseudo-element
   rather than a face span because the label is this button's own text child and
   a wrapper would be a markup change on four screens for a paint the pseudo can
   do; `kit-tab-chip.css` §4 already draws its ring the same way.

   `z-index: -1` with `isolation: isolate` is what puts the surface ABOVE the
   element's own background and BELOW the label: a negative-z descendant paints
   at step 2 of its stacking context and inline content at step 5. Without the
   isolation the surface could escape to an ancestor context and paint under the
   page.

   ⚠️ **THE CUT IS 13, NOT 8.** `01 Foundations` §03, re-ruled 26 Aug 2026: 13 on
   controls. Named through `--rd-chamfer-control` so it moves with
   `CONTROL_CHAMFER_PX` and the kit button; the INNER value is a literal because
   `calc()` cannot carry the formula's √2 term. NO width query touches either —
   one chip, one cut, at every width. */
.rd-promd__kind {
  position: relative;
  isolation: isolate;
  /* The other half of the wrap decision above — see it for why a chip must
     never compress. */
  flex: none;
  border: 0;
  background: var(--rd-ctrl);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--rd-chamfer-control)), calc(100% - var(--rd-chamfer-control)) 100%, 0 100%);
  /* The drawn `7px 12px`, plus the 1.5px the border used to occupy: the box is
     the same box, and the stroke insets the surface instead of framing it. */
  padding: 8.5px 13.5px;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  font-size: 9.5px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--rd-fg2);
}

/* The surface. `--rd-hollow-fill` is the kit's outlined-control ground
   (`Fill::Hollow`) and it must be OPAQUE — a transparent surface lets the outer
   layer's stroke colour flood the whole chip. */
.rd-promd__kind::before {
  content: "";
  position: absolute;
  inset: 1.5px;
  z-index: -1;
  background: var(--rd-hollow-fill);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 12.12px), calc(100% - 12.12px) 100%, 0 100%);
}

/* The lit chip takes the accent as its STROKE, which is the outer layer's fill.
   Same treatment as before; only the property it is spelled with moved. */
.rd-promd__kind--on {
  background: var(--rd-acc);
  color: var(--rd-acc);
}

/* ⚠️ AN INSET RING, NOT THE HOUSE `outline`, AND IT IS MECHANICAL RATHER THAN A
   PREFERENCE: `clip-path` clips an `outline` away, so an outlined ring would
   draw nothing here. `kit-tab-chip.css` §4 states the fact and `adm-mob.css`
   ships the same substitution on its row.

   ⚠️ THE RING MOVED ONTO THE SURFACE LAYER WITH THE TWO-LAYER CHAMFER, AND IT
   HAD TO. An `inset` `box-shadow` paints with the element's OWN background —
   step 1 of the stacking context — which is now the stroke layer, underneath
   the surface. Left on the element the ring would have been painted over by the
   surface and focus would have shown nothing at all. */
.rd-promd__kind:focus-visible {
  outline: none;
}

.rd-promd__kind:focus-visible::before {
  box-shadow: inset 0 0 0 2px var(--rd-acc);
}

/* ---------------------------------------------------------------------------
   3. The right edge — the scannable verdict, and the row's one act.
   --------------------------------------------------------------------------- */
/* THE SLOT WRAPPER, AND IT CARRIES ONE NUMBER: 12.

   Batch 15, slice 9. The two treatments below used to be direct children of
   `.rd-prom-row`, so the 12px between them was the ROW's own `gap: 12` — the
   whole of `mrow`'s `gap` (`rd-pro-mobile-pages.jsx:9`). They are now mounted
   through `RdRow`'s `trail` slot, inside `.rd-row__trail`, which is
   `flex: none; display: flex; align-items: center` and declares no `gap` — one
   fill is what it is built for, and this screen is the case that hands it two.

   So this rule restores that number and declares nothing else. It is not a new
   treatment: it has no ground, no type and no colour, and deleting it would
   close the gap between the verdict and the act rather than change either. */
.rd-promd__edge {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* rd-pro-mobile-pages.jsx:68 — `pdisp(700, 78, '.1em')` at 8.5px.

   ⚠️ 9.5px, NOT THE DRAWN 8.5 — the condensed-uppercase floor. The same
   correction `.rd-prom__org-role` took in the shell part.

   ⚠️ IT IS A WORD, NOT A CHIP, at this width — for both drills and courses. The
   desktop draws a `ProChip` for a drill and plain words for a course, on doc
   35's argument that "a chip in that column implies a set of values to choose
   between and courses have none"; the drawn phone row makes every verdict a
   plain tracked word, which keeps that distinction without needing two
   treatments. It is therefore NOT `.rd-prom-row__chip` (adm-mob.css), which is a
   flex box built to hold a real chip.

   ⚠️ `--rd-fg3` BY DEFAULT, WITH ONE ACCENT MODIFIER, and this is now RULED —
   C-46.1 (27 Aug). The drawn phone row used to accent everything that is not
   `org` — which put `Community` in the accent — and doc 46's row-02 note
   argued for it; that was a rule change stated in a layout document, and doc
   46 opens by ruling that these frames "translate layout, never rules".
   `35 Pro Org Library` and `rd-pro-library.jsx:123-127` make `Published` the
   one live value on this axis, and the designer's reply confirms it: "Doc 35
   is right: Published is the only live-toned value. Doc 46's frame is
   corrected." So the modifier below is spent on `Published` alone, decided
   rather than flagged. */
.rd-promd__vis {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  font-size: 9.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--rd-fg3);
  white-space: nowrap;
}

.rd-promd__vis--live { color: var(--rd-acc); }

/* The row's one act, CARRIED PAST THE DRAWING: the drawn row has no act slot at
   all, and `35 Pro Org Library` rules that Publish is the row action and that an
   instructor "sees the same Publish control and is told no on click". One
   treatment, `.rd-adm-lib__act`'s, re-sized for the row it sits in — and 11px
   is the mixed-case floor, not a rounding. */
.rd-promd__act {
  flex: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg2);
}
.rd-promd__act:hover { color: var(--rd-fg); }
.rd-promd__act:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }
/* ==== ADM-EVD-MOB · 49 (Mobile) Pro Event Detail rows 01, 03–05 · owner: lane-drills ==== */
/*
   Drawn as `ScreenProEventDetailM` in `components/rd-pro-event-detail.jsx`,
   under `49 (Mobile) Pro Event Detail.html` row 01. Component:
   apps/web/src/pages/admin/event_detail.rs, the `DeviceClass::Phone` arm, which
   carries every ruling; only the values live here.

   Rows 03-05 are the event's three PUSHED phone screens (`ScreenProEvRosterM`,
   `ScreenProEvScoresM`, `ScreenProEvSquadsM`), drawn inside `EvScreenM`.
   Components: apps/web/src/components/pro/pushed.rs (the wrapper) and
   apps/web/src/pages/admin/event_screens.rs (the three routes), plus
   apps/web/src/pages/admin/event_roster_mobile.rs for row 03's body,
   apps/web/src/pages/admin/event_scores_mobile.rs for row 04's and
   apps/web/src/pages/admin/event_squads_mobile.rs for row 05's. Same document,
   same owner, so the SAME part — one document, one owner, one part.

   ⚠️ CITED BY SCREEN AND COMPONENT NAME, NEVER BY JSX LINE NUMBER.
   `rd-pro-event-detail.jsx` post-dates the ledger `rangeday check citations`
   resolves against, so a `file.jsx:LINE` reference to it would fail the gate
   outright — and `kit-implementation-contract.md` asks for names anyway. Same
   call `adm-mob.css`'s spec makes for `rd-pro-mobile.jsx`.

   ⚠️ THIS IS DOCUMENT 49's PART, NOT DOCUMENT 46's. `adm-mob.css` is ours too
   (owner: lane-drills) and its banner is explicit — "THIS IS THE DESTINATIONS
   PART, NOT THE SHELL'S", scoped to `46 (Mobile) Pro Destinations`. 49 is a
   different document, so this is a SIBLING part, which is the same call
   `adm-lib-mob.css` made for row 02 of 46. One document, one owner, one part.

   ANATOMY (inside `.rd-prom__body`, which the shell supplies — ADM0 §2.5):

     [ ProTop — the event's name, one act: Edit ]
     [ .rd-prome — the hub body ]
       [ .rd-ev-msg--error — the desktop arm's, borrowed ]
       [ .rd-prome__facts ]
         [ RdEventStatus — .rd-chip*, the desktop's chip ]
         [ .rd-prome__when — `{when} · {place}` ]
       [ .rd-rodoor — s47.css's, borrowed whole, gated on live-today ]
       [ .rd-ev-life — adm1-pr2.css's, COMPRESSED by the overrides in §3 ]
       [ .rd-prome__hub ]
         [ RdRowLink — `.rd-row--hub`, kit-row.css's. NOT declared here. ]
       [ .rd-ev-danger — adm1-pr2.css's, re-laid to stack in §4 ]

   ANATOMY, rows 03-05 — the pushed screens (`EvScreenM`), also inside
   `.rd-prom__body`:

     [ .rd-prom__back — a Link: chevron + the event's name ]
       [ .rd-prom__back-chev ]
       [ .rd-prom__back-label ]
     [ ProTop — the screen's name, 22px here; one optional act ]
     [ the screen's body ]

   ANATOMY, row 05 — Squads, the landing body (`ScreenProEvSquadsM`), inside the
   frame above. ⚠️ IT HAS ONE ACT SINCE §C-51.3 (27 Aug), and the act is in the
   BODY rather than in the head — the frame draws `Edit` on the note's own row,
   the same call it makes for row 04's `Export CSV`. The EDITOR that act opens is
   §10 at the foot of this part:

     [ .rd-prome-sq ]
       [ .rd-prome-sq__act-row ]
         [ .rd-prome-sq__note — "Built ahead in Squads & Stages · run from the
                                RO's phone" ]
         [ .rd-pro-top__btn — `Edit`, ADM1's Pro act box, borrowed whole ]
       [ .rd-ev-msg — the desktop arm's, borrowed: waiting, failed, empty ]
       [ .rd-prome-sq__list ]
         [ RdRow — `.rd-row--squad`, kit-row.css's. NOT declared here. The
                   Scored/Waiting chip is `.rd-row__chip` and its tone. ]
       [ RdRowNotice — `.rd-row-notice`, kit-row.css's ]

   ANATOMY, row 04 — the Scores screen's body (`ScreenProEvScoresM`), mounted
   with NO act at all: round 11 ruling 3 drops Export at this width.

     [ .rd-evsm ]
       [ .rd-evsm__chips — three chips: the two selection filters and the
                           voided-shown state ]
         [ .rd-evsm__chip — one chip box, a positioning context only ]
           [ .rd-evsm__pick — the acting control: the desk's own `<select>` or
                              checkbox, transparent, over the whole box ]
           [ .rd-evsm__face — the drawn `ProChip` span, `.rd-prochip*`, NOT
                              declared here; `aria-hidden`, because the control
                              above announces the same word ]
       [ .rd-evsm__msg — the error and the wait ]
       [ .rd-evsm__list ]
         [ RdRow — `.rd-row--score`, kit-row.css's. NOT declared here. The
                   three right edges are `.rd-row__stack` (time over points, or
                   the Confirm/Keep ask) and `.rd-row__text` (Voided). ]
           [ .rd-evsm__values-toggle — the value fold's opener, in the kit row's
                                       LEAD slot. Ours and undrawn. ]
             [ .rd-evsm__values-mark — its caret ]
         [ .rd-evsm__values — the value fold: the desk's Zones, Hit Factor,
                              Penalties and Notes columns, read at this width.
                              Doc 47's inline breakdown, respelled here ]
           [ .rd-evsm__values-chips — the zone strip ]
             [ RdChipStatus — `.rd-chip*`, the kit's. NOT declared here. ]
           [ .rd-evsm__values-attr — Hit Factor · Penalties ]
           [ .rd-evsm__values-note — the note, unlabelled ]
       [ .rd-ev-scores__more — adm1-pr4.css's pager, borrowed whole ]
       [ RdRowNotice — `.rd-row-notice`, the pinned foot. NOT declared here. ]

   ANATOMY, row 03 — the Roster screen's body (`ScreenProEvRosterM`):

     [ .rd-evrm ]
       [ .rd-evrm__msg — the personal note, the error, the empty state ]
       [ .rd-evrm__list ]
         [ RdRow — `.rd-row--person`, kit-row.css's. NOT declared here. ]
           [ .rd-row__stack — the fact-or-act over Remove ]
       [ RdRowNotice — `.rd-row-notice`, the pinned foot. NOT declared here. ]
       [ .rd-ev-pick — adm1-pr3.css's picker, re-positioned by §8 ]

   ⚠️ WHAT IT BORROWS AND DOES NOT DECLARE: `.rd-pro-page`, `.rd-pro-top*` (the
   shell restyles those), `.rd-rodoor*` (s47.css, same owner, same door), the
   kit's `.rd-chip*` through `RdEventStatus`, `.rd-ev-msg*` and the confirm
   modal's own block, `.rd-ev-pick*` (the add-members picker) and the kit's
   `.rd-row*` / `.rd-row-notice`. Every one of them is the desktop arm's or the
   kit's, mounted unchanged
   — which is what stops the two widths disagreeing about a colour nobody meant
   to change.

   ⚠️ WHAT IT OVERRIDES, AND WHY THE ORDER IS LOAD-BEARING: §3 and §4 re-lay
   `.rd-ev-life*` and `.rd-ev-danger*`, both declared in `adm1-pr2.css` (owner:
   spine) with the desk's spacing, and §8 re-positions `.rd-ev-pick`
   (`adm1-pr3.css`, same owner). Every override is scoped under `.rd-prome`,
   so the desktop event detail is untouched, and each compound is (0,0,2,0)
   against that part's (0,0,1,0) — it wins on specificity alone, and sitting
   after that file keeps it winning if the specificity ever levels. The idiom is
   `adm-mob.css`'s own `.rd-prom .rd-adm-ev`.

   ⚠️ NO `@media` IN THIS FILE. The breakpoint is asked ONCE, in Rust
   (`device::use_device_class` — in `admin/layout.rs` for the shell and in
   `admin/event_detail.rs` for this rendering), and these rules paint what those
   two lines mounted. The same call `adm-mob.css`, `adm-lib-mob.css`,
   `adm-prom.css` and `adm0-mobile.css` all make.

   ⚠️ NO NEW TOKENS. Every colour below is one the sheet already declares.

   ⚠️ THE TYPE FLOORS, applied not transcribed (`01 Foundations` §05, "Two
   floors, one exemption"): mixed-case text never goes below 11px. The drawing's
   10.5px facts line, 10.5px row count and 10px lifecycle note all ship at 11.
   Marked at each site.
*/

/* ---------------------------------------------------------------------------
   1. The hub body.
   --------------------------------------------------------------------------- */
/* No gutter of its own: the phone shell already carries 18px (`.rd-prom`), and
   the desktop detail's page padding is on `.rd-ev-overview`, which this arm does
   not mount. The bottom room is the drawn frame's own foot space. */
.rd-prome {
  padding: 0 0 24px;
}

/* ---------------------------------------------------------------------------
   2. The facts line — the chip and `{when} · {place}` on one row.
   --------------------------------------------------------------------------- */
/* `ScreenProEventDetailM`: `display: flex, alignItems: 'center', gap: 8,
   marginTop: 8`. Doc 49 puts the chip and the facts on ONE line; the desktop
   head's `sub` slot is left empty on this arm so the date is not said twice. */
.rd-prome__facts {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
}

/* `tbody` at 10.5px, `color: t.fg3`. ⚠️ 11px, NOT the drawn 10.5 — the
   mixed-case floor. The same correction `.rd-prom-row__sub` took in
   `adm-mob.css`. */
.rd-prome__when {
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.4;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   3. The lifecycle card, compressed — `adm1-pr2.css`'s strip, re-laid.
   --------------------------------------------------------------------------- */
/* ⚠️ THE CARD IS THE SAME CARD, AND THAT IS THE RULING RATHER THAN A SAVING.
   Doc 49: "the state card keeps 31 verbatim: the forward act, the visibility
   line, and the reverse below the hairline with its why". So this compresses
   the desktop component instead of drawing a second one — three step marks, one
   forward act, one note, one hairline, all still `Lifecycle`'s.

   The drawn card: `background: t.surf`, `tcham(10)`, `padding: '11px 12px'`,
   `marginTop: 10` — and NO border, where the desk draws one. `tcham(n)` is the
   corpus helper at `rd-train-kit.jsx`, transcribed as the same `clip-path`
   polygon `adm-mob.css` and `kit-tab-chip.css` already ship. */
.rd-prome .rd-ev-life {
  border: 0;
  padding: 11px 12px;
  margin-top: 10px;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%);
}

/* The connector's desk margin is 14px each side, which leaves three marks no
   room on a 448px screen. The drawing's is 8. */
.rd-prome .rd-ev-life__link {
  margin: 0 8px;
}

/* ⚠️ THE FORWARD ACT DROPS BELOW THE TRACK, AND IT IS A WRAP RATHER THAN A
   SECOND MARKUP. The drawing puts `Set Active` / `Mark Completed` on its own
   full-width line under the three marks; the component renders the button
   INSIDE `.rd-ev-life__track`, beside them. `flex-wrap` plus a 100% basis is
   what moves it without the phone arm mounting a different component — which is
   the whole point of compressing in CSS: the act, its label and its handler stay
   the desktop's. */
.rd-prome .rd-ev-life__track {
  flex-wrap: wrap;
}

.rd-prome .rd-ev-life__next {
  flex: 1 0 100%;
  margin-left: 0;
  margin-top: 10px;
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* `fontSize: 10, marginTop: 8` — ⚠️ 11px, the mixed-case floor again. */
.rd-prome .rd-ev-life__note {
  margin-top: 8px;
  font-size: 11px;
}

/* ⚠️ THE REVERSE STACKS INSTEAD OF SITTING BESIDE ITS WHY, AND THE REGISTER IS
   UNCHANGED. The desk lays label and sentence on one baseline row with a 14px
   gap; 448px has no room for a 12.5px label and a sentence side by side. The
   drawing stacks them (`marginTop: 5` under the label) and keeps everything the
   caption rules — below the hairline, text with no box, the sentence quiet
   beside it. Position compresses; weight does not. */
.rd-prome .rd-ev-life__reverse {
  display: block;
  margin-top: 9px;
  padding-top: 9px;
}

.rd-prome .rd-ev-life__why {
  display: block;
  margin-top: 5px;
}

/* ---------------------------------------------------------------------------
   4. The hub rows — one per table, each carrying its count.
   --------------------------------------------------------------------------- */
/* `display: grid, gap: 6, marginTop: 10`. Deliberately NOT `.rd-prom-list`
   (`adm-mob.css`): that is 46's row stack for a LIST of like records, at gap 8
   with a 14px lead. Three fixed doors are a different object — the drawing gives
   them their own gap and their own lead — and borrowing the list's name would
   make a future change to one silently change the other. */
.rd-prome__hub {
  display: grid;
  gap: 6px;
  margin-top: 10px;
}

/* ⚠️ `.rd-prome__row`, `:focus-visible`, `__row-main`, `__row-name`,
   `__row-sub` and `__chev` WERE HERE AND ARE GONE (2026-08-24). They declared
   the drawn hub row — `minHeight: 52`, `padding: '10px 14px'`, `gap: 12`,
   `t.surf`, `tcham(10)`, a 13px/600 name over an 11px count, a 7×12 chevron —
   and every one of those values is `.rd-row--hub` in `kit-row.css`, because
   `kit/row.rs`'s `RowIntent::Destination` was drawn FROM THIS FRAME: its doc
   quotes doc 49's own "Hub, not tabs" paragraph.

   Two declarations of one drawn object is exactly the drift the row taxonomy
   exists to stop, so the hub mounts `RdRowLink` and this block went with the
   `event_detail.rs` entry in `kit::pro_table`'s ALLOWED list. Written as an
   epitaph rather than deleted silently, the way `adm-mob.css` §2 records its
   own. */

/* ---------------------------------------------------------------------------
   5. The delete foot — `adm1-pr2.css`'s danger slot, stacked.
   --------------------------------------------------------------------------- */
/* ⚠️ THE WORDS AND THE THREE-WAY RULE ARE THE DESKTOP'S; ONLY THE LAYOUT MOVES.
   `delete_slot` decides Offered / Refused / Silent at both widths (the 13 Aug
   server rule), and `.rd-ev-danger__link`, `__denied` and `__why` keep their
   register. What changes is the desk's 40px lead and its baseline row: the
   drawn foot is a hairline with the act above its sentence, 9px apart. */
.rd-prome .rd-ev-danger {
  display: block;
  margin-top: 22px;
  padding-top: 9px;
}

.rd-prome .rd-ev-danger__why {
  display: block;
  margin-top: 6px;
  font-size: 11px;
}

/* ---------------------------------------------------------------------------
   6. The pushed screen's back row — `EvScreenM`, rows 03-05.
   --------------------------------------------------------------------------- */
/* ⚠️ THIS BLOCK IS IN THE `.rd-prom*` NAMESPACE, WHICH IS DOCUMENT 45's, AND
   THAT IS FLAGGED RATHER THAN QUIET. `EvScreenM` is drawn as a near-copy of
   `ProMobileScreen` (`45 (Mobile) Pro Shell`, `adm0-mobile.css`, owner: lane-3)
   plus a back row and a smaller title — so by nature the back row is a SHELL
   affordance and belongs in that part. It ships here because doc 49 is the only
   document that draws it and this is doc 49's part; the alternative was a third
   part for four declarations, which the one-document-one-part rule forbids
   outright.

   The ask to lane-3 is on the PR: take `.rd-prom__back*` into `adm0-mobile.css`
   §45 and delete this section. Until then the name is the drawn one, so the
   move is a cut and paste rather than a rename.

   ⚠️ NOT A `ProTop` SLOT. `pro/top.rs` rules that there is no back slot in
   the top bar; the back row is a SIBLING of the title row, so the ruling is
   untouched and `ProTop` is unchanged.

   ⚠️ RULING — JAMES, REHEARSAL 2026-08-29: THE ROW SITS UNDER THE TITLE ROW,
   ON EVERY FRAME. Doc 49's `EvScreenM` draws the row ABOVE the title; James
   overrode that for the phone too, so the order is one order — head first
   (a hard 76 on the console, so its hairline meets the rail head's), the back
   row under it, the content after. `ProPushedScreen` builds it once
   (`components/pro/pushed.rs`, `ruling_the_back_row_sits_under_the_title_row`).

   ⚠️ THE PADDING BELOW IS THE PHONE'S, AND THE CONSOLE RESTATES ALL FOUR
   SIDES. `padding: 12px 0` is the phone's rhythm on both edges — 12 is the
   drawn head-to-first-row distance on these screens (`.rd-evrm__list` and
   `.rd-prome-sq__list`, `marginTop: 12`), so the row takes the same 12 from the
   head above it and gives the same 12 to the content under it. No horizontal
   inset, because both phone roots supply one (`.rd-prom` and `.rd-prom-bare`
   each pad 18px, `adm0-mobile.css` §1/§1b). The console's page root has none
   and the console's head-to-region rhythm is the kit's 18, so
   `.rd-pro__main .rd-prom__back` in `adm0-chrome.css` restates the whole
   `padding` there — that rule is console-only by its selector, so this base
   still reaches the two phone frames unchanged. Pinned by
   `ruling_the_pushed_back_row_is_a_row_under_the_hard_76`
   (`components/pro/shell.rs`). */
.rd-prom__back {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 12px 0;
  text-decoration: none;
  color: var(--rd-fg3);
}

/* An inset ring rather than the house `outline`: every chamfered surface on this
   screen already substitutes one, and a back row that focused differently from
   the rows under it would read as a different kind of control. */
.rd-prom__back:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--rd-acc);
}

.rd-prom__back-chev {
  flex: none;
  color: currentColor;
}

/* `EvScreenM`'s back label: `pdisp(700, 78, '.1em')` at 9.5px, `color: t.fg3`
   — inherited from the row above rather than re-declared.

   ⚠️ 9.5px STANDS, and that is the OTHER floor rather than an exemption from
   the one this file keeps quoting. `01 Foundations` §05 is "two floors": 11px
   for mixed-case text, 9.5px for tracked condensed uppercase, which is what
   this is. The same call `.rd-prom__org-role` makes in `adm0-mobile.css`. */
.rd-prom__back-label {
  font-weight: 700;
  font-stretch: 78%;
  font-size: 9.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
}

/* ⚠️ THE PUSHED SCREEN'S TITLE IS 22px, NOT THE SHELL'S 27px, AND IT IS
   SCOPED RATHER THAN A PROP. That is a drawn difference between `EvScreenM` and
   `ProMobileScreen`; expressing it as a `ProTop` size prop would be a kit change
   in lane-3's component for one screen family's sake. The scope says what the
   markup says: a title row inside a PUSHED page root is the pushed screen's.

   ⚠️ AMENDED 2026-08-29 — `.rd-pro-page--pushed`, NOT THE `+` COMBINATOR. This
   read `.rd-prom .rd-prom__back + .rd-pro-top .rd-pro-top__title` while the
   back row led the title row. James's ruling of the 29th puts the head FIRST
   and the row under it, so the adjacency the selector leaned on is gone;
   `ProPushedScreen` now stamps `.rd-pro-page--pushed` on its root and the
   title is scoped on that. (`:has(+ .rd-prom__back)` would have worked and
   was refused: `pro/shell.rs`'s sweep bars any selector that spells
   `.rd-pro-top` BEFORE `.rd-prom__back`, because that shape is how a slot
   inside the head gets built.)

   ⚠️ AMENDED 2026-08-24 — SCOPED UNDER `.rd-prom`, AND THE SCOPE IS THE
   WHOLE POINT. Batch 16 gave the two Shooter Progress detail screens a back row
   through the same wrapper on BOTH frames (`pages/admin/progress.rs`), and
   this file declares no `@media` — so unscoped, a desktop console page with a
   back row would silently have taken the phone's 22px title in place of the
   drawn 26px (`adm0-chrome.css`), a redraw nobody asked for arriving as a side
   effect of a navigation fix. `.rd-prom` is `ProMobileScreen`'s root and wraps
   every phone-frame admin route (`admin/layout.rs`), so the phone keeps 22px
   at (0,0,4,0) and the desktop keeps 26px. Same call, same reason, as the
   `.rd-prome`-scoped override sections above. */
.rd-prom .rd-pro-page--pushed .rd-pro-top__title {
  font-size: 22px;
}

/* ---------------------------------------------------------------------------
   7. Row 05 — the Squads screen, read-only (`ScreenProEvSquadsM`).
   --------------------------------------------------------------------------- */
/* ⚠️ THREE DECLARATIONS, BECAUSE THE ROW IS THE KIT'S. `kit-row.css` carries
   the card ground, the chamfer, the 56px height, the 13px/600 name over the
   11px sub-line, the tracked-uppercase chip and its two tones, and
   `.rd-row-notice` carries the foot. What is left for this part is the screen's
   own frame: the note under the title, the gap between the cards, and the room
   at the bottom. A second spelling of any of the rest is the drift the row
   taxonomy exists to stop.

   ⚠️ NO ACT ANYWHERE, AND IT IS NOT CSS THAT ENFORCES IT.
   `RowIntent::Squad.permits_acts()` is false, so an act on this row is
   unrepresentable rather than merely unstyled — "this screen reads,
   deliberately actless". */
.rd-prome-sq {
  padding: 0 0 24px;
}

/* `tbody` at 10.5px, `color: t.fg3`, `marginTop: 6` — ⚠️ 11px, NOT the drawn
   10.5: the mixed-case floor, the same correction `.rd-prome__when` above takes
   and the same one `adm-mob.css` made for its sub-line. */
.rd-prome-sq__note {
  margin: 6px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.4;
  color: var(--rd-fg3);
}

/* `display: grid, gap: 7, marginTop: 12`. Deliberately NOT `.rd-prom-list`
   (`adm-mob.css`, gap 8) and NOT `.rd-prome__hub` (§4, gap 6): three drawn
   stacks, three drawn gaps, and borrowing one name would make a change to one
   silently change the others. */
.rd-prome-sq__list {
  display: grid;
  gap: 7px;
  margin-top: 12px;
}

/* ---------------------------------------------------------------------------
   8. The roster screen's body — row 03, `ScreenProEvRosterM`.
   --------------------------------------------------------------------------- */
/* ⚠️ THE ROW IS THE KIT'S AND IS NOT DECLARED HERE. `RowIntent::Person` was
   drawn FROM THIS FRAME — its doc quotes row 03's own note — so `kit-row.css`
   already carries the 54px minimum, the hairline rule, the 12.5px name over the
   10px sub, and the right edge's `display: grid, gap: 4, textAlign: right` with
   its fact/act registers. Declaring any of it again would be two spellings of
   one drawn object, which is what the row taxonomy exists to stop. What is left
   is the body around the list, and it is four rules.

   The foot is `RdRowNotice` (`.rd-row-notice`, kit-row.css) — the drawn
   sentence at `marginTop: 12`, floored to 11px there. Not re-declared either.

   ⚠️ THE TYPE FLOOR, APPLIED NOT TRANSCRIBED: the drawn 10px body copy ships at
   11 — `01 Foundations` §05, mixed-case never below 11. Same correction §2's
   facts line took. */

/* No gutter of its own: the phone shell already carries 18px (`.rd-prom`), and
   the desktop tab's page padding is on `.rd-ev-roster`, which this rendering
   does not mount. The bottom room is the drawn frame's own foot space, matching
   `.rd-prome` in §1. */
.rd-evrm {
  padding: 0 0 24px;
}

/* `marginTop: 12` — the drawn distance from the head to the first row. The rows
   rule themselves apart, so there is no gap here: a `grid` with a gap would put
   air between hairlines the frame draws touching. */
.rd-evrm__list {
  margin-top: 12px;
}

/* The three lines that are not rows — the personal-roster note, the error, and
   the empty state. One block, because they are one register: the desk gives them
   three classes at two sizes and the phone frame has room for neither. */
.rd-evrm__msg {
  margin-top: 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.55;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

.rd-evrm__msg--error {
  color: var(--rd-danger);
}

/* ⚠️ THE PICKER'S SCRIM GOES FULL-SCREEN AT THIS WIDTH, AND ONLY THE POSITION
   MOVES. `.rd-ev-pick` (adm1-pr3.css, owner: spine) is `position: absolute;
   inset: 0` because on the desk it covers the TAB BODY and deliberately leaves
   the rail and the page head lit — "so the rail and the page head stay lit".
   There is no rail at phone width and nothing beside the dialog worth leaving
   lit, and the phone body is only as tall as its list, so an absolute scrim
   would cover a short roster and stop. `fixed` is the same scrim against the
   viewport instead; the panel's own `max-width: calc(100vw - 32px)` and
   `max-height: calc(100% - 32px)` already fit it.

   Scoped under this screen's block, at (0,0,2,0) against that part's (0,0,1,0),
   so the desktop tab is untouched — the idiom §3 and §4 already use for
   `adm1-pr2.css`'s lifecycle and danger blocks.

   ⚠️ **UPDATE 2026-08-25: THE DESKTOP RULE IS `fixed` NOW TOO, so this override
   restates rather than overrides.** Everything above describes the arrangement
   as it stood; the desk arm moved for a different reason — under the fixed-rail
   scroll model an absolute scrim centred its panel on the tab-body scroller
   instead of on the viewport (the mechanism is at `.rd-confirm`, adm1-5-1.css).
   Kept, not deleted: this part's argument for `fixed` at phone width is its own
   and stands on its own, and a rule that agrees with its base is not a defect.
   If the desk arm is ever ruled back to `absolute`, this override is what keeps
   the phone correct — deleting it now would quietly remove that. */
.rd-evrm .rd-ev-pick {
  position: fixed;
}

/* ---------------------------------------------------------------------------
   9. The scores screen's body — row 04, `ScreenProEvScoresM`.
   --------------------------------------------------------------------------- */
/* ⚠️ THE ROW IS THE KIT'S AND IS NOT DECLARED HERE. `RowIntent::Score` was drawn
   FROM THIS FRAME — its doc calls it "the densest fold in the corpus" and quotes
   row 04's own three right edges — so `kit-row.css` already carries the 56px
   minimum, the hairline rule, the 12.5px name over the 10px sub, the strike on a
   voided name, the `mono` measurement line, the quiet points line under it and
   the danger `Voided` word. Declaring any of it again would be two spellings of
   one drawn object. What is left is the body around the list, the chip row's
   three boxes and the focus ring that box needs.

   ⚠️ WHAT ELSE THIS BLOCK BORROWS RATHER THAN DECLARES: `.rd-prochip*` (the
   three filter chips are `ProChip`s, which is what the frame draws — spans, and
   they STAY spans: what acts is a transparent native control laid over the chip
   box, because the kit-owner ruling is that ProChip never covers selectables)
   and `.rd-ev-scores__more` / `.rd-ev-scores__more-btn`
   (adm1-pr4.css, the desk's pager, taken whole). The pager is UNDRAWN at this
   width and kept anyway: `ListScores` is 50 rows a request and a qualification
   day is hundreds by design, so the alternative is a screen that stops at row
   fifty and says nothing about it. The foot is `RdRowNotice`
   (`.rd-row-notice`), the drawn sentence at marginTop 12, floored to 11px
   there.

   ⚠️ EXPORT IS BACK, AND IT COSTS THIS BLOCK TWO RULES. `design-reply-
   2026-08-27.md` §C-51.3 overturns round 11 ruling 3 — "CSV export on the phone:
   plain download, same file as desktop" — and doc 49 row 04 draws the act in the
   BODY rather than in the head: a caveat on the left, a Pro act box on the right,
   above the chip row. So the head still takes no action (`event_screens.rs`), and
   what lands here is the row that holds the two, plus the caveat's register.

   The act's own box is `.rd-pro-top__btn` (ADM1, `_legacy-events-list.css`),
   BORROWED WHOLE — the same control the Roster screen's `Add` mounts one screen
   across, so the two pushed screens' acts are one box rather than two spellings.
   The drawn face is outlined and the shipped Pro act is volt-filled; that
   divergence is ADM1's and is not re-decided here.

   The desktop keeps its own head button and its `has_more` partial-export
   warning (`.rd-ev-scores__partial`, `adm1-pr4.css`), untouched — the phone's
   caveat is a shorter, unconditional sentence, which is what the frame draws.

   ⚠️ THE TYPE FLOOR, APPLIED NOT TRANSCRIBED: the drawn 10px body copy ships at
   11 — `01 Foundations` §05, mixed-case never below 11. Same correction §2's
   facts line and §8's message register took. */

/* No gutter of its own: the phone shell already carries 18px (`.rd-prom`), and
   the desktop tab's page padding is on `.rd-ev-scores`, which this rendering
   does not mount. The bottom room is the drawn frame's own foot space, matching
   `.rd-prome` in §1 and `.rd-evrm` in §8. */
.rd-evsm {
  padding: 0 0 24px;
}

/* `display: flex, alignItems: center, gap: 10, marginTop: 10` — the export row,
   drawn first on the screen, above the chips. Two children: the caveat, which
   takes the slack (`flex: 1`), and the act, which does not.

   ⚠️ THE ACT IS NOT DECLARED HERE. It is `.rd-pro-top__btn`, borrowed — see the
   block banner. A second box with this screen's own name would be two spellings
   of one Pro act. */
.rd-evsm__export {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 10px;
}

/* The caveat. `tbody` at the drawn 9.5px, `color: t.fg3`, `flex: 1` — ⚠️ 11px,
   NOT 9.5: mixed-case never below 11 (`01 Foundations` §05), the same correction
   §2's facts line and this block's message register take. It is the SAME
   register as `.rd-evsm__msg` and is deliberately not that class: the message
   line owns a `margin-top` of its own and this one sits inside a flex row. */
.rd-evsm__export-note {
  flex: 1;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.4;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

/* `display: flex, gap: 6, marginTop: 8, flexWrap: 'wrap'` — the drawn chip row.
   It wraps because a selected shooter's name is as long as the name is, and
   three chips that cannot wrap are a horizontal scroll on the one surface the
   row taxonomy forbids one on.

   ⚠️ 8, NOT 10, SINCE THE EXPORT ROW LANDED. The frame's chip row moved down a
   step when the act was drawn above it — the 10 this rule used to carry was the
   distance from the TITLE, and the title is no longer what sits above it. */
.rd-evsm__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}

/* THE CHIP BOX. A positioning context and nothing else: the drawn chip's own
   geometry is `.rd-prochip`'s and is not respelled here (see the block banner).
   `inline-flex` rather than `inline-block` so the box is exactly the face's box
   — a descender's worth of line-height under the control would put a tap target
   below the chip that looks like nothing. */
.rd-evsm__chip {
  position: relative;
  display: inline-flex;
}

/* The drawn face. `aria-hidden` in the markup, so this rule owns the paint and
   nothing else; the ink, the border and the 9px tracking are `.rd-prochip`'s. */
.rd-evsm__face {
  display: inline-flex;
}

/* THE CONTROL, OVER THE FACE. Doc 49 ("Filters survive as chips") draws the
   chip; the desk's own `<select>`s and checkbox are what act. Laid transparently
   across the chip's whole box so the tap lands on a REAL control and the picker
   that opens is the platform's — `48 Kit Rules` exempts OS-owned controls
   (native input faces, the picker) from the chamfer, which is why a rounded
   system wheel over a chamfered kit is not a defect here.

   ⚠️ NO `font-size`, DELIBERATELY. The control is invisible; a size on it would
   be a second spelling of the chip's 9px, and this block's type-floor pin reads
   every size it declares. `appearance: none` strips the native arrow the face
   already does not draw. */
.rd-evsm__pick {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  opacity: 0;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
}

/* ⚠️ THE FOCUS RING GOES ON THE FACE, because the control that has focus cannot
   be seen. Without this the keyboard tabs into three invisible controls and the
   screen shows nothing at all — the one way this pattern fails badly. */
.rd-evsm__pick:focus-visible ~ .rd-evsm__face {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* `marginTop: 8` — the drawn distance from the chip row to the first score. The
   rows rule themselves apart, so there is no gap here: a `grid` with a gap would
   put air between hairlines the frame draws touching. Deliberately NOT §8's
   `.rd-evrm__list` (marginTop 12): two drawn stacks, two drawn distances, and
   borrowing one name would make a change to one silently change the other. */
.rd-evsm__list {
  margin-top: 8px;
}

/* The two lines that are not rows — the error and the wait. One block, because
   they are one register, and the same register §8 gives the roster's three. */
.rd-evsm__msg {
  margin-top: 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.55;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

.rd-evsm__msg--error {
  color: var(--rd-danger);
}

/* ---------------------------------------------------------------------------
   THE VALUE FOLD — the desk's Zones, Hit Factor, Penalties and Notes columns,
   readable at this width.

   ⚠️ WHY IT EXISTS AT ALL. Round 11 ruling 3 drops Export here, and those four
   columns had no other door on a phone. `51 Shell Lab` rules that a defect:

       capability parity (ruled 21 Aug) — anything one device can do or read,
       the other can; only the presentation changes. Filters, acts, search,
       inspectors, commits and value columns appear on both sides of every
       section, and a control missing on one side is a defect in the contract,
       not a device adaptation.

   READ, not export. No file is written at this width and none is offered.

   ⚠️ THE TREATMENT IS DRAWN AND IT IS NOT DRAWN HERE. Doc 49 draws no fold for
   these values, so rather than invent one this screen reuses the corpus's one
   drawn breakdown of a recorded row — `47 (Mobile) RO Range Day.html:56`:

       A scored row expands inline to its breakdown. Render-always,
       collect-never — the board stays flat until tapped.

   built over `.rd-board__breakdown*` in `s09-board.css`. THE GEOMETRY IS COPIED
   VERBATIM (`padding: 2px 0 13px 21px`, chip `gap: 5`, `margin-top: 8`); THE
   CLASSES ARE NOT BORROWED. `s09-board.css` is the shooter-side RO board's
   document and this is Pro admin — the pager borrow two rules above works the
   other way round (`.rd-ev-scores__more` is this same screen's desktop arm, one
   document), and a rule that styled two products from one selector could not be
   changed for one of them.

   Collect-never is STRUCTURAL, not styling: nothing in this subtree takes
   input. Pinned by `ruling_the_value_fold_collects_never`.
   --------------------------------------------------------------------------- */

/* THE OPENER. Ours and undrawn — doc 47 makes the whole row the control, and
   the kit gives `RdRow` no on-row click on purpose ("a `div` with a click
   handler is a link that a keyboard cannot reach"), so this is a real button in
   the row's lead slot. 56 square is `51 Shell Lab`'s phone touch floor, stated
   for both axes rather than leaned on the row's own 56px height. No background
   and no border: the row is one object, and a boxed control at its head would
   read as a second one. */
.rd-evsm__values-toggle {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  min-height: 56px;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--rd-fg3);
  cursor: pointer;
}

.rd-evsm__values-toggle:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -4px;
}

/* THE CARET. A CSS triangle rather than the kit's `.rd-row__chev`, which this
   screen may not spell (the row's classes are the kit's and are emitted from
   its props). 7 wide by the same 7-and-a-bit tall the chevron is, pointing down
   at rest and right when closed is the other convention — down-when-open is the
   one a disclosure uses, and `aria-expanded` is what actually carries the state
   to a reader. */
.rd-evsm__values-mark {
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 6px solid currentColor;
  transition: transform 120ms ease;
  transform: rotate(-90deg);
}

.rd-evsm__values-toggle[aria-expanded='true'] .rd-evsm__values-mark {
  transform: rotate(0deg);
}

@media (prefers-reduced-motion: reduce) {
  .rd-evsm__values-mark {
    transition: none;
  }
}

/* `.rd-board__breakdown` verbatim: the block hangs off the same left edge as the
   row above it and reads as part of that row rather than as a new section.
   Unlike a row it takes NO hairline — the row's own bottom rule is already under
   it, and a second would draw a line through a single record. */
.rd-evsm__values {
  padding: 2px 0 13px 21px;
}

/* `.rd-board__breakdown-chips` verbatim — gap 5. The strip is ABSENT rather than
   empty when a drill declares no zones (`chip.rs` role C §1.2), so no height is
   reserved for chips that may not exist. */
.rd-evsm__values-chips {
  display: flex;
  gap: 5px;
  flex-wrap: wrap;
}

/* `.rd-board__breakdown-attr`'s register and its 8px of air — but NOT its 10px.
   This part's floor is 11 (`01 Foundations` §05, mixed-case never below 11), the
   same correction §2, §8 and the message register above all took, and 11 is also
   `.rd-row__sub`'s size — so the fold reads in the same register as the stage
   line directly above it. */
.rd-evsm__values-attr,
.rd-evsm__values-note {
  margin-top: 8px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.55;
  color: var(--rd-fg3);
}

/* The note is a sentence somebody typed on a range, so it wraps and it may
   contain a word longer than the column. `--rd-fg2` rather than `--rd-fg3`: it
   is content, where the line above it is a label strip. */
.rd-evsm__values-note {
  color: var(--rd-fg2);
  overflow-wrap: anywhere;
  text-wrap: pretty;
}

/* ---------------------------------------------------------------------------
   10. Row 05's EDITOR — `ScreenProEvSquadsEditM`, §C-51.3.
   --------------------------------------------------------------------------- */
/* ⚠️ WHY THIS BLOCK EXISTS AT ALL. `design-reply-2026-08-27.md` §C-51.3:
   "Ruled (James): parity wins — all three carve-outs reopen. … Squads on the
   phone: full parity — everything the desktop editor does." The frame was
   redrawn with it: `ScreenProEvSquadsM` gained an `Edit` act on the note's row,
   and `ScreenProEvSquadsEditM` is a new frame.

   ⚠️ IT IS NOT `adm1-6.css`'s BOARD, AND THAT IS THE POINT. The desktop builder
   (`.rd-rday-*`) is two scrolling columns of selectable chips with a placement
   button per column; doc 49 draws a vertical stack of CHECKBOX ROWS and one
   `Move n To…` sheet. Borrowing `.rd-rday-*` here would be one name for two
   drawn objects — the drift the row taxonomy exists to stop — so the editor gets
   its own names under this document's `.rd-prome-sq` family. What IS shared is
   the MODEL, in Rust (`squads.rs`'s `Board`, `place`, `moved_count`,
   `SaveState`), which is where sharing actually prevents a disagreement.

   ⚠️ WHAT IT BORROWS RATHER THAN DECLARES: `.rd-pro-top__btn` (ADM1) for the
   read view's `Edit` act — the same Pro act box the Roster screen's `Add`
   mounts; `.rd-ev-msg` / `.rd-ev-msg--error` / `.rd-ev-msg__dismiss`
   (adm1-pr2.css) for the save failure and its way out, exactly as §7 already
   borrows the first two; `.rd-row-notice` (kit-row.css) for the foot; and the
   kit's own empty state through `RdEmptyState`. None of them is respelled.

   ⚠️ NO NEW TOKENS, and no `@media`: the breakpoint is asked once in Rust.

   ⚠️ THE TYPE FLOORS, APPLIED NOT TRANSCRIBED (`01 Foundations` §05): the frame
   draws 10.5px row and header copy and 10.5px act words; mixed-case ships at 11
   here, the same correction every other block in this part has taken.

   ANATOMY:

     [ .rd-prome-sq__act-row — the read view's note + `Edit` ]

     [ .rd-prome-sqe — the editor ]
       [ .rd-ev-msg--error — the save failure, over the live board ]
       [ RdEmptyState — the desk's whole ruled sentence, board empty ]
       [ .rd-prome-sqe__squad ] × n, then once more for the pool
         [ .rd-prome-sqe__head ]
           [ .rd-prome-sqe__name (+ --pool, + .rd-prome-sqe__bay)
             | two .rd-prome-sqe__field inputs while renaming ]
           [ .rd-prome-sqe__acts ]
             [ .rd-prome-sqe__act (+ --danger) ]
         [ .rd-prome-sqe__none — nobody here / nobody unassigned ]
         [ .rd-prome-sqe__row (+ --on) ]
           [ .rd-prome-sqe__pick — the label wrapping the real checkbox ]
             [ .rd-prome-sqe__check ] [ .rd-prome-sqe__who ]
           [ .rd-prome-sqe__ord ] [ .rd-prome-sqe__ord-btn ] × 2
       [ .rd-prome-sqe__add — the dashed row ]
       [ RdRowNotice — the desk's FOOT, which is the server's refusal verbatim ]
       [ .rd-prome-sqe__bar ]
         [ .rd-prome-sqe__move ] [ .rd-prome-sqe__save ]
       [ .rd-prome-sqe__pickwrap — fixed, inset 0: the console's sheet grammar ]
         [ .rd-prome-sqe__scrim ]
         [ .rd-prome-sqe__sheet — capped at 80%, scrolls its destinations ]
           [ .rd-prome-sqe__sheet-head ]
             [ .rd-prome-sqe__sheet-title ] [ .rd-prome-sqe__sheet-note ]
           [ .rd-prome-sqe__dest (+ --here) ] */

/* The read view's act row. `display: flex, alignItems: center, gap: 10,
   marginTop: 6` — the frame puts `Edit` on the note's own line, which is why the
   note is no longer a block on its own. */
.rd-prome-sq__act-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 6px;
}

/* The note keeps its register and loses its own top margin: the row above
   carries it now, and two of them would double the drawn 6px. */
.rd-prome-sq__act-row .rd-prome-sq__note {
  flex: 1;
  margin: 0;
}

/* The editor's frame. Room at the foot for the bar, which is part of the
   scrolling body rather than fixed: the frame draws it after the last squad, and
   a fixed bar on a phone would sit over the pool the reader is choosing from.
   `outline: none` because the element is `tabindex="-1"` only so it can receive
   the Escape key — it is not a control and must not paint a focus ring. */
.rd-prome-sqe {
  padding: 0 0 24px;
  outline: none;
}

/* One squad, and the pool, which is drawn as one more of these without acts. */
.rd-prome-sqe__squad {
  margin-top: 10px;
}

/* `display: flex, alignItems: baseline, justifyContent: space-between,
   marginBottom: 4`. `baseline` rather than `center` so the name and the quiet
   act words sit on one line of type, which is what the frame draws — and `wrap`
   because a 60-character squad name is representable and must not push the acts
   off the edge. */
.rd-prome-sqe__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 4px;
}

/* `tdisp(800, 78, '.04em')` at 12 — the drawn squad name, tracked and condensed.
   Uppercase is NOT applied: the name is a thing a reader typed. */
.rd-prome-sqe__name {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .04em;
  font-size: 12px;
  color: var(--rd-fg);
  overflow-wrap: anywhere;
}

/* The pool's head reads `Unassigned · 1` and is one register quieter than a
   squad's, because it is a derived group rather than a thing with a name. */
.rd-prome-sqe__name--pool {
  color: var(--rd-fg3);
}

/* `· Bay 2` — the drawn second half, body register inside a display line, so it
   carries its own weight and stretch rather than inheriting the head's. */
.rd-prome-sqe__bay {
  font-weight: 400;
  font-stretch: 100%;
  letter-spacing: 0;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* The two acts, `Rename · Delete`, drawn as words rather than boxes: the frame
   puts them in the body register at the head's right edge. */
.rd-prome-sqe__acts {
  flex: none;
  display: flex;
  align-items: baseline;
  gap: 12px;
}

/* ⚠️ A BUTTON THAT LOOKS LIKE A WORD IS STILL A BUTTON, and it needs a boundary
   a pointer can find. `--rd-ctrl` is the control token; `--rd-line` and
   `--rd-chip` are explicitly NOT usable as a sole interactive indicator
   (`_legacy-tokens.css`, the `--rd-ctrl` note). */
.rd-prome-sqe__act {
  appearance: none;
  -webkit-appearance: none;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rd-ctrl);
  padding: 0 0 1px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.4;
  color: var(--rd-fg2);
  cursor: pointer;
}

.rd-prome-sqe__act:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* Destructive TEXT is `--rd-danger`; `--rd-dfill` is the ground a Delete BUTTON
   sits on with white type, and this is a word rather than a filled button. */
.rd-prome-sqe__act--danger {
  color: var(--rd-danger);
  border-bottom-color: var(--rd-danger);
}

/* The rename pair. The desk's two fields, at this width's geometry: the name
   takes the slack, the bay is as wide as a bay label needs. */
.rd-prome-sqe__field {
  flex: 1;
  min-width: 0;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  padding: 9px 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg);
}

.rd-prome-sqe__field:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 1px;
}

/* ⚠️ THE ROW IS 44 DRAWN AND 44 SHIPPED, AND THE TOUCH TARGET IS THE LABEL.
   `51 Shell Lab` floors touch at 56 on a phone; the frame draws these rows at 44
   because they are a dense multi-select list rather than a navigation row, and
   the thing that takes the tap is `.rd-prome-sqe__pick`, which fills the row and
   is a real `<label>` wrapping a real checkbox. Ruling item: the drawn 44 sits
   under the 56 floor, and that floor was written for rows that navigate. */
.rd-prome-sqe__row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 0 12px;
  margin-bottom: 4px;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
}

/* The selected row. The drawn treatment is a lift of the ground plus a full-ink
   boundary — NOT the accent, which the checkbox inside already carries. */
.rd-prome-sqe__row--on {
  background: var(--rd-surf2);
  border-color: var(--rd-fg);
}

/* The label IS the target: it fills the row so the tap lands anywhere on it, and
   it wraps the checkbox so the browser gives the name to the control for free. */
.rd-prome-sqe__pick {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  cursor: pointer;
}

/* `width: 14, height: 14, border 1.5px` — the drawn box, painted by the platform
   with our accent so the checked state is the OS's own mark rather than a square
   of ours that only looks checked. `48 Kit Rules` exempts an OS-owned control
   face from the chamfer, which is why a rounded system tick inside a chamfered
   kit is not a defect here. */
.rd-prome-sqe__check {
  flex: none;
  width: 14px;
  height: 14px;
  margin: 0;
  accent-color: var(--rd-acc);
}

.rd-prome-sqe__check:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* The shooter's name. The frame ellipsises it at 12; a name is the one string on
   this screen a reader is scanning for, so it truncates rather than wrapping —
   the row is a fixed-height list. */
.rd-prome-sqe__who {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  line-height: 1.3;
  color: var(--rd-fg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The reorder pair — UNDRAWN at this width, and the quietest thing on the row.
   The desk's `↑` / `↓`, which is what shooting order inside a squad is set with.
   36 square rather than 44: two of them plus a 44 label is what fits, and they
   are secondary to the checkbox that IS the row's act. Ruling item. */
.rd-prome-sqe__ord {
  flex: none;
  display: flex;
  gap: 2px;
}

.rd-prome-sqe__ord-btn {
  appearance: none;
  -webkit-appearance: none;
  width: 36px;
  height: 36px;
  background: none;
  border: 1px solid var(--rd-ctrl);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  line-height: 1;
  color: var(--rd-fg2);
  cursor: pointer;
}

.rd-prome-sqe__ord-btn:disabled {
  color: var(--rd-ctrl);
  border-color: var(--rd-line);
  cursor: default;
}

.rd-prome-sqe__ord-btn:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* `Nobody here yet.` / `Nobody unassigned.` — the desk's two sentences, in this
   part's message register. */
.rd-prome-sqe__none {
  padding: 10px 12px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

/* `padding: '11px 12px', border: 1px dashed` — the drawn add row, verbatim. A
   dashed boundary is the one place in this part where a control is drawn as the
   outline of a thing that is not there yet. Full width, because it is a row. */
.rd-prome-sqe__add {
  appearance: none;
  -webkit-appearance: none;
  display: block;
  width: 100%;
  min-height: 44px;
  margin-top: 10px;
  padding: 11px 12px;
  background: none;
  border: 1px dashed var(--rd-ctrl);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  line-height: 1.4;
  text-align: left;
  color: var(--rd-fg2);
  cursor: pointer;
}

.rd-prome-sqe__add:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* `display: flex, gap: 8, marginTop: 10` — the drawn bottom bar. Both acts are
   `flex: 1` in the frame; with nothing checked the move act is absent and Save
   takes the width, which is the same geometry with one child. */
.rd-prome-sqe__bar {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}

/* The move — the volt fill and the 7px chamfer, the drawn commit. Not
   `.rd-pro-top__btn`: that box is `10px 18px` and hugs its label, and this one
   is half a bar. */
.rd-prome-sqe__move {
  appearance: none;
  -webkit-appearance: none;
  flex: 1;
  min-height: 44px;
  background: var(--rd-volt);
  border: 0;
  color: var(--rd-on-volt);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 11px;
  cursor: pointer;
  clip-path: polygon(0 0, calc(100% - 7px) 0, 100% 7px, 100% 100%, 7px 100%, 0 calc(100% - 7px));
}

/* Save is the OUTLINED half of the drawn pair, which is the frame's own
   statement about the two: the move is the act you are in the middle of, the
   save is the one that ends the session. */
.rd-prome-sqe__save {
  appearance: none;
  -webkit-appearance: none;
  flex: 1;
  min-height: 44px;
  background: none;
  border: 1.5px solid var(--rd-ctrl);
  color: var(--rd-fg);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 11px;
  cursor: pointer;
}

/* ⚠️ THE WORD DOES NOT CHANGE WHEN THE BUTTON IS DISABLED, AND IT DOES NOT
   CHANGE WHILE SAVING (`43 Loading States`, and `SaveState::label` answers
   `Save` in all three states). What changes is the ink. */
.rd-prome-sqe__save:disabled {
  color: var(--rd-fg3);
  border-color: var(--rd-line);
  cursor: default;
}

.rd-prome-sqe__move:focus-visible,
.rd-prome-sqe__save:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* ⚠️ THE CONSOLE'S PHONE-SHEET GRAMMAR, TAKEN WHOLE — NOT A THIRD SPELLING.
   `.rd-prom__overlay` + `.rd-prom__sheet` (the org sheet, adm0-mobile.css) and
   `.rd-wizm__pickwrap` + `.rd-wizm__sheet` (the wizard's picker, adm1-wiz.css)
   are the same three rules: a `position: fixed; inset: 0; z-index: 40` wrap, an
   absolute scrim inside it, and an absolute bottom-anchored sheet capped at
   `max-height: 80%` that scrolls its own rows. This is the third of them.

   CORRECTED. This first shipped as two `position: fixed` siblings with the
   sheet at `max-height: 70vh`, which is a fourth scroll model in a console that
   has one — `pro/shell.rs`'s `ruling_no_console_part_declares_a_competing_
   scroller` said so. The wrap is what makes the cap a percentage of a real
   containing block rather than a fraction of the viewport claimed by hand, and
   it is why `.rd-prome-sqe__sheet` can join SCROLL_ALLOWLIST on the same one-line
   reason its two siblings already carry: an overlay capped at `max-height: 80%`
   is not a region inside `.rd-pro-page` at all. */
.rd-prome-sqe__pickwrap {
  position: fixed;
  inset: 0;
  z-index: 40;
}

/* `background: rgba(0,0,0,.55)` — the drawn scrim, and the same literal both
   sibling sheets use. A dimming of whatever is behind it rather than a surface
   colour, so it does not flip with the theme. */
.rd-prome-sqe__scrim {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, .55);
}

/* The sheet: bottom-anchored, full width, capped and scrolling its own
   destinations — a real event can hold more squads than a drawn frame shows,
   and a picker that cannot reach its last row is a picker with rows nobody can
   pick. The drawn 40px under the last destination is the home indicator's. */
.rd-prome-sqe__sheet {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  max-height: 80%;
  overflow-y: auto;
  padding-bottom: 40px;
  background: var(--rd-surf);
  border-top: 1px solid var(--rd-line);
}

/* `padding: '15px 16px 11px'` — the drawn head, verbatim. */
.rd-prome-sqe__sheet-head {
  padding: 15px 16px 11px;
  border-bottom: 1px solid var(--rd-line);
}

/* `tdisp(900, 66, '.01em')` at 15 — the sheet's own title register. */
.rd-prome-sqe__sheet-title {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 900;
  font-stretch: 66%;
  letter-spacing: .01em;
  font-size: 15px;
  color: var(--rd-fg);
}

.rd-prome-sqe__sheet-note {
  margin-top: 4px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.45;
  color: var(--rd-fg3);
}

/* `minHeight: 46, padding: '0 16px'` — one destination. A full-width row, and a
   real button: one tap moves and closes. */
.rd-prome-sqe__dest {
  appearance: none;
  -webkit-appearance: none;
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 46px;
  padding: 0 16px;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.4;
  text-align: left;
  color: var(--rd-fg);
  cursor: pointer;
}

/* Where they already are. Printed and quieted rather than hidden, so the reader
   can see which row is not a move. */
.rd-prome-sqe__dest--here {
  color: var(--rd-fg3);
  cursor: default;
}

.rd-prome-sqe__dest:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}
/* ==== ADM1-DRAFT · 52 Events Lab frame 10b — the draft row's context · owner: 1-track3 ==== */
/*
   Drawn in `rd-events-lab.jsx` (`LabEventsDraft`) under
   `52 (Desktop) Events Lab.html`, and in `rd-events-lab-mobile.jsx`
   (`MLabEventsDraft`) under `52 (Mobile) Events Lab.html`. Component:
   apps/web/src/pages/admin/events.rs, which carries every copy pin and the
   undrawn-trigger flag. Only the values live here.

   ANATOMY:

     [ .rd-evdraft        — role=rowgroup, one draft inside the desktop ProTable ]
       [ ProRow           — kit-pro-table.css; the four drawn columns, unchanged ]
         [ .rd-evdraft__namecell — the name over the resume sentence ]
       [ .rd-evdraft__trigger    — the menu button, in the list's right gutter ]
       [ .rd-evdraft__away       — the click-away behind the popover ]
       [ .rd-evdraft__pop        — the popover: Resume · Discard draft… ]

     [ .rd-evdraft-m      — the phone pair: the card and its trigger tile ]
       [ RdRowLink        — kit-row.css; the card and everything inside it ]
       [ .rd-evdraft-m__trigger ]

     [ .rd-evsheet        — the phone bottom sheet, mounted at page level ]
       [ .rd-evsheet__scrim · .rd-evsheet__panel > __title · __act ]

   ⚠️ WHY THIS IS A PART AND NOT A BLOCK APPENDED TO AN EXISTING ONE. Three
   files could have held it and none may:

     - `_legacy-events-list.css` holds `.rd-adm-ev*` and is the UNOWNED
       DELETE-ONLY pool. Its own banner: "no lane owns this file, so no lane may
       add to it". An addition there rebuilds the shared address the split
       removed.
     - `adm1-3.css` (owner: lane4) and `adm-mob.css` (owner: lane-drills) are
       other lanes' sections; `order.txt`'s banner makes each part's own header
       the authority for ownership, and `rangeday check css` reconciles the two.
     - This is a different DOCUMENT besides: 52 Events Lab, not 32 Pro Events and
       not 46 Pro Destinations. `s56-loc.css` is the precedent one drawing over —
       the shooter half of the same numbered document took its own part for the
       same reason.

   ⚠️ NO `@media` IN THIS FILE. The breakpoint is asked ONCE, in Rust
   (`device::use_device_class`), and these rules paint what it mounted — the same
   call `adm-mob.css` and `adm0-mobile.css` make, and for the same reason: a
   second statement of the threshold in CSS drifts against the Rust one and the
   two disagree at exactly the boundary width.

   ⚠️ NO NEW TOKENS, AND NO NEW ROW GEOMETRY. The row is `ProRow` / `RdRowLink`
   and every cell width, hairline and hover ground is the kit's; this part
   declares the sub-line, the trigger, the popover and the sheet, which is all
   frame 10b adds to a row that already exists.
   --------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
   1. The desktop draft row — LabEventsDraft.
   --------------------------------------------------------------------------- */

/* A `role="rowgroup"` between the table and its row, because the trigger and
   the popover are SIBLINGS of the row and not children of it: `ProRow` renders
   a router Link over the whole row when `to` is set, and a control nested in an
   anchor is invalid markup and navigates when it is pressed. `rowgroup` is the
   one child role `role="table"` accepts that may itself hold a row, so the
   nesting costs the tree nothing.

   `display: contents` is NOT used: the popover is positioned against this box,
   and a box with `display: contents` generates none. `.rd-ptable` is a flex
   column, so this is one flex item holding one full-width row. */
.rd-evdraft {
  position: relative;
}

/* The name cell grows a second line. `rd-pro-events.jsx`'s live row is one span;
   `LabEventsDraft` draws `<div>{name}</div>` over `<div>Left at step 4 of 9 …
   </div>` inside the same `flex: 1, minWidth: 0` cell. */
.rd-evdraft__namecell {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* `pbody, fontSize: 11, color: t.fg3, marginTop: 3` — LabEventsDraft's sub-line,
   verbatim. It is the drawing's own quiet grey rather than the name's, and it
   truncates rather than wrapping: the row's height is the table's, and a draft
   with a long name may not be taller than the events beside it. */
.rd-evdraft__sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  margin-top: 3px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ⚠️ UNDRAWN. `LabEventsDraft` draws the popover ALREADY OPEN and draws nothing
   that opens it; so does the phone frame. The acts inside are entirely the
   drawing's and this target is entirely ours — raised as a designer question in
   the PR that added it.

   It sits in the list's own right gutter (`.rd-adm-ev` is `padding: 4px 32px
   32px`), which is why the offset is negative: the four drawn column widths are
   the drawing's and a fifth column would move every cell on the screen. 24px in
   a 32px gutter leaves the hairline clear on both sides. */
.rd-evdraft__trigger {
  position: absolute;
  top: 50%;
  right: -28px;
  transform: translateY(-50%);
  z-index: 1;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  background: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 14px;
  line-height: 1;
  color: var(--rd-fg3);
  cursor: pointer;
}

.rd-evdraft__trigger:hover {
  color: var(--rd-fg);
}

.rd-evdraft__trigger:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* The click-away. A real button so the popover is dismissible from the keyboard
   as well as by a press — the same call `.rd-prom__scrim` makes for the org
   sheet. Transparent, unlike that one: this is a popover over a page that is
   still readable, and dimming a whole console to show a two-item menu would be
   a modal treatment the drawing does not give it. */
.rd-evdraft__away {
  position: fixed;
  inset: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: default;
}

/* `position: absolute, top: 58, right: 130, width: 190, background: t.surf,
   border: 1px t.line, boxShadow: '0 12px 32px rgba(0,0,0,.45)'` —
   LabEventsDraft's own values. `top: 58` is measured from the list box in the
   drawing, where the draft is the first row; anchored to the ROW instead, the
   same distance is the row's own bottom edge, so it is written that way here
   and cannot drift when a draft is not the first row. `right: 130` is likewise
   the drawn distance from the list's right edge; against this row it is the
   same edge.

   ⚠️ THE SHADOW IS A LITERAL AND STAYS ONE. It is a dimming of whatever sits
   behind the panel rather than a surface colour, so it does not flip with the
   theme — the same call `.rd-prom__scrim` states at length. */
.rd-evdraft__pop {
  position: absolute;
  top: 100%;
  right: 130px;
  z-index: 3;
  width: 190px;
  background: var(--rd-surf);
  border: 1px solid var(--rd-line);
  box-shadow: 0 12px 32px rgba(0, 0, 0, .45);
}

/* `padding: '12px 16px', pbody, fontSize: 13, color: t.fg`, with the hairline
   under every item but the last — the drawing puts `borderBottom` on Resume and
   none on Discard. */
.rd-evdraft__act {
  display: block;
  width: 100%;
  box-sizing: border-box;
  padding: 12px 16px;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  background: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.rd-evdraft__act:last-child {
  border-bottom: 0;
}

.rd-evdraft__act:hover {
  background: var(--rd-surf2);
}

.rd-evdraft__act:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* `color: '#FF5A3C'` in the drawing, which is `--rd-danger` exactly — the same
   identity `s13-plan.css` records for the same hex. The token, not the literal,
   so the light theme gets its own darker red rather than a value calibrated for
   a dark ground.

   ⚠️ TEXT, NOT A FILL. The fill is spent on the CONFIRM — `48 Kit Rules.html`:
   "Confirmed irreversible, on the confirm itself: danger fill (dfill, white
   label) — never volt". A menu item that already wears the loudest treatment
   leaves the confirm nothing to escalate to. */
.rd-evdraft__act--danger {
  color: var(--rd-danger);
}

/* ---------------------------------------------------------------------------
   2. The phone draft row — MLabEventsDraft.
   --------------------------------------------------------------------------- */

/* The card and its trigger, side by side. `.rd-prom-list` is a `gap: 8` grid, so
   this pair is one of its cells; the card keeps `flex: 1, min-width: 0` and the
   tile never compresses. */
.rd-evdraft-m {
  display: flex;
  align-items: stretch;
  gap: 8px;
}

.rd-evdraft-m > .rd-row {
  flex: 1;
  min-width: 0;
}

/* ⚠️ UNDRAWN, with the desktop trigger and flagged with it. The tile takes the
   card's own ground and chamfer (`tcham(10)` at `MLabEventsDraft`) so it reads
   as part of the row rather than as chrome floating beside it, and 34px clears
   the 44px-minus-gap target the phone rows are drawn to. */
.rd-evdraft-m__trigger {
  flex: none;
  width: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  background: var(--rd-surf);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 14px;
  line-height: 1;
  color: var(--rd-fg3);
  cursor: pointer;
  clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 10px, 100% 100%, 10px 100%, 0 calc(100% - 10px));
}

.rd-evdraft-m__trigger:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* ---------------------------------------------------------------------------
   3. The phone sheet — MLabEventsDraft's `menu` arm.
   --------------------------------------------------------------------------- */

/* `position: fixed`, viewport-anchored, exactly as `.rd-prom__overlay` is: a
   bottom sheet belongs to the screen and not to the row that opened it. The
   z-index clears the phone chrome the shell mounts above the Outlet. */
.rd-evsheet {
  position: fixed;
  inset: 0;
  z-index: 50;
}

/* `background: 'rgba(0,0,0,.55)'` — the drawn scrim, and a LITERAL for the
   reason `.rd-prom__scrim` states: it is a dimming of what is behind it rather
   than a surface colour, and it is the same value in both themes. */
.rd-evsheet__scrim {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: rgba(0, 0, 0, .55);
  cursor: pointer;
}

.rd-evsheet__scrim:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -4px;
}

/* `left/right 0, bottom 0`, ground #141414 / t.surf, `borderTop: 1px t.line`,
   `paddingBottom: 40`.

   ⚠️ THE 40 IS `--rd-sheet-clear` AND NOT A LITERAL. `48 Kit Rules.html`:
   "Nothing tappable sits in the home-indicator zone … every bottom sheet ends
   with 40px under its last act". The token lives in `kit-shell.css` §0 so the
   sheets that carry it cannot drift apart, and this sheet takes it for the same
   reason `.rd-prom__sheet` does — it is viewport-anchored, so nothing above it
   has already spent the clearance. */
.rd-evsheet__panel {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  background: var(--rd-surf2);
  border-top: 1px solid var(--rd-line);
  padding-bottom: var(--rd-sheet-clear);
}

/* `padding: '15px 16px 11px', pdisp(800, 75, '.02em'), fontSize: 15, color:
   t.fg`, hairline under. It names the row — "Steel Night — draft" — which is the
   only thing the sheet says that the desktop popover does not, and the reason it
   says it is that a sheet covers the row it belongs to.

   ⚠️ `__title`, NOT `__head`, AND THE NAME IS LOAD-BEARING. `pro_table.rs`'s
   `ruling_no_admin_page_hand_rolls_a_table_row` sweeps every file under
   `pages/admin` for a `__head` / `__row` class and reads one as a hand-rolled
   table header strip or record row. This is a bottom sheet's title bar over two
   acts — not the table taxonomy at all — and the honest fix is the accurate
   name rather than an exemption for the whole file, which would then excuse a
   real hand-rolled row somebody adds here next month. */
.rd-evsheet__title {
  padding: 15px 16px 11px;
  border-bottom: 1px solid var(--rd-line);
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 75%;
  letter-spacing: .02em;
  font-size: 15px;
  color: var(--rd-fg);
}

/* `padding: '13px 16px', pbody, fontSize: 13, color: t.fg`, hairline under every
   act but the last — one pixel more padding than the desktop popover's 12, and
   the drawing's, not a rounding. */
.rd-evsheet__act {
  display: block;
  width: 100%;
  box-sizing: border-box;
  padding: 13px 16px;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  background: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.rd-evsheet__act:last-child {
  border-bottom: 0;
}

.rd-evsheet__act:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* `color: '#FF5A3C'`, the same identity and the same refusal of a fill as the
   desktop item one section up. */
.rd-evsheet__act--danger {
  color: var(--rd-danger);
}
/* ==== S14-ADD · 14 People — Add A Friend (rd-people.jsx) · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   The code-landing screen, `/add/:code` (`pages/friend_code.rs`, `AddByCode`).

   ⚠️ SCOPE, because the drawing's name is wider than this file's. `14 People`
   draws `ScreenAddFriend` as a SEARCH screen — a name-or-username field, its
   results, and two foot rows (Scan their code / Show my code). **We have no
   route for that screen**; the only way into "add this person" today is a
   scanned deep link, which is what this file paints. The search field, the
   results list and the two foot rows are UNBUILT rather than restyled — see
   the page's own flag. When that screen is built it belongs in this part.

   So the material here is borrowed from the two drawn things this screen
   genuinely is: `PRow2`'s identity block (avatar, name, `@tag · sub`) for who
   the code resolved to, and `ScreenPerson`'s foot for the one action —
   `Add Friend` as the screen's single fill, and the quiet two-line
   "Request sent" block after it, on the argument that *"sending is loud;
   undoing is not"*.

   The frame, heading, button and banner are all kit; only the row rhythm and
   the two quiet type roles the kit does not carry are page CSS — the same
   split `s16.css` states.
   --------------------------------------------------------------------------- */
.rd-add {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
}

/* The identity block. Drawn as a 60-high row with an 11px gutter beside the
   mark; here it heads the page rather than sitting in a list, so it takes the
   row's geometry without the row's bottom hairline — there is no next row for
   a divider to divide it from. */
.rd-add__who {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 60px;
  margin-top: 15px;
}

.rd-add__ident {
  flex: 1;
  min-width: 0;
}

.rd-add__name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .04em;
  font-size: 12.5px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The handle line, and it is the row's second job: on the drawn row this is
   where a result explains itself. Ours carries the handle when the resolved
   user has one, and nothing when they do not — an empty `@` would assert a
   handle that is not there. */
.rd-add__tag {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The foot. `margin-top: auto` puts the action at the bottom of the shell the
   way the drawing's `flex: 'none'` foot does, without pinning it — a short
   viewport scrolls to it rather than overlapping the content above. */
.rd-add__foot {
  flex: none;
  margin-top: auto;
  padding-top: 14px;
}

/* The banner sits ABOVE the control it governs, which is the kit's standing
   rule and the reason a send failure no longer replaces the whole person. */
.rd-add__send-error { margin: 0; }

/* "Request sent". Quiet on purpose — the drawing spends the screen's one loud
   treatment on the send and none on its aftermath.

   ⚠️ **PIXEL-TIED TO `.rd-person__sent*` IN s14-person.css.** Two surfaces
   render the same drawn foot and the drawing gives them one treatment. The tie
   is asserted, not asked for: `user_profile.rs` compares the two rule bodies out
   of the shipped sheet and fails naming the property that drifted. Edit one,
   edit both. */
.rd-add__sent {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 48px;
  border-top: 1px solid var(--rd-line);
  padding-top: 11px;
}

/* The copy column — see the twin in s14-person.css. */
.rd-add__sent-copy {
  display: flex;
  flex-direction: column;
  gap: 3px;
  flex: 1;
  min-width: 0;
}

/* The `Undo`. Quieter than the thing it undoes — see the twin. */
.rd-add__undo {
  flex: none;
  border: 0;
  background: none;
  padding: 0 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-fg3);
  cursor: pointer;
}

.rd-add__undo:disabled { cursor: default; opacity: .55; }

.rd-add__undo:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

.rd-add__sent-h {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 10.5px;
  color: var(--rd-fg2);
}

.rd-add__sent-p {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
  line-height: 1.4;
}

/* The dead-code state. The drawn `none` state is two steps of body copy, not a
   banner: a code that has expired is an ordinary thing that happens on a
   five-minute timer, and painting it as a failure would overstate it. Same
   two-step shape the drawing gives "Nobody by that name." */
.rd-add__none {
  margin-top: 16px;
  flex: 1;
  min-height: 0;
}

.rd-add__none-h {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg2);
  line-height: 1.5;
}

.rd-add__none-p {
  margin-top: 6px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  line-height: 1.5;
  text-wrap: pretty;
}

/* ---------------------------------------------------------------------------
   THE SEARCH SCREEN — `/profile/friends/add` (`pages/add_friend.rs`).

   ⚠️ THIS IS THE SCREEN THE HEADER ABOVE SAID WAS UNBUILT. `ScreenAddFriend`
   drawn in full — the name-or-username field, the results under it in the same
   screen, the two-step no-match state and the two labelled code rows — now that
   it has a route. The prediction it was left against ("when that screen is
   built it belongs in this part") is honoured rather than a second part being
   cut; the drawing is one frame and its CSS is one place.

   ⚠️ TWO PREFIXES IN ONE PART, AND THEY ARE DIFFERENT SCREENS. `.rd-add*` above
   is the code LANDING (`/add/:code`), which borrows this drawing's frame for a
   scanned deep link. `.rd-af*` here is the drawn search screen. They share a
   drawing and nothing else — one resolves a code and has no field, the other
   searches and never sees a code — so they are name-disjoint and neither
   declares a selector the other names.

   ⚠️ THE ROW GEOMETRY IS `PRow2`'s AND IT IS COPIED, NOT SHARED. `.rd-fr__row`
   in s14-fr.css carries the same numbers. Sharing would mean one screen's part
   reaching into another's, which the four-parts-off-drawing-14 rule in
   s14-fr.css's header exists to prevent — the parts are name-disjoint so their
   ORDER is not load-bearing, and a cross-part selector would silently make it
   so. The duplication is the price of that and it is the cheaper side.
   --------------------------------------------------------------------------- */

.rd-af {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  padding-top: 4px;
}

/* The wrapper class the screen hands `RdSearchField`. A bare `margin-top`, not
   a flex row with a button beside it — item 4 retired the button — which is why
   the TABLET modal (`friends.rs`'s TabAddFriendModal) reuses it verbatim.

   ⚠️ `.rd-af__search-sweep` WAS HERE and is retired. The in-flight mark is the
   kit field's own end slot now (`.rd-search__sweep`, `au5.css`), which is the
   same four declarations this rule held — written once instead of three times
   across this part, `s16.css` and `adm-plat.css`. See
   `docs/design/kit/A6-search-field.md` §8.1. */
.rd-af__search { margin-top: 15px; }

.rd-af__go { margin-top: 10px; }

/* The results region. `min-height: 0` is what lets it scroll inside the shell's
   flex column instead of pushing the two code rows off the bottom. */
.rd-af__body {
  flex: 1;
  min-height: 0;
  margin-top: 16px;
}

.rd-af__error { margin: 0; }

.rd-af__list { margin-top: 4px; }

/* `PRow2` — see the header note on why this is a copy. */
.rd-af__row {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 60px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-af__row:last-child { border-bottom: none; }

.rd-af__open {
  display: flex;
  align-items: center;
  gap: 11px;
  flex: 1;
  min-width: 0;
  text-decoration: none;
}

.rd-af__ident {
  flex: 1;
  min-width: 0;
}

.rd-af__name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .04em;
  font-size: 12.5px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The row's second line. Drawn as `@{tag} · {sub}`; ours is the handle alone.
   The `sub` half here is "2 friends in common", which the drawing calls *"the
   fastest disambiguator there is"* — and it has no source: no RPC, no field, no
   column, and mutual-friends is not planned. Omitted rather than zeroed, because
   "0 friends in common" would assert a figure nobody computed. */
.rd-af__tag {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Add, and its aftermath. A word in the accent rather than a bordered control:
   the row is the object and the word is what you do to it. Never a fill —
   *"anything drawn per row cannot take a fill."* */
.rd-af__add,
.rd-af__sent {
  flex: none;
  border: 0;
  background: none;
  padding: 0 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 9.5px;
}

.rd-af__add {
  color: var(--rd-acc);
  cursor: pointer;
}

.rd-af__sent { color: var(--rd-fg3); }

/* The two-step no-match state. *"Usernames are exact"* is the actual fix for a
   misspelling; "try again" is not. */
.rd-af__empty {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 28px 0 40px;
}

.rd-af__empty-h {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  line-height: 1.5;
  color: var(--rd-fg2);
}

.rd-af__empty-p {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.5;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

/* ── The two code doors ─────────────────────────────────────────────── */

/* `margin-top: auto` puts them at the foot of the shell the way the drawing's
   `flex: 'none'` foot does, without pinning — a short viewport scrolls to them
   rather than overlapping the results above. */
.rd-af__doors {
  flex: none;
  margin-top: auto;
  padding-top: 12px;
  border-top: 1px solid var(--rd-line);
}

/* A real `button`, so the keyboard and the screen reader get a control, wearing
   a row's geometry. The second row takes the hairline between the two; the
   first does not, because `.rd-af__doors` already drew one above it. */
.rd-af__door {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  min-height: 48px;
  padding: 0;
  border: 0;
  background: none;
  text-align: left;
  cursor: pointer;
}

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

.rd-af__door-copy {
  flex: 1;
  min-width: 0;
}

.rd-af__door-name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 12px;
  color: var(--rd-fg);
}

.rd-af__door-sub {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
}

.rd-af__chev {
  flex: none;
  font-size: 15px;
  line-height: 1;
  color: var(--rd-fg3);
}

/* ── The code, as a layer ───────────────────────────────────────────── */

/* *"Ephemeral gets a layer, durable gets a page"* — a code that dies in five
   minutes is the ephemeral end of that rule, which is why `profile.rs` hosts
   the same component the same way. The wrapper is duplicated for the reason the
   row geometry is: `.rd-prof__over*` belongs to the profile's part.

   ⚠️ ADDED 2026-08-19: `s14-tablet.css`'s tablet Add Friend modal
   (`TabAddFriendModal`, relay-5 item 7) mounts this whole block VERBATIM for
   its own `show_my_code` swap and for its own scrim — no third copy of the
   overlay mechanics for the same card. See that part's own header. */
.rd-af__over {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px;
  box-sizing: border-box;
  background: rgba(6, 6, 6, .82);
}

/* 250px, and the number is the drawing's rather than a round one: the panel is
   sized by a QR held up at arm's length, not by prose. Same value `s18-foot.css`
   gives `.rd-prof__over-panel--code` for the same component. */
.rd-af__over-panel {
  width: 100%;
  max-width: 250px;
  max-height: 100%;
  overflow-y: auto;
  box-sizing: border-box;
  background: var(--rd-surf);
  border: 1px solid var(--rd-line);
}

.rd-af__over-body { padding: 18px 17px 16px; }

.rd-af__over-close-row {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 6px;
}

.rd-af__over-close {
  border: 0;
  background: none;
  padding: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-fg3);
  cursor: pointer;
}

.rd-af__over-close:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }
/* ==== S15 · 15 Events (rd-events.jsx) · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   Both shooter-side event surfaces, because they are one drawing: `/events`
   (`pages/events.rs`, `ScreenEvents`) and `/events/:id` (`pages/event_detail.rs`,
   `ScreenEventDetail`). `rd-evl-*` is the list; `rd-evd-*` is the detail. The
   third screen in the drawing, `ScreenEventSquad`, is `squad_score_entry.rs`
   and is not this part's.

   The frame, heading, chips, banner, empty state, buttons and the avatar mark
   are all kit. What is page CSS is the card — a date column beside a text
   column, which the kit carries no primitive for — the two quiet type roles the
   kit does not carry (`--rd-fg3` meta and the vertical rail), and the row
   rhythm.

   ⚠️ THE DATE BLOCK IS OUTSIDE THE TEXT COLUMN AND IN THE NUMERIC ROLE, and the
   drawing says why in as many words: *"Tabular figures mean the day never
   nudges the title sideways between rows, which is the entire reason a date
   deserves its own column rather than a prefix."* So `.rd-evl-card__day` is
   `rd-num` at the call site and a fixed 40px wide here. Neither is decoration.

   ⚠️ WHAT THE DRAWN CARD SAYS THAT WE CANNOT. The drawn `who` line reads
   "T.REX ARMS · Indoor bay" and the drawn `meta` line reads "3 drills · 350
   rounds · 2 going". `EventDto` carries `org_id` (not the org's NAME),
   `location_text`, and `enrolled_count` — no drill count, no round count. The
   card renders what is on the wire and states nothing it would have to invent;
   the page's own doc flags each absence. Nothing is styled here for a datum
   that does not exist.
   --------------------------------------------------------------------------- */

/* ── The list ─────────────────────────────────────────────────────────────── */

.rd-evl {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
}

/* The other door to the range-day form. On a populated list it sits UNDER the
   title rather than at the bottom, and the drawing says why: the list scrolls,
   and an action that scrolls away is an action you have to hunt for. */
.rd-evl__plan {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  min-height: 44px;
  margin-top: 9px;
  padding: 0 0 9px;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  text-align: left;
  cursor: pointer;
}

.rd-evl__plan:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-evl__plan-text { flex: 1; min-width: 0; }

.rd-evl__plan-name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 12px;
  color: var(--rd-fg);
}

.rd-evl__plan-sub {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
}

/* The 7x12 chevron the kit uses wherever a row opens something. Sized here
   because the mark sets no width or height of its own and takes
   `currentColor`. */
.rd-evl-chev {
  flex: none;
  width: 7px;
  height: 12px;
  color: var(--rd-fg3);
}

.rd-evl__body {
  flex: 1;
  min-height: 0;
  margin-top: 4px;
}

.rd-evl__group { margin-top: 13px; }
.rd-evl__group-lab { margin-bottom: 6px; }

.rd-evl__rows { display: grid; gap: 7px; }

/* One card. A whole-row control, so the `<button>` is outside the box and
   carries no paint of its own — the same construction `s03-drills.css` uses
   for `.rd-tdl-row`, and the reason is the same: a `<button>` cannot nest and
   the chamfer is a two-layer clip. */
.rd-evl-row {
  display: block;
  width: 100%;
  padding: 0;
  background: none;
  border: 0;
  text-align: left;
  cursor: pointer;
}

.rd-evl-row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

.rd-evl-card {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 62px;
  padding: 11px 13px;
}

/* A finished event reads as finished. The drawn `opacity: past ? .7 : 1` is on
   the card's contents rather than on the control, so the focus ring above stays
   at full strength — a dimmed focus ring is a dimmed way out. */
.rd-evl-card--past { opacity: .7; }

.rd-evl-card__date {
  width: 40px;
  flex: none;
  text-align: center;
}

.rd-evl-card__day {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 21px;
  line-height: 1;
  color: var(--rd-fg);
}

/* The dim day, for a card in the Done group. */
.rd-evl-card__day--past { color: var(--rd-fg3); }

/* ⚠️ A MULTI-DAY EVENT COMPRESSES INTO THE BLOCK — it never widens it. Ruled
   18 Aug, drawn on 15 Events as the Autumn Qualifier card: *"`8–9` over `Aug`
   for a same-month range, `30–1` over `Aug–Sep` across a boundary, one size step
   down so the block's 40px never moves and titles stay aligned."*

   THE LINE-HEIGHT IS THE HALF THAT KEEPS THE PROMISE. 21px at line-height 1 and
   14px at line-height 21px are the same line box, so a range row's title sits on
   exactly the baseline a single-day row's does. Changing the font-size without
   it re-flows every card in the list. */
.rd-evl-card__day--range { font-size: 14px; line-height: 21px; }

.rd-evl-card__month {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .14em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

/* Cross-month only: `Aug–Sep` is nearly twice the glyphs of `Aug` in the same
   40px, so the drawing spends the tracking rather than the width. A same-month
   range keeps `.14em` — its month did not get any longer. */
.rd-evl-card__month--range { letter-spacing: .02em; }

.rd-evl-card__copy { flex: 1; min-width: 0; }

.rd-evl-card__name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .04em;
  font-size: 13px;
  line-height: 1.15;
  color: var(--rd-fg);
}

.rd-evl-card__who {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Where on the range, under the line that names it — the second of the two
   place lines C-56.9.1 ruled apart (26 Aug). One step quieter than `__who`
   above it and one step louder than `__meta` below, because it is a detail
   about the place rather than a second subject. */
.rd-evl-card__where {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rd-evl-card__meta {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

.rd-evl-card__chip { margin-top: 5px; }

/* The vertical rail on a personal range day. It is the card's only marker of
   whose day it is — the drawn card carries no type badge — so it reads down
   the right edge where a badge would have taken a line of the text column. */
.rd-evl-card__mine {
  flex: none;
  writing-mode: vertical-rl;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%;
  letter-spacing: .2em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

/* The empty list, centred in the frame the way the drawing centres it. Both
   doors, and the drawing's argument for the order: "find an org" is a long
   road and planning your own Saturday is not. */
.rd-evl__empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 12px;
  padding-bottom: 34px;
}

.rd-evl__empty-lead {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg2);
  line-height: 1.5;
}

.rd-evl__empty-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  line-height: 1.5;
}

.rd-evl__empty-acts { display: grid; gap: 8px; margin-top: 4px; }

/* Pagination as an accent word, matching the arc's treatment for "there is
   more, fetch it". */
.rd-evl__more {
  display: flex;
  align-items: center;
  justify-content: center;
  /* The in-flight sweep sits BESIDE the label, which does not change
     (43 Loading States.html:53) — so the row needs a gap it never needed while
     the word was swapped for another word. */
  gap: 8px;
  width: 100%;
  min-height: 42px;
  margin-top: 8px;
  background: none;
  border: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-acc);
  cursor: pointer;
}

.rd-evl__more:disabled { color: var(--rd-fg3); cursor: default; }

.rd-evl__more:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-evl__banner { margin-top: var(--rd-banner-gap); }

/* ── The detail ───────────────────────────────────────────────────────────── */

.rd-evd {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
}

/* The detail screen entering: the kit's marks in this screen's own rhythm. The
   primitives carry their own geometry (kit-loading.css) and claim no spacing
   between each other, so the gap between the chip-row mark, the name line and
   the card stack is the screen's to state — the same 12px the loaded body puts
   between its own blocks. */
.rd-evd__skel {
  display: grid;
  gap: 12px;
}

.rd-evd__chips { display: flex; gap: 6px; margin-bottom: 8px; }

/* The when-and-where line. `--rd-fg2` is the middle step, which `RdText`
   carries — but this line is a `<div>` inside a flow the page owns, and giving
   it a rule here keeps the 8px lead off the heading in one place. */
.rd-evd__when {
  margin-top: 8px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg2);
  line-height: 1.5;
}

/* Line 2 of the drawn read side — Where on the range, under the line that names
   it (C-56.9.1, 26 Aug). `--rd-fg3` rather than `--rd-fg2`: the range is the
   subject and this is a detail about it. */
.rd-evd__where {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  line-height: 1.5;
}

/* ── Your own RSVP, at the top of the screen ────────────────────────────────
   DRAWN: `41 (Mobile) Event Logistics - Participant and Guest`,
   `rd-logistics-guest.jsx:62-138`.

   ⚠️ IT SITS ABOVE THE CARD AND ABOVE THE ROSTER, and that is the ruling
   rather than a stacking order that happened: *"YOUR OWN RSVP IS THE TOP OF THE
   SCREEN, above the itinerary and above the gear, because it is the only thing
   here that is asking the shooter for something."* (`rd-logistics-guest.jsx:40`)

   The container is the drawing's exactly (`:71`): `marginTop: 14,
   paddingBottom: 14, borderBottom: 1px solid t.line` — a rule UNDER the block
   and nothing above it, because the org label, the name and the when·place·where
   line are what it separates itself from. */
.rd-evd__rsvp {
  flex: none;
  margin-top: 14px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--rd-line);
}

/* The ask. `fontSize: 12.5`, `t.fg`, `marginBottom: 9`
   (`rd-logistics-guest.jsx:104`) — body type, not display: it is a question in
   sentence case, and the drawing spends its display weight on the two buttons
   under it. */
.rd-evd__rsvp-ask {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
  line-height: 1.4;
  margin-bottom: 9px;
}

/* ⚠️ TWO CHILDREN, EACH `flex: 1`, WITH THE DRAWN 8px GAP
   (`rd-logistics-guest.jsx:65`, `:107`). The equal split is the ruling made
   visible — *"THE THREE-STATE CONTROL IS TWO BUTTONS, not three"* (`:46-48`) —
   so neither answer is the easier one to reach.

   `flex: 1` lands on the child rather than on `RdButton`, which has no width
   prop past `wide`; `min-width: 0` because a flex item's default `auto` floor
   would let the pair overflow a narrow phone rather than share it. */
.rd-evd__rsvp-acts {
  display: flex;
  gap: 8px;
}

.rd-evd__rsvp-acts > * {
  flex: 1;
  min-width: 0;
}

/* The answered read-back: the body line and `Change` on one baseline, the
   drawing's `alignItems: 'baseline', gap: 10` (`rd-logistics-guest.jsx:83`). */
.rd-evd__rsvp-read {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-top: 7px;
}

/* `fontSize: 13`, `t.fg` (`rd-logistics-guest.jsx:77`, `:84`). */
.rd-evd__rsvp-body {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg);
  line-height: 1.4;
  margin-top: 7px;
}

/* Inside the read row the body already has the row's own top margin. */
.rd-evd__rsvp-read .rd-evd__rsvp-body { margin-top: 0; }

/* CHANGE, an accent word — `tdisp(800, 78, '.1em')`, `fontSize: 9.5`, `t.acc`
   (`rd-logistics-guest.jsx:85`). A real `<button>`, so it is reset to the word
   the drawing draws: no chrome, no padding of its own, and the accent colour
   carrying the affordance. Same treatment as `.rd-evd-drill__act` above, which
   is the drawing's other accent-word control. */
.rd-evd__rsvp-change {
  flex: none;
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-acc);
}

/* The note under a read-back: the drawn completed sentence
   (`rd-logistics-guest.jsx:78`, `fontSize: 10`, `t.fg3`, `marginTop: 5`), and
   the same slot carries the declined stub's in-product flag. */
.rd-evd__rsvp-note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
  line-height: 1.45;
  margin-top: 5px;
}

.rd-evd__body { flex: 1; min-height: 0; margin-top: 15px; }

.rd-evd__sec-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

.rd-evd__sec { margin-top: 16px; }

/* ⚠️ EMPTY SECTIONS GET COPY INSTEAD OF VANISHING, and that is a decision the
   drawing states rather than a style: *"The shipped detail page omits the
   drills and roster blocks entirely when they are empty, so an event with
   nothing attached renders a title, a date and nothing else."* This rule is
   what those two sentences are set in. */
.rd-evd__sec-empty {
  margin-top: 8px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  line-height: 1.5;
  text-wrap: pretty;
}

.rd-evd__drills { display: grid; gap: 6px; margin-top: 6px; }

/* A drill row hosts TWO controls — score yourself, score the squad — so it
   cannot be one `<button>`: buttons do not nest. The row is the flex parent and
   the card is the first child, which is the same construction `event_detail.rs`
   shipped before the re-skin and the only one available. */
.rd-evd-drill-row {
  display: flex;
  align-items: center;
  gap: 4px;
}

/* A drill row. Scoring lives HERE rather than in a menu, and the drawing's
   reason is physical: *"You are standing on a bay with a phone in one hand."* */
.rd-evd-drill {
  display: block;
  flex: 1;
  min-width: 0;
  padding: 0;
  background: none;
  border: 0;
  text-align: left;
  cursor: pointer;
}

.rd-evd-drill:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

.rd-evd-drill__inner {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 46px;
  padding: 9px 12px;
}

.rd-evd-drill__copy { flex: 1; min-width: 0; }

.rd-evd-drill__name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rd-evd-drill__sub {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* SCORE as an accent word on the row, exactly where the drawing puts it. */
.rd-evd-drill__act {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .14em;
  font-size: 9px;
  color: var(--rd-acc);
}

/* SHOT, where SCORE would be on a row you have not shot yet.
   `A drill already shot shows its hit factor and the word SHOT instead of the
   SCORE affordance, and its outline goes volt.` Smaller and quieter than SCORE
   — 7px against 9px, `--rd-fg3` against `--rd-acc` — because SCORE is an
   instruction and SHOT is a spent one. The drawing heads this block with the
   hit factor; that figure has no field behind it (see `event_detail.rs`), so
   the slot carries the word alone rather than a placeholder number. */
.rd-evd-drill__shot {
  flex: none;
  text-align: right;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .14em;
  font-size: 7px;
  color: var(--rd-fg3);
}

/* The squad door. A second control on the same row, so the row cannot be one
   `<button>` — buttons do not nest. Quieter than SCORE because scoring yourself
   is the common case and scoring the squad is the RO's. */
.rd-evd-drill__squad {
  flex: none;
  padding: 6px 8px;
  background: none;
  border: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .14em;
  font-size: 9px;
  color: var(--rd-fg3);
  cursor: pointer;
}

.rd-evd-drill__squad:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-evd__roster { margin-top: 7px; }

.rd-evd-person {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 34px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-evd-person:last-child { border-bottom: none; }

.rd-evd-person__name {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The right-hand mark on a roster row. The drawing spends it on "You" /
   "Organiser"; ours carries the placeholder state, which is the one fact about
   an enrolled person the wire actually gives us. */
.rd-evd-person__mark {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .14em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

.rd-evd__foot {
  flex: none;
  margin-top: auto;
  padding-top: 11px;
  padding-bottom: 12px;
  display: grid;
  gap: 8px;
}

.rd-evd__foot-note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
  line-height: 1.45;
}
/* ==== S17 · 17 Leaderboard (rd-leaderboard.jsx) · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   The board for one drill — `/leaderboard/:drill_id?:from`
   (`pages/leaderboard.rs`). Drawn: `ScreenLeaderboard`, `LBRow`, `LBPeriod`.

   The frame, heading, scope chips, banner, empty state and buttons are all
   kit. What is page CSS is the row — a ranked line the kit carries no
   primitive for — the inert period group, and the position strip at the foot.

   ⚠️ THE ROW IS THREE COLUMNS AND THE OUTER TWO ARE NUMERIC. The rank and the
   value both take `rd-num` (tabular figures, substrate §P2) for the same
   reason the friend-code countdown does: a board re-sorts and re-paginates
   under the reader, and proportional figures make every column edge move when
   the digits change. The drawing spends `mono` on both and nothing else.

   ⚠️ WHAT IS NOT HERE, AND WHY IT IS ABSENCE RATHER THAN OMISSION. The drawn
   row's second line is `@handle · Recorded by brc_range` / `· From the old
   app`, and the drawn foot carries "Scores from the old app don't rank here."
   `LeaderboardEntryDto` is `user_id`, `display_name`, `value`, `rank` — no
   handle, no provenance tag, no legacy flag. Every one of those would have to
   be invented per row. The page's own doc flags them; nothing is styled here
   for a datum that does not exist.
   --------------------------------------------------------------------------- */
.rd-lb {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
}

/* The subtitle under the drill name. `--rd-fg3` is the meta step, which
   `RdText` deliberately does not carry (it owns `--rd-fg2` and nothing else),
   so this is a page rule for the same reason `s16.css`'s footnote is. */
.rd-lb__sub {
  margin-top: 6px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
}

.rd-lb__scopes { margin-top: 12px; }

/* The org switcher's row, directly under the scope chips — `ScreenLeaderboard`
   draws it at `marginTop: 7` when the scope is Org, one `TChip` per org the
   shooter belongs to. Spacing only: the row itself is `RdChipSelector`, so the
   chip treatment, the fill-flip lit state and the keyboard are the kit's. */
.rd-lb__orgs { margin-top: 7px; }

/* The count line and the period group share one baseline row. */
.rd-lb__meta {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 15px;
}

.rd-lb__count {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .12em;
  font-size: 10px;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   The period group. Three words, not a menu — the drawing's own argument is
   that a dropdown costs a tap and a sheet to say the same thing.

   ⚠️ INERT, AND DRAWN AS THE LIVE CONTROL ANYWAY. Nothing on the wire carries
   a period, so all three would return the same board today; the buttons are
   `disabled` and the page states why beneath them. The `--rd-ctrl` boundary is
   the drawing's `stroke={t.ctrl}` and is also the WCAG 1.4.11 floor for a
   control's own outline — a disabled control still has to be visible as one,
   or the reason-line below it explains something the reader cannot see.
   --------------------------------------------------------------------------- */
.rd-lb__period {
  display: flex;
  flex: none;
  border: 1px solid var(--rd-ctrl);
}

.rd-lb__period-cell {
  padding: 5px 7px;
  background: none;
  border: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 8.5px;
  color: var(--rd-fg3);
  cursor: not-allowed;
}

/* Every cell but the first carries the divider, so the group reads as one
   control with three cells rather than three controls in a box. */
.rd-lb__period-cell + .rd-lb__period-cell { border-left: 1px solid var(--rd-line); }

/* The lit cell. Accent TEXT on no fill — `--rd-acc`, never `--rd-volt`, per
   the token layer's own volt/acc split. */
.rd-lb__period-cell--on { color: var(--rd-acc); }

.rd-lb__stub-note {
  margin-top: 6px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
}

.rd-lb__banner { margin-top: var(--rd-banner-gap); }

.rd-lb__empty { padding: 24px 0 8px; }
.rd-lb__empty-act { margin-top: 16px; }

.rd-lb__list {
  flex: 1;
  min-height: 0;
  margin-top: 8px;
}

/* ---------------------------------------------------------------------------
   One ranked row. Rank, mark, who, the number.
   --------------------------------------------------------------------------- */
.rd-lb__row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 48px;
  border-bottom: 1px solid var(--rd-line);
  /* A board can run to a thousand ranked rows, so off-screen ones skip layout
     and paint. 49px = the 48px `min-height` (which already contains the 32px
     avatar and both text lines) + the 1px hairline; `auto` keeps the measured
     height, which is what covers a row whose handle line wraps. */
  content-visibility: auto;
  contain-intrinsic-size: auto 49px;
}

.rd-lb__row:last-child { border-bottom: none; }

.rd-lb__rank {
  width: 20px;
  flex: none;
  text-align: right;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

.rd-lb__who {
  flex: 1;
  min-width: 0;
}

.rd-lb__name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rd-lb__value {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 14px;
  color: var(--rd-fg);
}

/* Your own row. Three marks change together — rank, name and value all go
   accent, and the name gains weight — because the drawing's whole claim is
   that on a paginated board your row is the one thing you came to find. The
   modifier is ADDED to the base so a state change never re-bases an element. */
.rd-lb__rank--me { color: var(--rd-acc); }
.rd-lb__name--me { font-weight: 700; color: var(--rd-acc); }
.rd-lb__value--me { color: var(--rd-acc); }

/* Pagination as an accent WORD, matching the arc's existing treatment for
   "there is more, fetch it" (`.rd-cruns__more-btn` in train.css, and the
   drawing's own centered `Load More`). No top border: the last list row keeps
   its own divider, so a row whose only paint is a rule would read as an empty
   sixth entry. */
.rd-lb__more {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 42px;
  background: none;
  border: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-acc);
  cursor: pointer;
}

.rd-lb__more:disabled { color: var(--rd-fg3); cursor: default; }

.rd-lb__more:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* The row's second line: the handle, and what the row has to explain about
   itself. One line doing two jobs, so a board of five rows is not a board of
   ten — the drawing's own phrasing. */
.rd-lb__handle {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   The position strip. Fixed at the foot, outside the list, because the gap to
   the shooter above you is the only actionable number on a board and scrolling
   for it is the shipped failure.

   ALL THREE OF THE DRAWING'S STATES ARE PAINTED now that the response carries
   the caller's position: the gap sentence when your row is on screen, your
   whole row when it is forty pages down, and the prompt when you have never
   shot the drill. `.rd-lb__you` is the FRAME the three share — it owns the rule
   and the spacing and nothing else, so no state re-bases the element.
   --------------------------------------------------------------------------- */
.rd-lb__you {
  flex: none;
  border-top: 1px solid var(--rd-line);
  padding-top: 10px;
  margin-top: 10px;
}

/* State one: your row is visible above, so the strip carries what the row
   cannot — the gap. */
.rd-lb__you-gap {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg2);
  line-height: 1.45;
}

/* State two: your row is not on the page. The strip IS your row — same three
   accented marks as `--me` in the list, because it is the same row. */
.rd-lb__you-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.rd-lb__you-note {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* State three: you have never shot it. A prompt and the one control that
   answers it. */
.rd-lb__you-prompt {
  display: flex;
  align-items: center;
  gap: 11px;
}

.rd-lb__you-title {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .08em;
  font-size: 10.5px;
  color: var(--rd-fg2);
}

.rd-lb__you-sub {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
}

.rd-lb__you-act {
  flex: none;
  width: 108px;
}
/* ==== S19-PREFS · 19 Profile Pages — Settings (rd-profile-forms.jsx) · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   `/profile/settings` (`pages/notification_prefs.rs`). Drawn:
   `ScreenSettings` — both `PGroup`s, `PSeg`, `PActionRow`, `PToggleRow`, and a
   centred foot. `PToggle`'s dim state is now only HALF the toggle's story —
   see below.

   ⚠️ SCOPE, CORRECTED 2026-08-15. This block used to flag the drawn screen as
   WIDER than the route: Display had no surface anywhere in the app and the page
   shipped the Notifications group alone. The Display group is now built, so the
   flag is retired rather than repeated. What is here is the whole drawn screen.

   THE SEGMENT IS NOT IN THIS FILE, deliberately. The theme control is
   `RdSegmented`, a kit primitive, and its values live in `kit-segmented.css`
   (owner: spine). This file owns only the row that holds it — its padding, its
   hairline and the caption underneath. Reaching in here to restyle `.rd-seg*`
   would put a primitive's appearance in a page's part, which is the drift the
   part split exists to prevent.

   ⚠️ THE TOGGLE WENT LIVE, 2026-08-15 — retiring the paragraph that used to
   sit here. `GetNotificationPreferences` / `UpdateNotificationPreferences` had
   no client binding when this was written, so the drawing's `on off` pair
   (`PToggle`'s value prop and its disabled prop, both true) was drawn as a
   locked default-on switch, and volt was deliberately withheld: *"a filled
   track is what a live switch looks like,"* and there was no live switch yet.
   There is now. Each row fetches and sends its OWN account value — `on` and
   `off` are both real states a person can be in, not one locked default — and
   a genuinely-`on` row spends the volt fill this file used to reserve. See
   `.rd-nprefs__toggle-inner--on` / `--off` below — the outer track stays one
   static ground for both states; only the inner layer and the knob vary.

   ⚠️ TWO LAYERS, AND THE DRAWING SAYS WHY: *"A hairline track has to be two
   layers: a border on the same element as the clip-path loses its stroke along
   the diagonal."* So the track is a `--rd-line` ground with an inner inset by
   1px, each chamfered — never one element with a border. This is the same
   construction `RdChamferBox` makes, hand-rolled here for the one reason
   `adm0-picker.css` states for its steppers: no kit primitive serves a toggle,
   and `Fill` carries no `--rd-chip` variant to force it into. The inner
   layer's fill and the knob position now vary with the account value — see
   `.rd-nprefs__toggle-inner--on` / `--off` below — so `--rd-chip` is one of
   its two colours rather than its only one.
   --------------------------------------------------------------------------- */
.rd-nprefs {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
}

/* The drawn `PGroup`: a label, then a hairline-topped stack. No card — the
   sub-page reads as the same material as the profile list that reached it.

   ⚠️ THE GROUP'S TOP MARGIN MOVED, 2026-08-15, and it is a correction rather
   than a taste. `PGroup` spaces the WHOLE group — `marginTop` sits on the
   wrapper, above the label. This file had it on the stack instead, which is
   invisible with one group on the page (the heading's own spacing absorbed it)
   and wrong the moment there are two: the label would have sat tight under the
   heading and 15px clear of its own rows. It is now on the label, which is the
   group's first element, so `.rd-nprefs__lab` + `.rd-nprefs__group` reproduce
   `PGroup` without a wrapper element. */
.rd-nprefs__group {
  border-top: 1px solid var(--rd-line);
}

.rd-nprefs__lab {
  margin-top: 15px;
  margin-bottom: 8px;
}

/* The Display group is drawn at `top={18}` rather than the default 15 — the
   extra 3px under the page heading. Drawn value, not a rounding. */
.rd-nprefs__lab--first { margin-top: 18px; }

/* ---- Display ------------------------------------------------------------- */

/* The segment's row. Drawn `padding: '11px 0 12px'` with the group's hairline
   below it; the segment itself is `RdSegmented` and paints nothing here. */
.rd-nprefs__theme {
  padding: 11px 0 12px;
  border-bottom: 1px solid var(--rd-line);
}

/* The caption under the segment. 10.5px `--rd-fg3` — the meta step, quieter
   than the group note below (11px) because it explains a control rather than
   the state of a whole group. */
.rd-nprefs__theme-note {
  margin-top: 8px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  line-height: 1.4;
  text-wrap: pretty;
}

/* The drawn `PActionRow`. It is drawn at its `last` variant — no bottom
   hairline — and it closes the Display group, so nothing is drawn under it.

   ⚠️ IT WAS NOT ALWAYS LAST (2026-08-27 → 2026-08-29), and the rule that
   handled that is gone with the row that needed it. An experimental, undrawn
   Fullscreen row sat below this one inside the same group, so the hairline was
   drawn by POSITION — `.rd-nprefs__act:not(:last-child)` — rather than by the
   drawn `last` variant. James ruled that row out on 2026-08-29, this row closes
   the group again, and the positional rule is deleted rather than left standing
   as a guard for a sibling that cannot come back without a design decision.

   ⚠️ IT IS A STUB AND IT IS NOT DIMMED. That is the opposite call from the
   toggle rows below, and the difference is what the two are being honest
   about: the toggles are ON and unchangeable, so the dim says "you cannot move
   these"; Text Size has no state at all and no mechanism behind it, so dimming
   it would claim a disabled control where there is only an unbuilt one. Drawn
   at full fidelity, doing nothing — see the flag at the call site. */
.rd-nprefs__act {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 52px;
  padding: 0 2px;
}

.rd-nprefs__act-name {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 12px;
  color: var(--rd-fg);
}

/* The right-aligned fact. `--rd-fg2`, the drawn `t.fg2` — one step above the
   sub-line greys, because it is a value rather than a caption. */
.rd-nprefs__act-value {
  flex: none;
  max-width: 130px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg2);
}

/* Drawn 7x12, `--rd-fg3`, 1.6 stroke. `currentColor` in the markup, so the
   colour is set once here. */
.rd-nprefs__chevron {
  flex: none;
  width: 7px;
  height: 12px;
  color: var(--rd-fg3);
}

/* ---- the foot ------------------------------------------------------------ */

/* The build line that closes the page. Drawn at 8.5px, which is the type floor
   for anything rendered as HTML — the smallest size that exists in this system,
   and correct here: it is the string support asks for, not something anyone
   reads on the way past. */
.rd-nprefs__ver {
  padding: 14px 0 6px;
  text-align: center;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .18em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

/* ---- Notifications ------------------------------------------------------- */

/* A failed fetch or send. ABOVE the group, never instead of it — the same
   contract `.rd-stand__error` states in `s15-stand.css`. */
.rd-nprefs__error {
  margin-bottom: 8px;
  padding: 8px 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.45;
  color: var(--rd-danger);
  border: 1px solid var(--rd-danger);
  background: var(--rd-danger-wash);
}

/* The group note, verbatim from the drawing's `web` frame — until 2026-08-15;
   see the page's own `GROUP_NOTE` doc for the copy that replaced it. `--rd-fg3`
   is the meta step, which `RdText` deliberately does not carry. */
.rd-nprefs__note {
  margin-top: 9px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  line-height: 1.45;
  text-wrap: pretty;
}

/* One row. ⚠️ 2026-08-15: the blanket `.62` dim retired with the group. The
   drawing's `opacity: off ? .62 : 1` was keyed on a whole-group disabled flag
   that no longer exists — each switch now carries its OWN on/off look
   (`.rd-nprefs__toggle-inner--on` / `--off`, below), so the row itself reads
   at full opacity regardless of the value its switch holds. */
.rd-nprefs__row {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 50px;
  padding: 0 2px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-nprefs__row:last-child { border-bottom: none; }

.rd-nprefs__row-text { flex: 1; min-width: 0; }

.rd-nprefs__name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 12px;
  color: var(--rd-fg);
}

.rd-nprefs__sub {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
}

/* The track's outer layer — the hairline, as a ground rather than a border.
   Shared by both live states; only the inner layer and the knob vary. */
.rd-nprefs__toggle {
  width: 46px;
  height: 26px;
  flex: none;
  padding: 0;
  border: 0;
  background: var(--rd-line);
  clip-path: polygon(6px 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%, 0 6px);
  cursor: pointer;
}

.rd-nprefs__toggle:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 3px;
}

/* The inner layer, inset by the hairline's own width. The chamfer steps down
   with the inset, which is the same relation `inner_chamfer_px` encodes for
   the kit's boxes. Fill and knob position are per-state — see `--on` / `--off`
   below — because this is now a genuinely live switch with two real values,
   not one locked default. */
.rd-nprefs__toggle-inner {
  width: 44px;
  height: 24px;
  margin: 1px;
  clip-path: polygon(5px 0, 100% 0, 100% calc(100% - 5px), calc(100% - 5px) 100%, 0 100%, 0 5px);
  display: flex;
  align-items: center;
  padding: 3px;
  box-sizing: border-box;
}

/* OFF: the well, knob left. This is the look every row used to render
   permanently; now it means the account's real value is off. */
.rd-nprefs__toggle-inner--off {
  background: var(--rd-chip);
  justify-content: flex-start;
}

/* ON: the volt fill this file used to withhold — *"a filled track is what a
   live switch looks like,"* and there is one now. Knob right. */
.rd-nprefs__toggle-inner--on {
  background: var(--rd-volt);
  justify-content: flex-end;
}

/* The knob. Dim `--rd-fg3` for OFF — the same colour every row used to render
   permanently. `--rd-on-volt` for ON, the same on-volt content colour
   `kit-segmented.css` uses over the identical fill, for the same reason: it
   is legible sat on volt in both themes. */
.rd-nprefs__knob {
  width: 20px;
  height: 20px;
  flex: none;
  background: var(--rd-fg3);
  clip-path: polygon(5px 0, 100% 0, 100% calc(100% - 5px), calc(100% - 5px) 100%, 0 100%, 0 5px);
}

.rd-nprefs__toggle-inner--on .rd-nprefs__knob {
  background: var(--rd-on-volt);
}
/* ==== S19-DOORS · 19 (Mobile) Profile Pages — the three doors · owner: lane-lib ==== */

/* ---------------------------------------------------------------------------
   The three pages behind the Profile landing's editable rows — `ScreenEditProfile`
   (`/profile/edit`), `ScreenEditAccount` (`/profile/account`) and `ScreenPassword`
   (`/profile/account/password`). Components: `pages/edit_profile.rs`,
   `pages/edit_account.rs`, `pages/change_password.rs`.

   THIS IS THE FOURTH PART OFF DRAWING 19 and the second one that is a screen.
   `s19-prefs.css` (owner: lane-reskin) owns `ScreenSettings`, the same
   document's row 06; `profile-community.css` (owner: lane-2) owns the LANDING
   these three are reached from; `s18-foot.css` (owner: lane-reskin) owns the
   landing's foot and the two overlays. The four are disjoint by block name —
   `.rd-nprefs*` / `.rd-prof*` there, `.rd-eprof*` / `.rd-eacct*` / `.rd-pw*`
   here — so this part's position re-orders no existing pair.

   ⚠️ THREE BLOCKS IN ONE PART, AND NOT ONE. The drawing calls all three "the
   three pages" and shares a `PPage` frame between them, which is the argument
   for one file. It is NOT an argument for one block: they are three routes with
   three different bodies, and a shared `.rd-doors__` prefix would make every
   rule ambiguous about which screen it paints. The shared frame is the kit's
   (`RdShell` + `RdHeading`), which is where sharing belongs.

   ⚠️ WHAT THIS FILE DOES NOT OWN, and where it went instead. The delete dialog
   MOVED onto Edit Account with this change (`22 Account Deletion`: *"Deletion
   lives under Edit Account, not on the Profile landing"*), and it kept its
   `.rd-prof__over` / `.rd-prof__del-*` class names rather than being renamed
   into this part. The rules are unchanged, they still paint the same dialog,
   and they live in `s18-foot.css` (owner: lane-reskin) — a cross-lane rename is
   not this change's to make, and re-declaring them here under a second name
   would give one dialog two implementations. NOTED FOR THAT OWNER: s18-foot's
   own banner says it holds *"the landing's unmigrated foot"*, and after this
   change the foot, the danger zone and the delete dialog are Edit Account's.
   The code modal is the one thing in that part still rendered by `profile.rs`.

   ⚠️ AND THREE OF ITS RULES NOW HAVE NO CALLER AT ALL — the three under that
   part's "Below the drawn content" heading, which painted the loose sign-out
   and the danger zone as things sitting UNDER a page rather than as rows IN a
   group. Their drawn home is a `PGroup` row, which is `.rd-eacct__row` here, so
   nothing renders them any more. NOT deleted: that part is another lane's, and
   delete-only applies to `_legacy-*`. Their names are deliberately not spelled
   out in this file or in the page's doc — the dead-class checker reads source
   for class usage, and a class named in a comment stops being reported, which
   is a lesson `components/kit/mod.rs` records paying for. Run the checker for
   the live list.
   --------------------------------------------------------------------------- */

/* ── The shared page frame ─────────────────────────────────────────────────
   `PPage` is `TShell` + `TH1` + a body, all of which the kit supplies
   (`RdShell { back }` + `RdHeading`), so there is no `.rd-doors__page`. What
   is left is the column each screen's root carries.

   ⚠️ **EDIT PROFILE NO LONGER CLAIMS THE FOLD, and that is the layout half of
   the no-Save defect.** `.rd-eprof` used to be `flex: 1` with a `flex: 1;
   min-height: 0` body under it — the geometry a PINNED bar needs: the root
   takes exactly the scroller's height, the body shrinks below its content, and
   the `flex: none` bar lands on the fold. Edit Profile's commit is a foot row
   in the form now (#1354's ruling — see `pages/edit_profile.rs`), so the root
   is ordinary flow and the whole column scrolls inside `.rd-shell__content`.
   `.rd-eacct` and `.rd-pw` below keep the old shape; neither has a foot row. */

/* ── ScreenEditProfile · `/profile/edit` ─────────────────────────────────── */

.rd-eprof {
  display: flex;
  flex-direction: column;
}

.rd-eprof__body {
  display: flex;
  flex-direction: column;
}

/* The drawn photo row: `margin: '16px 0 20px'` around an avatar, a CHANGE
   button and a quiet Remove line. The row itself is `AvatarUpload`, which
   already composes exactly that flex (`.rd-avup`, `sx-shared.css`) — this rule
   is only the space the drawing puts around it, which is why there is no
   geometry here. */
.rd-eprof__photo { margin: 16px 0 20px; }

/* ── The tag ──────────────────────────────────────────────────────────────
   NOTHING HERE ANY MORE, DELIBERATELY. The tag was drawn as a three-state field
   and shipped as read-only body copy while no rpc wrote a username; that block
   owned five classes of its own (`__lab`, `__tag`, `__at`, `__handle`,
   `__handle--unset`) plus a `__note` under it. #1151 gave `UpdateProfile` a
   `username`, so the tag is now an `RdTextField` like the display name beside
   it — label, chamfered box, `@` prefix and status line all come from the kit,
   and every one of those six rules was a local restatement of something the
   primitive already draws.

   Deleted rather than left behind: an unreferenced rule that LOOKS like this
   screen's tag styling is what a later hand reads before the markup, and the
   two would disagree. */

.rd-eprof__field { margin-bottom: 15px; }

/* ── The foot act row ─────────────────────────────────────────────────────
   ⚠️ **REWRITTEN 2026-08-28. THIS WAS A PINNED SAVE BAR AND IS NOW A FOOT ROW
   IN THE FORM.** James, phone web: *"Edit Profile has no Save button — the form
   cannot be submitted."* Two things made that true, and the markup half is in
   `pages/edit_profile.rs` (the `if dirty` gate). This is the layout half: the
   rule below used to be `flex: none` with a hairline, a `var(--rd-surf)` fill
   and a full-bleed negative gutter — a bar sitting on the fold, under a body
   that shrank below its own content, so a form taller than the fold overflowed
   the body and painted straight through it.

   #1354's ruling, carried over from the console: **the primary sits beside its
   back-out at the FOOT of the form, in the flow that scrolls with the fields.**
   So: no `flex: none`, no bleed, no hairline and no fill — a rule above a row is
   what a PINNED bar sits on, and this one scrolls.

   The shape is the one `19 (Tablet)` row 01 already ruled for the tablet pane —
   *"Save stays a 240px button with Discard as quiet text"* — which is why the
   phone and the pane are now one rule and `s18-tablet.css`'s override says
   almost nothing. That is deliberate: the override stays, because it is the
   place the tablet's own numbers are read from and its own test reads them. */
.rd-eprof__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  margin: 18px 0 0;
}

/* The drawn 240px, capped so a narrow phone gets the column instead of an
   overflow. `RdButton` is full-width by default, which was the pinned bar's
   geometry and is not this row's. */
.rd-eprof__bar > .rd-btn {
  width: 240px;
  max-width: 100%;
}

/* The row's own prose. Drawn only for the queued state; ours says something
   different and says it in the same place — see the page's `SAVE_OFFLINE_NOTE`.
   Grey, never volt: transient machine state is the grey rank, and a volt chip
   would claim the server agreed when it has not been asked.

   Full-width members, both of them: the error banner and this line are about
   the whole act, so they take their own line rather than sharing one with the
   pair. Left, not centred — centred was the pinned bar's own axis. */
.rd-eprof__bar-msg {
  flex-basis: 100%;
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  line-height: 1.4;
  text-wrap: pretty;
}

.rd-eprof__banner {
  flex-basis: 100%;
  margin: 0;
}

/* ── The quiet back-out ───────────────────────────────────────────────────
   ⚠️ **MOVED HERE FROM `s18-tablet.css` 2026-08-28.** It was declared in the
   tablet part as a tablet-only extension of this block, because `19 (Tablet)`
   row 01 drew a Discard and `19 (Mobile)` row 03 did not. The foot row gives
   the phone the same pair, so the control is no longer tablet-only and its rule
   belongs in the part that owns `.rd-eprof*`.

   `rd-tablet-identity.jsx:146` is still the source of the numbers
   (`fontSize: 9.5, color: t.fg3`). */
.rd-eprof__discard {
  appearance: none;
  background: none;
  border: 0;
  min-height: 44px;
  padding: 0 4px;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* Nothing to throw away. Quiet text already sits at the grey rank, so the
   disabled state is the cursor and the pointer events rather than a second
   colour step that would be indistinguishable from the enabled one. */
.rd-eprof__discard:disabled {
  cursor: default;
  opacity: .45;
}

.rd-eprof__discard:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* ── ScreenEditAccount · `/profile/account` ──────────────────────────────── */

.rd-eacct {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
}

/* `PGroup` — a label, then a hairline-topped stack, with the group's spacing on
   the LABEL because the label is the group's first element. The same
   construction `s19-prefs.css` arrived at for the same drawn primitive, and
   re-derived rather than shared for the reason the part split exists: reaching
   across to `.rd-nprefs__lab` would make one screen's spacing depend on
   another's file. */
.rd-eacct__lab { margin-top: 15px; margin-bottom: 8px; }

/* The Email group is drawn at `top={20}` rather than the default 15. */
.rd-eacct__lab--first { margin-top: 20px; }

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

/* The note under a group, which is where this screen puts its weight: each
   group says in plain words what its rows take with them. */
.rd-eacct__note {
  margin: 9px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.45;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

/* The email itself: a fact, not a field. Drawn `padding: '13px 2px 14px'` over
   the group's hairline, with the verification line under it. */
.rd-eacct__email {
  padding: 13px 2px 14px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-eacct__email-value {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 14px;
  color: var(--rd-fg);
  overflow-wrap: anywhere;
}

/* ── PVerifyLine — three states, and the third is the point ───────────────
   `verification.rs` models these and this file only paints them. Verified is a
   short SUCCESS label; not-verified is a warning label with a real control
   beside it; unconfirmed is a SENTENCE, at body weight, because the other two
   are verdicts and it is not one.

   ⚠️ VERIFIED IS `--rd-ok` AND WAS `--rd-acc`. Export 28 moved PVerifyLine's
   verified branch from `color: t.acc` to `color: t.ok` — `rd-profile-kit.jsx`,
   `function PVerifyLine`, the `state === 'verified'` branch, which now reads
   *"...tdisp(800, 78, '.14em'), fontSize: 9, color: t.ok, marginTop: 5 ...
   Verified"* (e28, pre-resync: the citations ledger is still at e27, where that
   line reads `color: t.acc`). It is the ONE line in that component the export
   touched, and it is the same re-assignment `/verify`'s 46x46 mark took
   (`rd-auth-screens.jsx:353`, `c: RD.volt` -> `c: RD.ok`): success had been
   borrowing the brand accent because the accent WAS green, and when the accent
   went tan the success signal kept the green under a key of its own.

   The token pair is `kit-ok.css` (owner: lane-3) — nothing is declared here.
   `au5.css`'s `.rd-field__status--ok` already made this move and its banner
   records that the two are deliberately one role; this rule is that role's
   second surface, so the two now agree again.

   ⚠️ THE OTHER TWO STATES DO NOT MOVE. `unverified-lab` stays `--rd-warn` and
   `unconfirmed` stays `--rd-fg3`: only the VERDICT of success is green. Nor
   does `.rd-banner--success` — `01 Foundations` §07 redraws the success
   banner's border tan in e28 (`border-color:#A89968` dark / `#5C5030` light,
   was `#CEFF51` / `#3E4E10`), so that one is a deliberate tan and not drift.

   ⚠️ THIS PART IS lane-lib's AND THIS IS THE ONLY RULE TOUCHED. Cross-cutting
   token sweep, head-assigned; the declaration edited is the success state's
   colour and nothing else in the block moved. */
.rd-eacct__verified {
  margin-top: 5px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .14em;
  font-size: 9px;
  color: var(--rd-ok);
}

.rd-eacct__unconfirmed {
  margin: 5px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

.rd-eacct__unverified {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
  flex-wrap: wrap;
}

.rd-eacct__unverified-lab {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .14em;
  font-size: 9px;
  color: var(--rd-warn);
}

/* The drawn 1x10 rule between the label and the control. Not a bullet and not
   a slash: the drawing separates them with a hairline. */
.rd-eacct__pip {
  width: 1px;
  height: 10px;
  flex: none;
  background: var(--rd-line);
}

/* "Send a new link" — a real control, painted as the drawn accent label rather
   than as a button, because that is what the drawing draws inside a value row.
   It is a `<button>` in the markup: it sends. */
.rd-eacct__resend {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .12em;
  font-size: 9px;
  color: var(--rd-acc);
}

.rd-eacct__resend:disabled { color: var(--rd-fg3); cursor: default; }

.rd-eacct__resend:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* What the resend came back with. Its own line under the row rather than
   replacing the control, so the state of the email and the receipt for the last
   press are two readable facts instead of one that overwrites the other. */
.rd-eacct__resend-said {
  margin: 6px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

.rd-eacct__resend-said--bad { color: var(--rd-danger); }

/* ── PActionRow ───────────────────────────────────────────────────────────
   One row, three uses on this screen and three different elements behind it:
   a `Link` (Change Password), an inert `div` (Change Email — nothing writes an
   address on any service), and a `button` (Sign Out, Delete Account). The paint
   is one rule; what a row IS lives in the markup, where it can be argued. */
.rd-eacct__row {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 52px;
  padding: 0 2px;
  border-bottom: 1px solid var(--rd-line);
  width: 100%;
  box-sizing: border-box;
  text-align: left;
  text-decoration: none;
  background: none;
  border-left: 0;
  border-right: 0;
  border-top: 0;
  font: inherit;
  color: inherit;
}

.rd-eacct__row--last { border-bottom: none; }

.rd-eacct__row--pressable { cursor: pointer; }

.rd-eacct__row:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: -2px; }

.rd-eacct__row-text { flex: 1; min-width: 0; }

.rd-eacct__row-name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 12px;
  color: var(--rd-fg);
}

/* ⚠️ RED IS SPENT ON THE ROW NAME AND NOWHERE ELSE ON THE ROW. The drawing puts
   `tone="danger"` on two rows and colours the name and the chevron only — the
   sub-line stays grey, because it is an explanation and not a warning. Both
   danger rows on this screen are buttons that carry no chevron (see the page's
   own rule about what a chevron promises), so the name is the whole of it. */
.rd-eacct__row-name--danger { color: var(--rd-danger); }

.rd-eacct__row-sub {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
}

.rd-eacct__chevron {
  flex: none;
  width: 7px;
  height: 12px;
  color: var(--rd-fg3);
}

.rd-eacct__banner { margin-top: var(--rd-banner-gap); }

/* ── ScreenPassword · `/profile/account/password` ────────────────────────── */

.rd-pw {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
}

/* ⚠️ THE OFFLINE NOTICE IS GREY, NOT RED, and the drawing says why: *"Being
   offline is a machine state, not an error, and the four-rank system spends red
   on failures only."* `RdBanner` carries three tones and all three are verdicts
   (error, success, warning), so this is the drawn `TBanner` rather than a
   primitive bent into a fourth rank — a `--rd-surf2` ground under a hairline,
   with the machine-state head at the top of the type scale's quiet end.

   It sits in `RdShell`'s banner slot, which is full-bleed and in normal flow, so
   the horizontal padding is the shell's own gutter rather than the drawn 18. */
.rd-pw__off {
  padding: 9px var(--rd-shell-gutter);
  background: var(--rd-surf2);
  border-bottom: 1px solid var(--rd-line);
}

.rd-pw__off-head {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .12em;
  font-size: 8.5px;
  color: var(--rd-fg2);
}

.rd-pw__off-body {
  margin: 3px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

/* The drawn `marginTop: 20` between the title and the first field. */
.rd-pw__fields { margin-top: 20px; }

/* Everything that is NOT the wrong-current-password state — offline,
   unreachable, or the server refusing the new password's length. Above the
   control it governs, which is #465's acceptance criterion. */
.rd-pw__banner { margin: 0; }

.rd-pw__act { margin-top: 4px; }

/* The footnote, which answers the question the screen provokes before it is
   asked. 11px `--rd-fg3` — the meta step, quieter than any field note above it,
   because it is about what happens next rather than about what to type.

   Hidden offline in the markup, not here: the banner already carries the
   offline fact AND its reason, and *"repeating it under the button would be the
   same condition twice on one screen."* */
.rd-pw__foot {
  margin: 12px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.45;
  color: var(--rd-fg3);
  text-wrap: pretty;
}
/* ==== SX-SHARED · shared page components inside drawn screens · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   The six components that are not screens. They have no drawing of their own;
   each one appears INSIDE a drawn screen, and each is skinned to that screen's
   treatment.

     .rd-avatar-photo / .rd-avup-*   `pages/avatar.rs`
     .rd-sdg-*                       `pages/stage_diagram.rs`
     .rd-satt-*                      `pages/stage_attachment.rs`
     .rd-mgal-*                      `pages/media_gallery.rs`
     .rd-share-*                     `pages/share_overlay.rs`
     .rd-tab-home-back-*             `pages/tablet_home_back.rs`

   ⚠️ TWO OF THE SIX ARE DRAWN AND FOUR ARE NOT, and the difference decides
   how much this file is allowed to invent.

   DRAWN — `03 Training`, `ScreenRunDetail`'s **Photo** slot (a `--rd-surf2`
   well behind a `--rd-line` hairline, 148 tall, a camera glyph over a tracked
   caption) and `ScreenResultShare`'s **share card**. `media_gallery.rs` and
   `share_overlay.rs` take those two treatments.

   UNDRAWN — the stage diagram's read-only slot, the stage-photo attachment, and
   the avatar's own chrome. `04 Authoring` and `05 Stage Editor` redraw the
   whole authoring FLOW rather than the slot as it exists today, so there is no
   frame to copy. These take the minimal move the brief allows: fixed greys and
   hexes become the `--rd-*` roles, geometry unchanged. **Flagged UNDRAWN on
   each component rather than designed here.**

   ⚠️ `.rd-tab-home-back-*` IS UNDRAWN FOR A DIFFERENT REASON THAN THE OTHER
   THREE — not a gap in the corpus, a NAMED DEVIATION FROM IT. `03 (Tablet)
   Training`'s rail reasoning rules a tablet back control OUT ("the rail is
   the back button"); James red-lined that rule on 2026-08-18 for three named
   screens anyway, and again the same day for a fourth (`Your Orgs`, item 11
   — see `pages/your_orgs.rs`'s own "THE TABLET BACK ROW" section). See
   `pages/tablet_home_back.rs`'s own module doc for the full argument and for
   why the component's label/destination generalised into props for the
   fourth site rather than forking — this file only carries the geometry, and
   the geometry is unchanged by the generalisation.

   ⚠️ WHY THIS PART EXISTS AT ALL, rather than an addition to `train.css`.
   `order.txt` records that train.css *"splits when a second owner takes any
   part of the arc"*, and the two drawn treatments above are part of the 03
   arc. This is that split, and it is scoped to the components rather than to
   the drawing so the arc's own screens stay with their owner.
   --------------------------------------------------------------------------- */

/* ── avatar.rs ────────────────────────────────────────────────────────────── */

/* The photo branch of the shared `Avatar`. Its INITIALS branch is `RdAvatar`
   and takes the kit's ground; this rule is what stops the two branches
   disagreeing the moment an image finishes loading — the placeholder ground
   behind a transparent PNG was `#f3f4f6`, a light disc in a dark app.

   Round, not chamfered: the kit's mark is round and the two must match. The
   sizing stays on the caller's `w-N h-N` pair for the same reason
   `tailwind_size_px` exists — eleven call sites pass class pairs, and the
   string API is what should retire, not the geometry. */
.rd-avatar-photo {
  border-radius: 50%;
  object-fit: cover;
  background: var(--rd-chip);
  flex: none;
}

.rd-avup { display: flex; align-items: center; gap: 14px; }

.rd-avup__acts { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }

/* The file picker. A `<label>` wrapping a hidden `<input type=file>`, so it
   cannot be `RdButton` — the primitive renders a `<button>`, and a button
   cannot open a file dialog for an input it does not contain. Painted as the
   kit's neutral control instead: `--rd-ctrl` bounds a control, which is the
   token's whole job and the WCAG 1.4.11 floor the substrate pins it at. */
/* `gap` is the in-flight sweep's, and it is the drawn 10px the kit uses between
   a mark and a word (`rd-loading.jsx:197`, and `.rd-btn__face--inflight` in
   kit-loading.css). Unconditional rather than a modifier: the only other child
   is the `display: none` file input, which a gap cannot separate from anything,
   so there is no state in which this value is visible without the mark. */
.rd-avup__btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 34px;
  padding: 0 14px;
  border: 1px solid var(--rd-ctrl);
  background: none;
  color: var(--rd-fg);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 10px;
  cursor: pointer;
}

/* A `<label>` takes no `:focus`; the INPUT inside it does, and the ring has to
   land on the thing a sighted keyboard user sees. */
.rd-avup__btn:focus-within {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

.rd-avup__err {
  margin-top: 6px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-danger);
  line-height: 1.4;
}

/* ── stage_diagram.rs — UNDRAWN ───────────────────────────────────────────── */

.rd-sdg { display: flex; flex-direction: column; gap: 6px; }

/* The scene well. The SVG comes from the shared core renderer and carries its
   own viewBox, so the container fixes the width and lets the aspect decide the
   height — the contract the legacy `.stage-diagram` rule already had, carried
   across unchanged because it is geometry rather than paint. */
.rd-sdg__scene {
  width: 100%;
  overflow: hidden;
}

.rd-sdg__scene svg {
  display: block;
  width: 100%;
  height: auto;
  max-height: 24rem;
}

/* ── stage_attachment.rs — UNDRAWN ────────────────────────────────────────── */

.rd-satt { display: flex; flex-direction: column; gap: 10px; }

.rd-satt__diagram { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }

/* The editor door. A router `<Link>` rather than a button, so it keeps a real
   `href` — middle-click, copy-link and the status bar all come from that — and
   is painted as the kit's outline control so it still reads as one. */
.rd-satt__editor {
  display: block;
  width: 100%;
  min-height: 38px;
  padding: 10px 16px;
  border: 1px solid var(--rd-ctrl);
  color: var(--rd-fg);
  text-align: center;
  text-decoration: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 10px;
}

.rd-satt__editor:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* The stage-photo picker is `.rd-avup__btn`'s twin plus one thing: it is the
   only control in its row, so it spans. Composed rather than restated — one
   file picker treatment, three mount points. */
/* In flight the sweep joins the label at 10px of gap (kit-loading.css §8). */
.rd-satt__upload { justify-content: center; width: 100%; gap: 10px; }

/* The no-claim fallback while the photo slot reads: one 26px mark centred on
   empty ground (43 Loading States.html:52). */
.rd-satt__wait {
  display: flex;
  justify-content: center;
  padding: 24px 0;
}

.rd-satt__hint {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  line-height: 1.45;
}

/* `object-fit: contain`, never `cover`: a stage photo is a PLAN of a bay, and
   cropping one loses the targets at its edges. The ground shows through the
   letterbox, so it is a token rather than the old fixed `#f9fafb`. */
.rd-satt__photo {
  display: block;
  width: 100%;
  max-height: 16rem;
  object-fit: contain;
  background: var(--rd-surf2);
}

/* ── media_gallery.rs — DRAWN (03 Training, ScreenRunDetail's Photo slot) ─── */

.rd-mgal { display: flex; flex-direction: column; gap: 10px; }

.rd-mgal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

/* Same `<label>`-wrapping-an-input constraint as the avatar's picker, same
   treatment — one control, painted one way, in both places. */
.rd-mgal__upload {
  display: inline-flex;
  align-items: center;
  flex: none;
  min-height: 30px;
  padding: 0 12px;
  border: 1px solid var(--rd-ctrl);
  background: none;
  color: var(--rd-fg);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 9.5px;
  cursor: pointer;
}

.rd-mgal__upload:focus-within {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* The drawn empty slot: 148 tall, a `--rd-surf2` well behind a `--rd-line`
   hairline, the camera glyph over a tracked caption. The drawing renders it as
   a stand-in for a photo that IS there; ours is the state where none is, so the
   frame is the drawing's and the caption says what is true. */
.rd-mgal__empty {
  height: 148px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 7px;
}

.rd-mgal__empty-glyph { color: var(--rd-fg3); }

.rd-mgal__empty-cap {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .14em;
  font-size: 9px;
  color: var(--rd-fg3);
}

.rd-mgal__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}

.rd-mgal__tile {
  position: relative;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background: var(--rd-surf2);
  border: 1px solid var(--rd-line);
}

.rd-mgal__thumb { width: 100%; height: 100%; object-fit: cover; display: block; }

/* A video tile. The play mark is the drawn volt disc from `VideoSlot` — the one
   place in this file volt is spent, and correctly: it is a FILL, which is the
   half of the accent that does not change between themes. */
.rd-mgal__video {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--rd-bg);
  border: 0;
  padding: 0;
  cursor: pointer;
}

.rd-mgal__play {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--rd-volt);
  color: var(--rd-on-volt);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* The delete affordance. Always visible rather than hover-only: this is a phone
   surface and there is no hover, so the shipped `opacity-0
   group-hover:opacity-100` made deletion unreachable on the device the app is
   for. Quiet enough not to compete with the photograph. */
.rd-mgal__x {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--rd-bg);
  border: 1px solid var(--rd-ctrl);
  color: var(--rd-fg2);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
}

.rd-mgal__x:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 1px;
}

.rd-mgal__confirm {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: var(--rd-panel-scrim);
  padding: 6px;
}

.rd-mgal__confirm-ask {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-fg);
}

.rd-mgal__confirm-acts { display: flex; gap: 6px; }

.rd-mgal__no,
.rd-mgal__yes {
  min-height: 28px;
  padding: 0 10px;
  background: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 9px;
  cursor: pointer;
}

.rd-mgal__no { border: 1px solid var(--rd-ctrl); color: var(--rd-fg2); }

/* Destructive TEXT and a destructive hairline take `--rd-danger`; `--rd-dfill`
   is the ground a filled Delete sits on, and this one is not filled. The token
   layer states the split and warns that substituting one for the other ships a
   brighter control than drawn in the default theme. */
.rd-mgal__yes { border: 1px solid var(--rd-danger); color: var(--rd-danger); }

.rd-mgal__no:focus-visible,
.rd-mgal__yes:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 1px;
}

/* The video lightbox. `z-index` comes from the shared `.z-modal` rule, which
   exists because the tab bar renders after the Outlet and won a 50-vs-50 tie on
   DOM order — a lightbox behind the tab bar. Not restated here. */
.rd-mgal__player {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--rd-panel-scrim);
  padding: 16px;
}

.rd-mgal__player-inner { width: 100%; max-width: 32rem; }

.rd-mgal__player video { width: 100%; display: block; background: var(--rd-bg); }

.rd-mgal__player-close {
  width: 100%;
  min-height: 38px;
  margin-top: 8px;
  border: 1px solid var(--rd-ctrl);
  background: none;
  color: var(--rd-fg);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 10px;
  cursor: pointer;
}

/* ── share_overlay.rs — DRAWN (03 Training, ScreenResultShare) ────────────── */

.rd-share__scrim {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: var(--rd-panel-scrim);
}

.rd-share__panel { width: 100%; max-width: 360px; }

/* The card itself. The drawing sets it on a near-black ground in BOTH themes
   because it is a picture of a result rather than a piece of app chrome — but
   ours is a preview inside the app and exports TEXT, not an image (see the
   component's flag), so it takes the app's own recessed ground. If image export
   is ever built, the exported canvas is where the fixed values belong, not
   here: a screen that paints itself the wrong way round in light mode to match
   a file it is not producing would be a bug wearing a rationale. */
.rd-share__card { display: flex; flex-direction: column; }

.rd-share__stamp {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .2em;
  font-size: 8px;
  color: var(--rd-fg3);
}

.rd-share__drill {
  margin-top: 12px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 900; font-stretch: 62%;
  font-size: 25px;
  line-height: .95;
  color: var(--rd-fg);
}

/* The one number the card is about, and the unit sitting on its baseline.
   Decision 19 keeps this pairing on the share card alone: folding the unit into
   the hero string set `HF` at 52px in the accent, which is not the drawing. */
.rd-share__hero {
  display: flex;
  align-items: flex-end;
  gap: 5px;
  margin-top: 14px;
}

.rd-share__hero-val {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 52px;
  line-height: .85;
  letter-spacing: -.03em;
  color: var(--rd-acc);
}

.rd-share__hero-unit {
  padding-bottom: 6px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .14em;
  font-size: 10px;
  color: var(--rd-fg3);
}

.rd-share__stats {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--rd-line);
}

.rd-share__stat-k {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .18em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

.rd-share__stat-v {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 14px;
  color: var(--rd-fg);
}

.rd-share__acts { display: grid; gap: 8px; margin-top: 18px; }

/* The close control. A word rather than an X: the drawn screen has a back row
   and this overlay has nowhere to put one, so the affordance says what it does. */
.rd-share__close {
  align-self: center;
  min-height: 34px;
  padding: 0 12px;
  background: none;
  border: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-fg3);
  cursor: pointer;
}

.rd-share__close:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* ── tablet_home_back.rs ──────────────────────────────────────────────────── */

/* `.rd-tab-home-back` — the tablet back affordance. UNDRAWN, and
   deliberately so — see this file's own header note and `tablet_home_back.rs`'s
   module doc for the full argument: `03 (Tablet) Training`'s rail reasoning
   rules a tablet back control OUT ("the rail is the back button"), and James
   red-lined that on 2026-08-18 for the three named screens `TabletHomeBack`
   first mounted on, then again the same day for a fourth (`Your Orgs`, item
   11, label and destination overridden to `Profile`). ONE rule for all four
   call sites, so the treatment cannot drift between them — the same reason
   `.rd-avup` above is one rule for every `Avatar` caller rather than a copy
   per screen.

   A WAY OUT, NOT THE PAGE'S ACTION — no fill, no accent ground, a quiet label
   at rest (`--rd-fg2`), the Accept-carries-the-fill weighting logic in
   reverse (`challenges.rs`'s own note on the point): this control must never
   compete with whatever `Kind::Primary` control the page already has.

   The chevron REPRODUCES `kit-shell.css`'s own `.rd-shell__chevron` geometry
   and stroke role (9×15, `--rd-acc`) rather than sharing the class — `Back`'s
   markup is inlined inside `RdShell` and assumes its bar slot, so there is
   nothing to share; the glyph is the one thing that travels. See
   `tablet_home_back.rs`'s own module doc, "Why this and not `kit::shell::Back`". */
.rd-tab-home-back {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-height: 44px;              /* touch floor — kit-tablet-shell.css's own
                                     action-slot rule, same reason: a mouse
                                     might be present, the target does not
                                     shrink for it */
  margin-bottom: 16px;
  padding: 0 2px;
  background: none;
  border: 0;
  cursor: pointer;
  font: inherit;
  color: inherit;
  text-align: start;
  -webkit-appearance: none;
  appearance: none;
}

.rd-tab-home-back__chevron {
  flex: none;
  display: block;
  width: 9px;
  height: 15px;
  /* ⚠️ --rd-acc, never --rd-volt — a STROKE takes acc (substrate §1.1); volt
     here is an unreadable tan chevron in light theme, the same trap
     `kit-shell.css`'s own chevron rule guards against. */
  stroke: var(--rd-acc);
}

.rd-tab-home-back__label {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--rd-fg2);
}

/* (hover: hover) so a touch device does not get a stuck hover after a tap —
   same guard `kit-shell.css`'s own back row takes. */
@media (hover: hover) {
  .rd-tab-home-back:hover .rd-tab-home-back__label { color: var(--rd-fg); }
}

.rd-tab-home-back:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}
/* ==== S09-BOARD · 09 Multi-Shooter squad setup + board, the RO scoring rows · owner: 1-track3 ==== */

/* ---------------------------------------------------------------------------
   MOVED VERBATIM out of the UNOWNED · DELETE-ONLY pool on 2026-08-21.

   Every rule below arrived byte-for-byte — rules, comments and blank lines —
   from `_legacy-kit-components-and-screens.css` sections S2a and S2b (spec:
   docs/design/kit/S2-squad.md §2 and §3, source `rd-squad.jsx`). NOTHING was
   edited in the move: no value, no selector, no wording. This slice is the
   change of custody and only that, so the diff a reviewer reads is an
   extraction rather than a re-skin, and any later value change lands against a
   part that has an owner to answer for it.

   WHY THEY NEEDED AN OWNER. `47 (Mobile) RO Range Day.html` puts these rows on
   the RO's phone and rules a floor on them — verbatim, from its row 03
   (`47 (Mobile) RO Range Day.html:126-130`, the anchor line is :128):

       notes={[
         'Exactly one row says Up next; scored rows carry their number; the rest say Waiting',
         'Rows clear the 56px phone floor',
         'The bar is a mount difference from 09\u2019s solo flow. Tab-away must re-enter this board, never a fresh one — the session-squad guard',
       ]}>

   and states the number once for the whole document, in its ruling strip
   (`47 (Mobile) RO Range Day.html:49`):

       56px is the phone touch floor on scoring surfaces. 68 was a tablet
       ruling for a gloved thumb at arm's length; the phone number covers the
       steppers, the keypad and the board rows.

   `.rd-board__row` set `min-height: 50px` below, so it did NOT clear that
   floor. The rule that had to change is in this block, and while it sat
   in the pool NO LANE COULD LEGALLY CHANGE IT — the pool is delete-only, and `adm-rday.css` (owner: lane-lib) already wrote
   down its own refusal to reach in and override these declarations rather than
   working around the rule, reporting it to design instead. That refusal was
   correct and it left the drawing unbuildable. Moving the rules out is the
   answer to it: the floor, the inline breakdown and the offline line are later
   slices, and they now have a part to land in.

   ⚠️ **THE FLOOR AND THE BREAKDOWN LANDED 2026-08-21**, in the first slice this
   part's ownership bought: `.rd-board__row` is `min-height: 56px`, the foot's
   controls are painted to 56 by `.rd-board__foot .rd-btn__face`, and
   `.rd-board__breakdown*` draws the scored row's inline expansion. Each carries
   its own citation at the rule. The offline line is still a later slice.

   ⚠️ THE POSITION IS NOT A RE-ORDER. `.rd-board*` and `.rd-squad*` are declared
   in no other part — the five other files that name them do so in prose only,
   including the pool they came out of — so this part contends with nothing and
   appending it re-orders no existing pair. It sits after `sx-shared.css` with the re-skin parts, and BEFORE
   `train.css`, which must keep winning ties for the shooter arc.

   ⚠️ AND IT MOVED LATER IN THE SHEET, WHICH IS FREE ONLY BECAUSE OF THAT. The
   pool concatenates well above here, so an extraction that landed on a
   contended name would have silently flipped which declaration wins with no
   diff on either rule. The disjointness above is what makes the move a no-op on
   the assembled sheet, and the receipt for this slice is exactly that: the
   sorted rule set of `style.css` is unchanged before and after.
   --------------------------------------------------------------------------- */

/* S2a — Score A Squad. Spec: docs/design/kit/S2-squad.md §2
   Source: rd-squad.jsx ScreenSquadSetup :87-174, SQSource :54-68, SQPick :70-83
   -------------------------------------------------------------------------

   ⚠️ The type combinations below are SCREEN-LEVEL and belong here rather than in
   a role: 00-substrate.md §2.3 draws that line itself — "Only these are
   Foundations' concern. Screen-level ad-hoc combinations arrive with each page
   migration." Where a recipe is restated (the mono numeral, the display face)
   it is because the role carries a size and a colour this site does not take. */

/* :98 — the content column. `flex: 1; min-height: 0` so the picker can scroll
   inside it while the foot stays put; `paddingTop: 4` under the shell's bar. */
.rd-squad {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding-top: 4px;
}

/* The banner, in RdShell's slot (:97). Screen-local: TBanner has 8 corpus call
   sites and will earn a kit spec, but it has exactly one consumer here — the
   position S1-home.md §3 correctly took on the avatar. :168-170 verbatim. */
.rd-squad__banner {
  background: var(--rd-surf2);
  border-bottom: 1px solid var(--rd-line);
  padding: 9px 18px;
}
.rd-squad__banner-title {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .12em;
  font-size: 8.5px;
  color: var(--rd-fg2);
}
.rd-squad__banner-body {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  margin-top: 3px;
  line-height: 1.4;
  text-wrap: pretty;
}

.rd-squad__loading { margin-top: 18px; }
.rd-squad__error { margin-top: 18px; }

/* :105-112 — the dead end. Vertically CENTRED in the remaining space and padded
   34px off the bottom: it is the whole screen, not a message on one. */
.rd-squad__dead {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 12px;
  padding-bottom: 34px;
}
.rd-squad__dead-lead {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg2);
  line-height: 1.5;
}
.rd-squad__dead-body {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  line-height: 1.5;
  text-wrap: pretty;
}
/* :108 — `display: grid; gap: 8; marginTop: 4`. */
.rd-squad__dead-actions { display: grid; gap: 8px; margin-top: 4px; }

/* :115-116 — the label sits 14px down, the row 6px under it. */
.rd-squad__source-lab { margin-top: 14px; }
.rd-squad__source { margin-top: 6px; }
/* :58 — `padding: '11px 13px'`, `gap: 10`, `minHeight: 54`. */
.rd-squad__source-inner {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 54px;
  padding: 11px 13px;
  box-sizing: border-box;
}
.rd-squad__source-copy { flex: 1; min-width: 0; }
/* :60 — disp(800, 78, .04em) @ 12.5, --rd-fg. */
.rd-squad__source-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .04em;
  font-size: 12.5px;
  color: var(--rd-fg);
}
/* :61 — body @ 10, --rd-fg3, 3px under the name. */
.rd-squad__source-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
  margin-top: 3px;
}

/* :118 — the empty region takes the remaining height so the foot stays down. */
.rd-squad__empty { flex: 1; min-height: 0; margin-top: 18px; }
/* :123 — body @ 12.5, --rd-fg2. */
.rd-squad__empty-lead {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg2);
}
/* :131 — body @ 10.5, --rd-fg3, 12px down. */
.rd-squad__empty-body {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  margin-top: 12px;
  line-height: 1.5;
  text-wrap: pretty;
}
/* :125-128 — the error-rank block. Its fill is --rd-danger-wash, the token this
   screen's measurement added; see the wash family at the top of the sheet. */
.rd-squad__roster-note { margin-top: 12px; }
.rd-squad__note { padding: 10px 12px; box-sizing: border-box; }
.rd-squad__note-text {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-danger);
  line-height: 1.4;
}

/* :137 — label and count on one baseline, 16px down. */
.rd-squad__order-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-top: 16px;
}
/* :139 — body @ 9.5, --rd-acc. The count is the ONE derivation (spec §2.3). */
.rd-squad__order-count {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-acc);
}

/* :141 — the picker takes the remaining height and scrolls inside it.
   ⚠️ `auto`, not the drawing's `hidden`: 03-tshell.md §7e ruled that a rule
   satisfied by concealing what it exists to prevent is not satisfied. A phone
   with 12 org members would silently lose the ones past the fold.

   `min-height: 0` is the load-bearing half: a flex child's default
   `min-height: auto` refuses to shrink below its content, so without it the
   list pushes the drill row and the action off the screen rather than
   scrolling.

   ⚠️ S2a carried this on a WRAPPER, because revealing the unmigrated entry rows
   as a flex sibling squeezed a `flex: 1` list to zero height and the picker
   vanished. S2b moved the scoring to the board, so the region has one occupant
   again and the wrapper collapsed into it. */
.rd-squad__picks { flex: 1; min-height: 0; overflow-y: auto; margin-top: 4px; }

/* :72 — `gap: 10`, `minHeight: 46`, hairline under each row.
   A real <button>, so the row is one hit target and reaches the keyboard. */
.rd-squad__pick {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 46px;
  width: 100%;
  padding: 0;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  background: transparent;
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.rd-squad__pick:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: -2px; }
/* :74 — body @ 12, --rd-fg, and it is the element that may ellipsize. */
.rd-squad__pick-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* :78 — 20x20, 1.5px, mono @ 10. THE ORDER NUMBER IS THE FEEDBACK, so this is
   a numeral box and not a tick box. --rd-volt as a FILL and --rd-acc as the
   STROKE is the substrate's split (§1.1) rendered on one control; the numeral
   sits in --rd-on-volt because it is ON the volt. */
.rd-squad__order {
  width: 20px;
  height: 20px;
  flex: none;
  box-sizing: border-box;
  background: transparent;
  border: 1.5px solid var(--rd-ctrl);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 10px;
  color: var(--rd-on-volt);
}
.rd-squad__order--on { background: var(--rd-volt); border-color: var(--rd-acc); }

/* :151/:155 — the foot. `flex: none` so it never shrinks, a hairline above it,
   and 12px of air below. */
.rd-squad__foot {
  flex: none;
  padding-top: 11px;
  border-top: 1px solid var(--rd-line);
  padding-bottom: 12px;
}
/* :156 — the drill row.
   ⚠️ NO CHEVRON (:162). We have no drill screen to send it to — the back row
   already returns to the drill's own entry — and a chevron is a promise. */
.rd-squad__drill {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
}
.rd-squad__drill-copy { flex: 1; min-width: 0; }
/* :158 — disp(700, 78, .14em) @ 8.5, --rd-fg3. */
.rd-squad__drill-kind {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .14em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}
/* :159 — disp(800, 78, .04em) @ 12, --rd-fg, 4px under the eyebrow. */
.rd-squad__drill-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .04em;
  font-size: 12px;
  color: var(--rd-fg);
  margin-top: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* :161 — body @ 10, --rd-fg3, never shrinks. */
.rd-squad__drill-rounds {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
  flex: none;
}
/* :164 — the action sits 9px under the drill row. */
.rd-squad__action { margin-top: 9px; }

/* S2 · RO SLICE 3 — today's squads, the search, the cold open, the attribution
   Spec source: rd-ro-day.jsx ScreenROSetup :85-135
   Ruling:      47 (Mobile) RO Range Day.html :50, :51, :55, :66, :176
   -------------------------------------------------------------------------

   Row 02 puts the desk-built squads ABOVE the roster picker and row 08 opens
   this screen from cache. Everything below is drawn in `ScreenROSetup` and
   nowhere else, so every rule carries the line it came from — same convention
   as the block above it. Screen-level type combinations again (00-substrate.md
   §2.3): these arrive with the page rather than with a role. */

/* :92 — the cold open's sub, under the H1: body @ 10.5, --rd-fg3, 7px of air.
   ⚠️ --rd-fg3 and not --rd-fg2, and the drawing makes the distinction itself:
   `color: stale ? t.fg3 : t.fg2`. The stale line is quieter than the live one
   it replaces, because it is a caveat and not the screen's subject. */
.rd-squad__stale {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.5;
  color: var(--rd-fg3);
  margin: 7px 0 0;
}

/* :93 / :105 — the two section labels. 13px above the first, 12px above the
   second, exactly as drawn: the gap before "Or build your own" is smaller
   because it separates two halves of one screen, not the screen from its head. */
.rd-squad__saved-lab { margin-top: 13px; }
.rd-squad__own-lab { margin-top: 12px; }

/* :94 — a 6px grid, not a hairline list. The saved squads are CARDS (each one
   its own chamfered box) while the roster picker below is rows separated by
   hairlines, and the difference is the point: one is a thing you take whole,
   the other is a list you build from. */
.rd-squad__saved { display: grid; gap: 6px; margin-top: 6px; }

/* :96 — a real <button> wrapping the chamfered box, so the whole card is one
   hit target and reaches the keyboard. The box paints its own border. */
.rd-squad__saved-row {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.rd-squad__saved-row:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }
/* :96 — `minHeight: 48`, `gap: 10`, `padding: '0 12px'` on the box's inside. */
.rd-squad__saved-inner {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 48px;
  padding: 0 12px;
  box-sizing: border-box;
}
.rd-squad__saved-copy { flex: 1; min-width: 0; }
/* :98 — disp(800, 78, .04em) @ 12, --rd-fg. The same recipe the drill row's
   name takes, because both are a proper noun at the head of a row. */
.rd-squad__saved-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .04em;
  font-size: 12px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* :99 — body @ 9.5, --rd-fg3, 2px under the name. "Bay 2 · 4 shooters". */
.rd-squad__saved-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
  margin-top: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* :106 — the search sits 6px under its label. The field itself is
   `RdTextField` and owns everything inside it; this is the gap and nothing
   more, the same restraint `.rd-squad__action` shows over `RdButton`. */
.rd-squad__search { margin-top: 6px; }

/* :129 — the attribution line: body @ 10.5, --rd-fg2, 1.45 leading, 9px above
   the action. --rd-fg2 and not --rd-fg3 — quiet, but this one is read. */
.rd-squad__attrib {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.45;
  color: var(--rd-fg2);
  margin: 0 0 9px;
}

/* S2b — the Squad Board. Spec: docs/design/kit/S2-squad.md §3
   Source: rd-squad.jsx ScreenSquadBoard :182-247
   -------------------------------------------------------------------------

   The screen that runs the squad: whose turn it is, what everybody has shot,
   and — once nobody is left — who took it. Same screen-level type rule as the
   block above (00-substrate.md §2.3): these combinations arrive with the page. */

/* :189 — the content column, matching the setup screen's. */
.rd-board {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding-top: 4px;
}
.rd-board__loading { margin-top: 18px; }
.rd-board__error { margin-top: 18px; }
.rd-board__note { padding: 10px 12px; box-sizing: border-box; }
.rd-board__note-text {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-danger);
  line-height: 1.4;
}

/* :191 — the identity strip, gap 6. The chips are RdChipStatus in emphasis
   (the corpus's `attr`), so their geometry is the kit's, not this screen's.

   ⚠️ `margin-bottom: 9px` is NOT in `:191`, and the render is what asked for it.
   TH1 carries `line-height: .92`, so a 23px heading occupies a 21.2px box and
   its caps overflow upward — against a chip strip with a zero gap, measured at
   exactly 0.0px between the two boxes, they touched. The corpus's OWN
   chips-above-heading site sets the value: `rd-train-run.jsx:72` draws the same
   stack with `marginBottom: 9`. So this is the drawing's rhythm restored where
   `:191` dropped it, not a number invented here. */
.rd-board__chips { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 9px; }

/* :195 — body @ 10.5, --rd-fg2, 7px under the heading. Rounds and the sentence
   about how the board is ordered. */
.rd-board__sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg2);
  margin-top: 7px;
}

/* :196 — the list takes the remaining height, 15px down from the sub-line.
   `min-height: 0` for the same reason the picker needs it: without it a squad
   whose rows are expanded pushes the foot off the screen instead of scrolling,
   and the foot is the only control on this screen. */
.rd-board__list-wrap {
  flex: 1;
  min-height: 0;
  margin-top: 15px;
  display: flex;
  flex-direction: column;
}
.rd-board__list-lab { flex: none; }
.rd-board__list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  margin-top: 5px;
}

/* :202 — gap 10, hairline under every row but the last.

   ⚠️ **THE ROW IS 56, AND `rd-squad.jsx:202` DREW IT 50.** The number moved
   because a second drawing put this row on an RO's phone and ruled a floor over
   it — `47 (Mobile) RO Range Day.html:49`, verbatim:

       56px is the phone touch floor on scoring surfaces. 68 was a tablet
       ruling for a gloved thumb at arm's length; the phone number covers the
       steppers, the keypad and the board rows.

   and again on the board's own row, `47 (Mobile) RO Range Day.html:126-130`
   (the anchor is :128):

       notes={[
         'Exactly one row says Up next; scored rows carry their number; the rest say Waiting',
         'Rows clear the 56px phone floor',
         'The bar is a mount difference from 09’s solo flow. Tab-away must re-enter this board, never a fresh one — the session-squad guard',
       ]}>

   `rd-ro-day.jsx:169` draws it as `minHeight: 56` on the same row. This is the
   later drawing raising the earlier one's number on a surface they share, not a
   value invented here — which is why the module header above could name the
   change before this part had an owner to make it.

   It is a MINIMUM, not a height: a row whose name wraps still grows. */
.rd-board__row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 56px;
  border-bottom: 1px solid var(--rd-line);
}
.rd-board__row--last { border-bottom: none; }
/* :204 — the position numeral. 11px of width, fixed, so names start on one
   vertical line whether the board holds 4 shooters or 14. */
.rd-board__pos {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 11px;
  color: var(--rd-fg3);
  width: 11px;
  flex: none;
}
.rd-board__who { flex: 1; min-width: 0; }
/* :207 — body @ 12, --rd-fg at 400. */
.rd-board__name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  font-weight: 400;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* :207 — "You" in --rd-acc at 700, so the shooter finds themselves without
   reading. --rd-acc as TEXT, which is the token's deep-warm-brown light-theme value
   (00-substrate.md §1.1) — not --rd-volt, which is a fill. */
.rd-board__name--me { color: var(--rd-acc); font-weight: 700; }
/* :210 — the status line. Display face @ 8.5, tracking .1em. */
.rd-board__status {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: none;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8.5px;
  color: var(--rd-fg3);
  margin-top: 3px;
}
/* The one row state that matters: whose turn it is. */
.rd-board__status--up { color: var(--rd-acc); }

/* :218 — the grip. It renders only on rows the gesture works on (§3.3), and
   `touch-action: none` is what makes it work there: without it the browser
   claims the vertical drag for scrolling and the reorder never starts. */
.rd-board__grip {
  flex: none;
  display: flex;
  align-items: center;
  color: var(--rd-fg3);
  cursor: grab;
  touch-action: none;
  padding: 6px 2px;
  margin: -6px 0;
  -webkit-tap-highlight-color: transparent;
}
.rd-board__grip:active { cursor: grabbing; }

/* :225 — the score. The mono numeral role at this screen's size; an unshot row
   shows an em dash in --rd-fg3, which is the kit's `numc` rule (a dash is dim
   even where the site would emphasise, because there is nothing to emphasise). */
.rd-board__val { font-size: 15px; flex: none; }
.rd-board__val--dim { color: var(--rd-fg3); }
/* :248 — a pass/fail board's cell holds a WORD, not a numeral, and the drawing
   drops it to 11.5px for it. At 15 in the display face it reads as a score,
   which is the one thing this profile does not have. A `Fail` also takes
   --rd-fg3 via --dim, so the column still says at a glance who cleared it. */
.rd-board__val--pf { font-size: 11.5px; }
/* A `Pass` is GREEN — James 30 Aug 2026, a DEVIATION from :248's `--rd-fg` (relay
   item 256): a passing verdict is `--rd-ok` everywhere it renders (#1431). `Fail`
   keeps --dim above. */
.rd-board__val--pass { color: var(--rd-ok); }

/* The `Up next` row's entry, expanded IN PLACE (ruling A). Inset under the row
   it belongs to and hairlined off, so it reads as part of that row rather than
   as a new section. */
.rd-board__entry {
  padding: 12px 0 14px 21px;
  border-bottom: 1px solid var(--rd-line);
}
.rd-board__row-error { margin-top: 10px; }

/* ---------------------------------------------------------------------------
   THE INLINE BREAKDOWN — a SCORED row, expanded in place.

   Drawn: `rd-ro-day.jsx:181-193` (`ScreenROBoard`, the `open &&` block), ruled
   at `47 (Mobile) RO Range Day.html:56`:

       A scored row expands inline to its breakdown. Render-always,
       collect-never — the board stays flat until tapped, and the unfolded row
       is also where Void lives, on the RO's own entries only.

   and described at `47 (Mobile) RO Range Day.html:158`:

       body="Tap a scored row and it expands in place: zone chips, who recorded
       it, and — on the RO's own entries only — Void. Render-always,
       collect-never; the board stays flat until asked."

   **Collect-never is the load-bearing half and it is a STRUCTURAL rule, not a
   styling one**: nothing in this subtree may take input. The counts here were
   already collected on the entry above; showing them back is a reading, and a
   second place to type them is a second place they can disagree. Pinned by
   `ruling_the_breakdown_renders_always_and_collects_never`.

   The inset (21px) and the bottom air are `:185` verbatim — `padding: '2px 0
   13px 21px'` — so the block hangs off the same left edge as `.rd-board__entry`
   and reads as part of the row above rather than as a new section. Unlike the
   entry it takes NO hairline: the row's own `border-bottom` is already under
   it, and a second one would draw a line through a single row.
   --------------------------------------------------------------------------- */
.rd-board__breakdown { padding: 2px 0 13px 21px; }
/* `:186` — gap 5. The strip is ABSENT rather than empty when a drill has no
   zones (the kit's own rule, `chip.rs` role C §1.2), so there is no height
   reserved for chips that may not exist. */
.rd-board__breakdown-chips { display: flex; gap: 5px; flex-wrap: wrap; }
/* `:189` — body @ 10, --rd-fg3, 8px under the chips. Attribution shown where
   the record was made. */
.rd-board__breakdown-attr {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
  margin-top: 8px;
}
/* `:190` — the actions row, 8px down, `gap: 16`. It draws nothing while it
   holds nothing — an empty flex row has no height — which is what it did for
   the slice that built the breakdown without the control. `Void this entry`
   landed in it 2026-08-21 and the geometry did not move, which is what the slot
   was reserved for. */
.rd-board__breakdown-actions { display: flex; gap: 16px; margin-top: 8px; }

/* ---------------------------------------------------------------------------
   VOID — the act in the breakdown, and the sheet it opens.

   Drawn: `rd-ro-day.jsx:191` (the act) and `:217-227` (the sheet), ruled at
   `47 (Mobile) RO Range Day.html:169-170`:

       'A sheet, because it must say what actually happens before it happens',
       'The refusal boundary is stated on the sheet: you can void only entries
        you recorded',

   ⚠️ **THE SHEET IS BUILT HERE BECAUSE THE KIT'S CONFIRM IS A CENTRED CARD.**
   `.rd-confirm` / `.rd-confirm__panel` (`adm1-5-1.css:20-35`) centre a 460px
   panel; row 07 draws a bottom-anchored sheet at phone width, and at a bay that
   is where the thumb already is. The MECHANISM is not re-invented — the markup
   borrows `ProOrgSheet`'s scrim-as-button, `role="dialog"`, Escape and focus
   restore — only the treatment is this screen's.
   --------------------------------------------------------------------------- */
/* `:191` — display 700 / 78 / .1em at 9, `--rd-danger`. A `button` in the
   markup where the drawing has a `span`, because it is pressable; every
   button-ness the browser supplies is reset here so what remains is the drawn
   text. The 56px floor is a ROW rule (`47:128`) and this is not a row — it is
   an act inside an opened disclosure, drawn at 9px by the same document. */
.rd-board__void-act {
  appearance: none;
  padding: 0;
  border: 0;
  background: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9px;
  color: var(--rd-danger);
  cursor: pointer;
}
.rd-board__void-act:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* `:221` — `position: relative … overflow: hidden` on the frame, which for a
   real screen is the viewport. Fixed rather than absolute for the same reason
   `.rd-confirm` is: the board scrolls, and a sheet that scrolled with it would
   leave the question behind. */
.rd-board__void {
  position: fixed;
  inset: 0;
  z-index: 40;
}

/* `:223` — `background: 'rgba(0,0,0,.55)'`.

   ⚠️ A LITERAL, DELIBERATELY, and the same one `.rd-prom__scrim` carries with
   the same note: this is not a surface colour that flips with the theme, it is
   a dimming of whatever is behind it, and no token carries a translucent black.
   A button rather than a div — see the component doc. */
.rd-board__void-scrim {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: rgba(0, 0, 0, .55);
  cursor: pointer;
}
.rd-board__void-scrim:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -4px;
}

/* `:224` — bottom-anchored, `borderTop: 1px t.line`, `padding: '18px 18px
   40px'`. The drawing's `t.tex ? '#141414' : t.surf` is its texture preview's
   branch, not a palette: `--rd-surf` is the token both themes already resolve
   (the same substitution `adm0-mobile.css:27-29` records for the drawer).

   ⚠️ THE 40 WAS 26 UNTIL EXPORT 29, and the number is the kit's now, not this
   part's. `48 Kit Rules.html`, Shell Geometry, James 25 Aug: *"Nothing tappable
   sits in the home-indicator zone … every bottom sheet ends with 40px under its
   last act"* — the rule puts the clearance in the kit so no screen has to
   remember it, so this reads `--rd-sheet-clear` (`kit-shell.css` §0) rather than
   restating a number that three other sheets also carry.

   This sheet takes the token because it is VIEWPORT-anchored: `.rd-board__void`
   above is `position: fixed; inset: 0`, so nothing between it and the home
   indicator has already spent the clearance. A sheet anchored inside
   `.rd-shell__content` must NOT take it — that region already ends 28px above
   the bottom edge. See `kit-shell.css` §0. */
.rd-board__void-sheet {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  background: var(--rd-surf);
  border-top: 1px solid var(--rd-line);
  padding: 18px 18px var(--rd-sheet-clear);
}
.rd-board__void-sheet:focus { outline: none; }

/* `:225` — display 800 / 72 / .02em at 17, `--rd-fg`. */
.rd-board__void-head {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 72%;
  letter-spacing: .02em;
  font-size: 17px;
  color: var(--rd-fg);
}
/* `:226` — body @ 11.5, `--rd-fg2`, 9px down, line-height 1.5. */
.rd-board__void-body {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg2);
  margin-top: 9px;
  line-height: 1.5;
  text-wrap: pretty;
}
/* UNDRAWN — ours. The refusal the sheet came back with, in danger ink, between
   the body and the acts: the RO reads why before their thumb reaches the
   button. Type follows the body it sits under. */
.rd-board__void-err {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-danger);
  margin-top: 11px;
  line-height: 1.5;
  text-wrap: pretty;
}
/* `:227` — `display: grid, gap: 8, marginTop: 15`. Stacked, full width: the
   drawing's two dense buttons at phone width. */
.rd-board__void-acts { display: grid; gap: 8px; margin-top: 15px; }
/* `:231` — body @ 10, `--rd-fg3`, 11px down, line-height 1.45. The refusal
   boundary, always printed. */
.rd-board__void-note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
  margin-top: 11px;
  line-height: 1.45;
  text-wrap: pretty;
}

/* `rd-squad.jsx:254` — the foot:
   `{ flex: 'none', paddingBottom: 12, display: 'grid', gap: 8 }`. A grid with
   an 8px gap, 12px of air below, and NO hairline and NO top padding.

   ⚠️ THE CITATION USED TO SAY `:232`, AND THE VALUES USED TO BE THE SETUP
   FOOT'S. `:232` in the snapshot (export 13) is `<div style={{ flex: 1,
   minWidth: 0 }}>` inside a board ROW — a stale line carried through the
   2026-08-21 custody move. The rule under it carried `padding-top: 11px` and
   `border-top: 1px solid var(--rd-line)`, which are the SETUP foot's values
   (`rd-squad.jsx:151` and `:155`), not this one's.

   THE TWO FEET DIFFER ON PURPOSE, AND THE LISTS ABOVE THEM SAY WHY. The setup
   foot gets the hairline, twice (`rd-squad.jsx:151` collapsed, `:155` full),
   and it closes a picker whose rows are hairlined UNCONDITIONALLY — `SQPick`
   draws `borderBottom: 1px solid t.line` on every row including the last
   (`:72`). The foot's rule continues that ruled edge. The board's list does the
   opposite: its rows drop the hairline on the last one on purpose
   (`:226`, `borderBottom: i === rows.length - 1 ? 'none' : ...`), which is the
   line a foot hairline would put straight back. The absence here is as
   deliberate as the presence there; it was not a borrow to keep.

   AND THE GAP IS REAL NOW. The drawing's `gap: 8` spaces EVERY child of the
   foot. It was previously faked by `margin-top: 8px` on `.rd-board__foot-note`
   below, which reached the note and nothing else — so when `Skip` renders
   (`squad_board.rs:2955`, a second, distinct `RdButton` in this same foot) the
   two controls stacked with ZERO gap and read as two primaries. The margin is
   dropped with the fake: with the grid gap in place it would put the note 16px
   down, and the drawing's note (`:261`, `:266`) carries no `marginTop`. */
.rd-board__foot {
  flex: none;
  display: grid;
  gap: 8px;
  padding-bottom: 12px;
}
/* ⚠️ **THE FOOT'S CONTROLS ARE PAINTED TO 56, AND THE KIT IS NOT TOUCHED.**
   `47 (Mobile) RO Range Day.html:49` puts the floor on the whole surface —
   *"the phone number covers the steppers, the keypad and the board rows"* — and
   the only other control this screen has is the foot. `RdButton` is 48 painted
   everywhere (`_legacy-kit-chamfer-button.css`: `.rd-btn__face` is 45 inner
   `+ 2 x 1.5` border), and that number is ITSELF adjudicated as the kit's
   default — *"the dense control is the most tapped in the system, used outdoors
   in gloves, so it is 48 PAINTED"*. So this is a BOARD-SCOPED raise, not a kit
   change: 53 inner + 2 x 1.5 = 56 painted, by the kit's own two-layer rule that
   *"height is a property of the painted box, and on a two-layer control the
   inner layer's min-height is not it."*

   Scoped to `.rd-board__foot` on purpose. `47` rules a floor for the RO's
   scoring surfaces; it does not rule one for every button in the product, and
   `.rd-btn` is shared by every screen there is. It reaches both controls in the
   foot — the primary and `Skip` — because both are a gloved thumb at a bay.

   ⚠️ AND IT OUT-RANKS `.rd-btn--tall .rd-btn__face` (49 inner / 52 painted):
   equal specificity, and this part concatenates later. That is the right way
   round — `tall` asks for MORE than the default and this floor is more than
   `tall` asks for — but it means a future `tall` in this foot would be
   flattened to 56 rather than raised past it. Nothing here asks for `tall`
   today; if something does, this rule is the one to revisit. */
.rd-board__foot .rd-btn__face { min-height: 53px; }
/* `rd-squad.jsx:261` (done) / `:266` (run) — body @ 10.5, `--rd-fg3`, centred,
   and no margin of its own: the 8px above it is the foot's grid gap. (The
   citation used to read `:236/:240`; `:236` in the snapshot is the row's
   `Up next` / `Waiting` line, another stale carry from the custody move.) */
.rd-board__foot-note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  text-align: center;
}


/* ===========================================================================
   THE BAY — 47 (Mobile) RO Range Day, row 04: one shooter's entry, in the two
   phases the hand actually works in. Source: `rd-ro-day.jsx:244`
   ("function ScreenROEntry"). Markup:
   `apps/web/src/pages/squad_score_entry.rs` (`SquadShooterEntry`).

   ⚠️ WHY THESE RULES LIVE IN *THIS* PART. `.rd-scorein--bay` below EXTENDS a
   block another lane declares — `.rd-scorein__*` is `adm0-picker.css`'s — so
   its position is load-bearing against that one file and free against every
   other. `order.txt` lists `adm0-picker.css` at :362 and this part at :538, so
   it concatenates later and the modifier wins. That arrangement is not new
   here: `s03-splits.css` already "sits after adm0-picker.css because it
   extends that file's `.rd-scorein__` block" (`order.txt:340`, and again at
   `:616`). This slice adds no part and moves no pin.

   The `.rd-bay__*` names are declared in no other part, so appending them
   re-orders no existing pair either.
   --------------------------------------------------------------------------- */

/* ⚠️ **THE 56px FLOOR, AS A MODIFIER RATHER THAN AN EDIT.**
   `47 (Mobile) RO Range Day.html:49`:

       56px is the phone touch floor on scoring surfaces. 68 was a tablet
       ruling for a gloved thumb at arm's length; the phone number covers the
       steppers, the keypad and the board rows.

   The steppers ship at 28 — `.rd-scorein__stepper-btn { width: 28px; height:
   28px; }` in `adm0-picker.css` — which is exactly half of it. That rule backs
   solo entry, the course stage and the hit-factor calculator as well as this
   row, and `47` rules a floor for the RO's scoring surfaces and for no others,
   so it is not the rule to change. The wrapper is emitted only when
   `ScoreInputs` is passed `bay: true`.

   Written as a FLOOR (`min-*`) and not as a size, because that is the word the
   ruling uses: 58x56 is the drawn key (`rd-ro-day.jsx:268`,
   "width: 58, height: 56"), and a longer count must be free to grow past it
   rather than clip. */
.rd-scorein--bay .rd-scorein__stepper-btn {
  width: auto;
  height: auto;
  min-width: 58px;
  min-height: 56px;
  font-size: 18px;
}
/* `:265` — mono @ 17, a fixed 26 wide and centred, so the count does not shift
   the two keys either side of it as it goes from 9 to 10. */
.rd-scorein--bay .rd-scorein__stepper-count {
  min-width: 26px;
  font-size: 17px;
  text-align: center;
}
/* `:258` — `minHeight: 56` on the zone row itself, so the row clears the floor
   even on a zone whose stepper is somehow shorter. */
.rd-scorein--bay .rd-scorein__zone-row { min-height: 56px; }

/* The row's own two-phase frame. It declares no width: the expanded row is as
   wide as the board leaves it. */
.rd-bay { display: flex; flex-direction: column; min-height: 0; }

/* `:275-277` — the hits foot: the control, then the advisory line 8px under it.
   Same shape as `.rd-board__foot` above and deliberately NOT the same class:
   this foot belongs to the expanded row and scrolls with it, while the board's
   is the screen's. */
.rd-bay__foot {
  flex: none;
  padding-top: 11px;
  padding-bottom: 12px;
}
/* The 56px floor reaches this control too, by the same two-layer rule the
   board's foot records above: 53 inner + 2 x 1.5 border = 56 painted. Scoped to
   `.rd-bay__foot`, never to `.rd-btn`. */
.rd-bay__foot .rd-btn__face { min-height: 53px; }
/* `:277` — body @ 10.5, `--rd-fg3`, centred. ADVISORY: it reports, it never
   gates. */
.rd-bay__count {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  text-align: center;
  margin-top: 8px;
}

/* `:277-280` — the time phase's collapsed zones. A real `button`, so the
   browser's own defaults have to be undone before it can look like the drawn
   strip. */
.rd-bay__zones {
  display: flex;
  align-items: center;
  gap: 5px;
  flex-wrap: wrap;
  width: 100%;
  margin-top: 13px;
  padding: 0;
  background: none;
  border: 0;
  text-align: left;
  cursor: pointer;
}
/* `:284` — body @ 9.5, `--rd-fg3`, 4px off the last chip. */
.rd-bay__adjust {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
  margin-left: 4px;
}
/* `:281` — the time field, 12px down. */
.rd-bay__field { margin-top: 12px; }
/* `:289` — the pad, 10px under the field. It takes this column's width and
   declares none of its own (`RdKeypad`'s own rule). */
.rd-bay__pad { margin-top: 10px; }

/* ---------------------------------------------------------------------------
   36 (Tablet) Pro Actual Range Day :125 — `ScreenROScore`, the tablet frame
   Source: rd-pro-rangeday.jsx `ScreenROScore` :202-245, `MDBar` :78-113
   -------------------------------------------------------------------------

   THE SAME BOARD, IN THE FRAME THE DRAWING PUTS IT IN. The document says so in
   its own words and it is why nothing below re-skins a row:

       This is the shooter's scoring screen with a bay header on it — not a
       Pro-side rewrite. ... A third copy is exactly how the two existing ones
       came to disagree.

   So every rule here is a descendant of `.rd-bayframe` — the wrapper
   `squad_board.rs`'s `BoardFrame` emits at `DeviceClass::Tablet` — and the
   board's own `.rd-board__*` and `.rd-bay__*` rules above are untouched. At
   phone the wrapper is not emitted at all and not one declaration below
   applies, which is what makes the phone arm byte-identical rather than merely
   unchanged-looking.

   ⚠️ WHY THESE RULES ARE ALLOWED TO BE HERE, AND `adm-rday.css` NAMES THE
   REASON. That part (owner: lane-lib) draws the rest of `36` and refuses this
   screen explicitly:

       THE SCORING SCREEN'S CONTROLS ARE NOT SCALED HERE, and that is a refusal
       rather than an omission. `ScreenROScore` is the SHOOTER's board with a
       header on it, and its pads, its time field and its `Save & Next` are
       `.rd-board__*` and `.rd-btn` — declared in `s09-board.css` (owner:
       1-track3, since 2026-08-21) ... Reported to design rather than worked
       around.

   This is that report answered from the owning side. No part is added, no part
   is re-ordered, and `order.txt` does not change — so the part-count pin in
   `cli/src/css.rs` does not move either.

   ⚠️ NO WIDTH BREAKPOINT, the same call `adm-rday.css` records for the rest of
   the family: it is TABLET-FIRST by the drawing's own exception ruling — *"it
   is drawn at iPad landscape rather than scaled down from 1280"* — so the
   geometry is the 1194 layout stated plainly and it degrades by flexing. The
   device fork is in Rust (`use_device_class`), not in a media query, because
   the two arms are different TREES and a query cannot swap a tree.

   ⚠️ 68px, NOT 56, AND THE RULING DISTINGUISHES THEM BY DEVICE. `36`:125:

       Targets are 68px, not 44. The mobile minimum assumes a bare finger
       indoors on a phone you are looking at. This is a gloved thumb, in sun, on
       a tablet, between shooters — and the cost of a mis-tap is a wrong score
       on somebody's record.

   and `47 (Mobile) RO Range Day.html:49` states the other half of the same
   sentence — *"56px is the phone touch floor ... 68 was a tablet ruling for a
   gloved thumb at arm's length"*. The 56px modifier above is the phone's and
   stays exactly as it is; these raise it at the one width that was ruled 68.
   Floors (`min-*`), never sizes, for the reason that block already gives.
   --------------------------------------------------------------------------- */

/* The column. TWO SHAPES, ONE CLASS, and the difference is who owns the
   viewport height:

   - Under the range-day mounts the bar is `RangeDayLayout`'s and this frame is
     the `flex: 1` child of `.rd-rday-app`, which is already `100dvh` — so it
     takes the height it is handed and declares none.
   - Under the two shooter twins there is no layout column at all, so
     `--own` supplies it: `100dvh` and NOT `100vh`, the same reasoning
     `.rd-shell` and `.rd-rday-app` both state — on a mobile browser `100vh` is
     the URL-bar-collapsed height, which would put the exit row below the fold. */
.rd-bayframe {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.rd-bayframe--own {
  height: 100dvh;
  width: 100%;
  box-sizing: border-box;
  background: var(--rd-bg);
  font-family: 'Archivo', system-ui, sans-serif;
}

/* The exit row, drawn ONLY where no `MDBar` is carrying one. It is the bar's
   own exit block lifted out of `MDBar` (`:103-109`): a 68px touch height, the
   accent chevron, the destination's name in body @ 14 on `--rd-fg2`, and a
   `--rd-line` rule under it — the same rule the drawn header has. */
.rd-bayframe__back {
  flex: none;
  border-bottom: 1px solid var(--rd-line);
}
.rd-bayframe__back-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 68px;
  padding: 0 32px;
  background: none;
  border: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
}
/* A real `button`, so the browser's own ring is replaced rather than removed —
   the same pair `.rd-board__void-act` above declares, at the same values. */
.rd-bayframe__back-btn:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}
.rd-bayframe__back-chev { font-size: 14px; color: var(--rd-acc); }
.rd-bayframe__back-lab { font-size: 14px; color: var(--rd-fg2); }

/* The banner keeps `RdShell`'s position and behaviour — full-bleed, in normal
   flow, PUSHING the board rather than covering it (the `#465` invariant, which
   the shell satisfies by position and so does this). */
.rd-bayframe__banner { flex: none; }

/* Everything under the chrome. `:226` — `padding: '24px 32px'`, `flex: 1`,
   `minHeight: 0`, and this is the element that scrolls. The 32px bottom is
   ours: the drawn frame is a fixed 834px tall and never scrolls, so it needs no
   tail, and a list that ends flush against the viewport edge reads as clipped. */
.rd-bayframe__body {
  flex: 1;
  min-height: 0;
  overflow: auto;
  padding: 24px 32px 32px;
}

/* The board's own top padding is the phone gutter's rhythm and the frame above
   supplies it here, so it is zeroed rather than added to. */
.rd-bayframe .rd-board { padding-top: 0; }

/* THE 68px FLOOR, ON THE FOUR CONTROLS THE DRAWING PUTS IT ON. Each is the
   phone's 56 raised at this width and nothing else about it changes.

   `:207` and `:209` — the zone pads. The minus and plus keys are `width: 58`
   and `height: 68`, and `:206` is the bordered row that holds them. */
.rd-bayframe .rd-scorein--bay .rd-scorein__stepper-btn { min-height: 68px; }
.rd-bayframe .rd-scorein--bay .rd-scorein__zone-row { min-height: 68px; }
/* UNDRAWN AT THIS EXACT NUMBER AND DERIVED FROM THE SCREEN'S OWN RULE. The
   drawn frame scores ONE shooter and has no row list, so no line of it sets a
   row height; what it sets is every other target on the screen at 68 (`:207`,
   `:224`, `:233`, `:237`, `:239`) and the ruling that generalises them —
   *"Targets are 68px, not 44 ... This is a gloved thumb, in sun, on a tablet"*.
   A 56px row among 68px controls is the phone floor left behind on a screen
   that ruled itself off it. */
.rd-bayframe .rd-board__row { min-height: 68px; }
/* `:239` — `Save & Next`, `height: 68`; the row's own hits foot takes it too,
   because it is the same act one level in. 65 inner + 2 x 1.5
   border = 68 painted, which is the same two-layer arithmetic the 56px floor
   records above; scoped to the two feet, never to `.rd-btn`. */
.rd-bayframe .rd-board__foot .rd-btn__face,
.rd-bayframe .rd-bay__foot .rd-btn__face { min-height: 65px; }
/* ==== S02-BOOT · 02 Auth — the restore beat, and the update notice · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   Two surfaces that render OUTSIDE the router, from `apps/web/src/main.rs`:
   the session-restore screen (`pages/gate_screens.rs`, `ScreenRestoring`) and
   the forward-compat "a newer version is available" notice.

   Drawn: **02 Auth** — `ScreenSplash`, in its `built` and `watchdog` states.
   The restore screen borrows that copy and `ScreenVerifying`'s geometry; the
   argument for the split is in `ScreenRestoring`'s own doc comment and is not
   repeated here.

   ⚠️ THE RESTORE SCREEN DECLARES NO RULES AT ALL, and that is the whole of
   what it needed. It composes `.rd-gate`, `.rd-gate--brand`, `.rd-gate__mark`
   and `.rd-gate__status` — every one of them already declared in
   `_legacy-kit-components-and-screens.css`'s AU1-gate-screens block, because
   the screen it sits beside is `ScreenVerifying` and being identical to it is
   the requirement. There is nothing here for it because there was nothing new
   to draw: what it replaced was a `min-h-screen` div carrying Tailwind's
   `text-gray-500`, and the fix is that it now carries the gate's classes
   instead of its own.

   Recorded rather than left silent, because a section file whose subject
   declares nothing looks like an omission. It is not one: the alternative —
   re-declaring the four rules under an `.rd-boot*` name — is how two screens
   that must not diverge acquire two places to diverge in.

   ⚠️ SO THIS PART EXISTS FOR THE SECOND SURFACE. The update notice is a
   genuinely new block, and it is here because it had NO classes before: it was
   written as inline `style:` attributes in fixed amber hexes (`#fffbeb`,
   `#fde68a`, `#92400e` — Tailwind's amber-50/200/800). Inline styles are
   invisible to the theme, to `check design-leaks`, and to every class sweep in
   the suite, so it painted a light strip across a dark app and no gate could
   see that it did.
   --------------------------------------------------------------------------- */

/* The strip. Full-bleed at the very top of the document, above the router and
   above the shell's own inset — a fact about the whole app, and it is now the
   ONLY strip in that position. ⚠️ Corrected 2026-09-01 (James, option 2): this
   used to read *"in the same position `.offline-banner` takes for the same
   reason"*. The offline strip moved INSIDE each shell frame, because a sibling
   above a `100dvh` shell has to be subtracted from it in four places. This one
   stays outside the router on purpose and pays no such cost: it is rendered
   from `main.rs` above the `Router` itself, so it precedes every shell there
   is and belongs to no one of them.

   Tones are the warning ladder's, not a second amber: `--rd-warn` is what
   `RdBanner`'s warning variant already uses, and a newer server is exactly
   that severity — recoverable, nothing lost, worth saying once. It is NOT
   `RdBanner` itself because that primitive renders a message and takes no
   actions, and this strip is two controls with a sentence beside them. */
.rd-update {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 16px;
  background: var(--rd-surf);
  border-bottom: 1px solid var(--rd-warn);
  color: var(--rd-warn);
}

.rd-update__msg {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.5;
}

.rd-update__acts {
  display: flex;
  gap: 8px;
  flex: none;
  align-items: center;
}

/* Square, like every other control in the kit — the substrate bans a radius and
   the inline version carried one (`border-radius: 0.25rem`). An outline rather
   than a fill: a solid amber block at the top of every page would outrank the
   screen under it, and this notice is never the most important thing there. */
.rd-update__refresh {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 9.5px;
  color: var(--rd-warn);
  background: transparent;
  border: 1px solid var(--rd-warn);
  padding: 5px 11px;
  cursor: pointer;
}

/* The dismiss is quieter than the action it sits next to — it is the way out,
   not the thing to do. `--rd-fg3` rather than the warn tone for exactly that
   reason: two amber controls side by side read as two suggestions. */
.rd-update__dismiss {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 16px;
  line-height: 1;
  color: var(--rd-fg3);
  background: transparent;
  border: 0;
  padding: 2px 4px;
  cursor: pointer;
}

.rd-update__refresh:hover,
.rd-update__dismiss:hover {
  color: var(--rd-fg);
}
/* ==== S14-CHAL · 14 People — Challenges · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   `/challenges`, `/challenges/new` and `/challenges/:id` (`pages/challenges.rs`).
   Drawn: **14 People**, Stage Four — `ScreenChallenges`, `ScreenChallenge` and
   `ScreenChallengeNew`.

   ⚠️ THIS IS THE SECOND PART OFF ONE DRAWING. `s14-add.css` already owns 14
   People's Add A Friend screen; this owns its Challenges stage. Two owners on
   one drawing rather than one part per document, and the names are disjoint by
   construction — `.rd-add*` there, `.rd-chal*` here. They merge if the two
   stages ever come to one screen, which they will not.

   The shell, heading, chips, banner, avatar, loading line and buttons are kit.
   What is page CSS is the three things the kit has no shape for: the list row
   (a three-line card with a state line the kit's rows do not carry), the ranked
   board (a four-column row with a rank gutter and an em-dash column), and the
   invite picker's checkbox.

   ⚠️ FOUR BLOCKS, NOT ONE, AND THE SPLIT IS THE ROUTE TABLE'S — plus one
   frame. `.rd-chal*` is the list, `.rd-chal-d*` the detail, `.rd-chal-n*` the
   create form, and `.rd-chal-i*` the invite frames. They share only the four
   `.rd-chal__` utilities the screens genuinely have in common — the loading
   line, the error banner's spacing, and the two empty states. A single block
   would have made every selector on this sheet ambiguous about which screen it
   paints.

   ⚠️ `.rd-chal-i*` IS THE ONE BLOCK WITH NO ROUTE OF ITS OWN, and that is the
   drawing's ruling rather than an oversight: *accepting is not a screen, it is
   how the challenge opens* (`14 People`, row 08, `ScreenChallengeInvite`), so
   `/challenges/:id` renders it in place of the detail body for a viewer who has
   not answered. It is a separate BLOCK because it is a different board — no
   avatars, "Not shot" in words where the detail draws an em-dash, and a rank
   gutter that dashes instead of dotting — and reusing `.rd-chal-d*` would have
   meant three modifiers on the detail's rows carrying "except on the invite
   frame".

   THE LIST ROW HAS ITS FOURTH COLUMN. It was absent while `ListChallenges`
   returned no standings, no drill and no participant count; those are on the
   list response now, so `.rd-chal__row-figure` paints the drawn top figure
   beside the name. It is still ABSENT rather than styled-and-empty on a row the
   call did not compute — that decision lives in `row_figure`, not here.
   --------------------------------------------------------------------------- */

/* ---- shared across the three screens ---- */

.rd-chal,
.rd-chal-d,
.rd-chal-n {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  padding-top: 4px;
}

/* The no-claim ground (docs/design/kit/L1-loading.md §4.1) — 43 Loading
   States.html:52: "real chrome, empty ground, one mark in the middle". Kept at
   the geometry the sentence used to stand in, because the sentence was in the
   right PLACE; what changed is what stands there. The watchdog composes its own
   column, so this rule only has to centre what it is given. */
.rd-chal__loading {
  padding: 32px 0;
  display: flex;
  justify-content: center;
}

/* The WARM refresh mark — a tab press behind rows that are already read leaves
   them exactly where they are (43 Loading States.html:50); this 13px sweep is
   the only change (12 until the ladder ruling, C-43.1, 27 Aug 2026). */
.rd-chal__refresh,
.rd-chal-t__refresh {
  display: flex;
  justify-content: flex-end;
  padding: 4px 0;
}

.rd-chal__banner { margin: var(--rd-banner-gap) 0 0; }

/* Both "there is nothing here" states. Set in BODY type, both lines, for the
   reason s12.css records at length: the kit's empty state renders its title in
   a display role, and a display role is uppercase by construction, so the
   drawn sentence would come out shouting. */
.rd-chal__empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 12px;
  padding-bottom: 40px;
}

.rd-chal__empty-lead {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  line-height: 1.5;
  color: var(--rd-fg2);
}

.rd-chal__empty-body {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.5;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

/* ---- the list ---- */

.rd-chal__tabs { margin-top: 13px; }

.rd-chal__list {
  flex: 1;
  min-height: 0;
  margin-top: 13px;
  overflow-y: auto;
}

/* A row is a button and looks like nothing. The drawn list has no cards and no
   chevrons — rows separated by a rule, which is what the shipped white
   `rounded-lg shadow` cards were not. */
.rd-chal__row {
  display: block;
  width: 100%;
  min-height: 74px;
  padding: 12px 0;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  background: transparent;
  text-align: left;
  cursor: pointer;
  font: inherit;
}

.rd-chal__row:last-child { border-bottom: 0; }

.rd-chal__row-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

.rd-chal__row-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: 0.04em;
  font-size: 13px;
  color: var(--rd-fg);
  flex: 1;
  min-width: 0;
}

/* The drawn top figure, right of the name on the row's baseline. 15px mono,
   which is the same size and role the detail board's value column takes — one
   figure, two screens, one weight. `rd-num` supplies the family and the tabular
   figures; this supplies the size, the tone and the column. */
.rd-chal__row-figure {
  flex: none;
  font-size: 15px;
  font-weight: 400;
  color: var(--rd-fg3);
}

/* Quiet is the DEFAULT here and volt is the modifier, which is the opposite way
   round from the detail board's value column — deliberately. There the base case
   is a real figure and the em-dash is the exception; on a list of boards, a row
   nobody has shot is entirely ordinary, and the base rule must be the one that
   is safe to land on when a modifier is dropped. */
.rd-chal__row-figure--set { color: var(--rd-acc); }

.rd-chal__row-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  margin-top: 4px;
}

/* The drawn third line is a tracked micro-label, accent while the challenge is
   still open and quiet once it is finished. Ours says the wire's status because
   the outstanding count is not in the list response. */
.rd-chal__row-state {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 8.5px;
  color: var(--rd-fg3);
  margin-top: 5px;
}

.rd-chal__row-state--live { color: var(--rd-acc); }

/* The foot: one commit above a rule, held out of the scroll. */
.rd-chal__foot {
  flex: none;
  padding-top: 11px;
  padding-bottom: 12px;
  border-top: 1px solid var(--rd-line);
}

/* ---- the detail board ---- */

.rd-chal-d__chips {
  display: flex;
  gap: 6px;
  margin-bottom: 9px;
}

.rd-chal-d__sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg2);
  margin-top: 7px;
}

.rd-chal-d__board {
  flex: 1;
  min-height: 0;
  margin-top: 16px;
}

.rd-chal-d__rows { margin-top: 6px; }

.rd-chal-d__nobody {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
  margin-top: 9px;
}

.rd-chal-d__row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 50px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-chal-d__row:last-child { border-bottom: 0; }

/* A fixed-width gutter so the avatars line up whether the row is ranked 1 or
   unranked. `rd-num` supplies the family and the tabular figures; this supplies
   only the size, the tone and the column. */
.rd-chal-d__rank {
  width: 11px;
  flex: none;
  font-size: 11px;
  font-weight: 400;
  color: var(--rd-fg3);
}

.rd-chal-d__rank--me { color: var(--rd-acc); }

.rd-chal-d__who {
  flex: 1;
  min-width: 0;
}

.rd-chal-d__name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg);
}

.rd-chal-d__name--me {
  color: var(--rd-acc);
  font-weight: 700;
}

.rd-chal-d__unshot {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
  margin-top: 2px;
}

.rd-chal-d__value {
  flex: none;
  font-size: 15px;
  font-weight: 400;
  color: var(--rd-fg);
}

.rd-chal-d__value--me { color: var(--rd-acc); }

/* The em-dash column. Quiet in its own right — a row with no run must not read
   as loudly as one with a figure, and it must NOT take the accent even when it
   is yours. */
.rd-chal-d__value--none { color: var(--rd-fg3); }

.rd-chal-d__foot {
  flex: none;
  padding-bottom: 12px;
  display: grid;
  gap: 8px;
}

.rd-chal-d__note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  text-align: center;
}

/* ⚠️ UNDRAWN AND FLAGGED. Mark Complete and the drill's own leaderboard are
   both shipped capabilities the drawing has no control for, so they are kept
   and rendered quiet and last — the same disposal `.rd-q__held*` gets one
   drawing over. A rule above the pair so they read as an appendix to the
   screen rather than as two more things it is asking for. */
.rd-chal-d__extra {
  display: grid;
  gap: 8px;
  margin-top: 10px;
  padding-top: 11px;
  border-top: 1px solid var(--rd-line);
}

.rd-chal-d__link {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 9px;
  color: var(--rd-acc);
  background: transparent;
  border: 0;
  padding: 4px 0;
  cursor: pointer;
  text-align: center;
}

.rd-chal-d__link:hover { color: var(--rd-fg); }

/* ---- the invite frames ---- */

/* `ScreenChallengeInvite`, both states. Rendered inside `.rd-chal-d`, which
   already supplies the column and the top padding, so this block declares only
   what the frame adds. */

/* `{drill} · {rounds} rounds · {n} shooters`. Same size and tone as the
   detail's subtitle — it is the same line of the same document, one clause
   different — but its own selector, because the two screens set it against
   different things above it and a shared rule would tie their spacing
   together. */
.rd-chal-i__sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  margin-top: 9px;
}

.rd-chal-i__board {
  flex: 1;
  min-height: 0;
  margin-top: 17px;
}

.rd-chal-i__rows { margin-top: 6px; }

.rd-chal-i__nobody {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
  margin-top: 9px;
}

/* Shorter than the detail's 50px row: no avatar, so nothing in it needs the
   height. The rule is on the row rather than the last-child override the
   detail uses, because the accepted frame appends a You row BELOW the list and
   the boundary between the two has to be one line, not two or none — see
   `--you`. */
.rd-chal-i__row {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 46px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-chal-i__row:last-child { border-bottom: 0; }

/* The You row the accepted frame adds. It takes its rule on TOP and drops the
   bottom one, so it reads as appended to the board rather than as one more of
   its rows — which is exactly what it is: you have joined, you have not shot,
   and nothing about the standings above has changed. */
.rd-chal-i__row--you {
  border-top: 1px solid var(--rd-line);
  border-bottom: 0;
}

/* A fixed gutter, as on the detail board, so the names line up whether the row
   is ranked or dashed. `rd-num` supplies the family and the tabular figures. */
.rd-chal-i__rank {
  width: 14px;
  flex: none;
  font-size: 11px;
  font-weight: 400;
  color: var(--rd-fg3);
}

.rd-chal-i__name {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
}

/* The only accented name on this board, and it appears exactly once: the You
   row, in the one state that draws it. */
.rd-chal-i__name--me { color: var(--rd-acc); }

.rd-chal-i__value {
  flex: none;
  font-size: 13px;
  font-weight: 400;
  color: var(--rd-fg2);
}

/* "Not shot" is text in a numeric column, so it is quiet — it must not read as
   loudly as a figure, which is a result. Same call as the detail board's
   em-dash column, arrived at from the other direction: there an absence, here
   two words. */
.rd-chal-i__value--none { color: var(--rd-fg3); }

.rd-chal-i__foot {
  flex: none;
  padding-bottom: 12px;
}

/* The accepted frame's copy, above its button — *"Nothing is scheduled — a
   challenge is a comparison, not an appointment."* Left-set and not centred:
   it is two sentences of explanation, and the foot note on the detail screen
   that IS centred is a single derived phrase. */
.rd-chal-i__note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--rd-fg3);
  margin-bottom: 12px;
  text-wrap: pretty;
}

/* ---- create ---- */

.rd-chal-n__field { margin-top: 13px; }

/* The drill picker is a native `<select>` inside a chamfered box — see the
   module doc for why the drawn row is not transcribed. The box owns the border
   and the ground; the select is stripped back so the two do not draw two
   borders, and it keeps its own arrow because a select with no affordance is a
   control nobody opens. */
.rd-chal-n__select-inner {
  padding: 0;
  min-height: 44px;
  display: flex;
  align-items: center;
}

.rd-chal-n__select {
  width: 100%;
  border: 0;
  background: transparent;
  color: var(--rd-fg);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  padding: 11px 13px;
  cursor: pointer;
}

.rd-chal-n__invite {
  margin-top: 15px;
  flex: 1;
  min-height: 0;
}

.rd-chal-n__invite-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

/* The label and, while the region is loading, the 10px sweep BESIDE it — 43
   Loading States.html:51: "its label is real, because the page knows it has a
   gear list before it knows what is on it, and the mark sits beside that label
   so a page with two slow regions says which is which without a word." The
   group exists so the mark stays with the words rather than being pushed to the
   far side by the head row's `space-between`. */
.rd-chal-n__invite-lab {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* The running count. Accent, because it is the answer to the question the
   label just asked and the only feedback the multi-select gives at a glance. */
.rd-chal-n__selected {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-acc);
}

/* ⚠️ `.rd-chal-n__loading` DELETED 2026-08-19 with the loading migration
   (slate 3). It painted "Loading friends…" as a line of grey text where the
   invite list goes; the region now takes the stack with a 10px sweep on its
   real label, and a rule with no markup left is a rule the next reader has to
   go and disprove. */

.rd-chal-n__none { margin-top: 9px; }

.rd-chal-n__none-lead {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--rd-fg2);
}

.rd-chal-n__none-body {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.5;
  color: var(--rd-fg3);
  margin-top: 6px;
  text-wrap: pretty;
}

.rd-chal-n__none-act { margin-top: 11px; }

.rd-chal-n__friends {
  margin-top: 4px;
  overflow-y: auto;
}

.rd-chal-n__friend {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 46px;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  background: transparent;
  cursor: pointer;
  font: inherit;
  text-align: left;
}

.rd-chal-n__friend:last-child { border-bottom: 0; }

.rd-chal-n__friend-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg);
  flex: 1;
  min-width: 0;
}

/* Square, 18px, 1.5px stroke — the drawn checkbox exactly. It is a real box
   when empty and a filled one when set: the shipped control drew a tick out of
   nothing, so an unselected row had no affordance at all and the list read as
   text rather than as something to choose from. */
.rd-chal-n__check {
  width: 18px;
  height: 18px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1.5px solid var(--rd-ctrl);
  background: transparent;
}

.rd-chal-n__check--on {
  border-color: var(--rd-acc);
  background: var(--rd-volt);
}

/* The tick is on the fill, so it takes the on-volt ink and NOT the accent — a
   volt square with an accent tick is brown on tan in the light theme. */
.rd-chal-n__check svg {
  width: 9px;
  height: 7px;
  stroke: var(--rd-on-volt);
}

.rd-chal-n__foot {
  flex: none;
  padding-top: 12px;
  padding-bottom: 12px;
}

/* ---- tablet: the invite board beside its list ---- */
/* ⚠️ ADDED 2026-08-18 — `ScreenTabChallengeInvite`, `rd-tablet-relay.jsx:81`.
   `RdTabletShell`, `RdPanes`, `RdTabletRail` and `RdSectionTabs` supply the
   chrome; this is the one page-level thing they have no shape for — the list
   pane's own row. The detail pane reuses `.rd-chal-i__*` UNCHANGED, because
   `ChallengeInviteFrame` (pages/challenges.rs) is the same content tree the
   phone renders — see that component's own doc for why. */

/* `TabPanes`'s own list child (`rd-tablet-relay.jsx:89`, "flex: 1, minHeight:
   0, overflow: 'hidden'"). `.rd-tpanes__list` (kit-tablet-panes.css) already
   owns the fixed width and the scroll; this is the column of rows inside it. */
.rd-chal-t__list {
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

/* ⚠️ ADDED 2026-08-18 with the LIST route (red-line item 9), and one of the two
   shapes LEFT THIS PART on 2026-08-19:

   `__chips` — the carried Active/Completed pair. Its geometry was borrowed from
   the People screen's own chip strip (`rd-tablet-social.jsx:128`) rather than
   invented, so the two list panes in one drawn document agree — and that is now
   literally true rather than a transcription of it: the strip is the kit's
   `RdTabChipRow` at `TabChipRowPad::Tight` (`kit-tab-chip-row.css`), which
   People's own pane mounts too. This part declares nothing for it.

   `__act` — the header action's link reset, the same shape `.rd-tppl__act` and
   `.rd-tev__act` take. The 44px box is `RdTabletShell`'s. */
.rd-chal-t__act {
  text-decoration: none;
  color: inherit;
  display: flex;
  align-items: center;
}

/* One row: name, `{drill} · {n} shooters}`, and the accent tag on the
   challenge this screen is about (`rd-tablet-relay.jsx:91`, minHeight 66,
   padding '0 16px', borderLeft 3px). */
/* ⚠️ THE LINK RESET WAS ADDED 2026-08-18, when `Route::Challenges` — the LIST
   route — joined the chrome seam on James's red-line item 9 and the row became
   a `Link` (see `tablet_challenge_row`). The rule is otherwise the drawn one,
   unchanged: an `<a>` and a `<div>` paint identically here, which is what lets
   the detail screen's own non-navigating row keep using it. */
.rd-chal-t__row {
  min-height: 66px;
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 0 16px;
  border-bottom: 1px solid var(--rd-line);
  border-left: 3px solid transparent;
  text-decoration: none;
  color: inherit;
}

.rd-chal-t__row:last-child { border-bottom: 0; }

/* The one selected row — always the invited challenge, never a second one
   (`rd-tablet-relay.jsx:91`, `i === sel`, and this screen's `sel` is always
   0). Accent rule and the raised ground together, one modifier. */
.rd-chal-t__row--on {
  border-left-color: var(--rd-acc);
  background: var(--rd-surf2);
}

.rd-chal-t__row-copy {
  flex: 1;
  min-width: 0;
}

.rd-chal-t__row-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg);
}

.rd-chal-t__row-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  margin-top: 3px;
}

/* The accent "Invited" tag — `rd-tablet-relay.jsx:99`, tracked micro-label,
   accent, no border and no fill (unlike the closed-org screen's `Closed`
   tag, which is a bordered chip: this one has nothing to be closed FROM,
   it is naming an open question). */
.rd-chal-t__row-tag {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 8.5px;
  color: var(--rd-acc);
  flex: none;
}

/* The detail pane's own padding — `rd-tablet-relay.jsx:103`, "padding: '22px
   26px', display: 'flex', flexDirection: 'column', overflow: 'hidden'".
   `.rd-tpanes__detail` (kit-tablet-panes.css) already sets the flex column
   and the scroll; this adds only the inset the drawing gives the content. */
.rd-chal-t__detail {
  padding: 22px 26px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
/* ==== S14-PERSON · 14 People — the person page (rd-people.jsx) · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   `/users/:id` (`pages/user_profile.rs`). Drawn: **14 People**, Stage Two —
   `ScreenPerson`, in all three of its relationship states.

   ⚠️ NAMES ARE DISJOINT FROM THE OTHER TWO PARTS OFF THIS DRAWING, and that is
   why appending here re-orders no existing pair. `s14-add.css` owns
   `.rd-add*` (the code landing), `s14-chal.css` owns `.rd-chal*` (Challenges),
   and this owns `.rd-person*`. None of the three declares or overrides a
   selector another one names.

   The shell, heading role, labels, banner, stat and buttons are kit. What is
   page CSS is the four shapes the kit has no vocabulary for: the identity
   block, the two-up stats grid inside the chamfer box, the list row this
   screen shares with the drawn `Shared drills` rhythm, and the quiet foot.

   ⚠️ THE FOOT IS THE SAME FOOT `s14-add.css` PAINTS, and the two must stay
   indistinguishable. `AddByCode` borrowed `ScreenPerson`'s foot in batch 1
   because it had no drawn foot of its own; this screen owns it. The values
   below are that file's, to the pixel — a shooter who scans a code and a
   shooter who opens the same person from their friends list must see one
   control and one aftermath, not two that nearly agree. They are not shared
   selectors because the two blocks belong to two screens and a shared name
   would make either one unable to move; they are shared VALUES, and this note
   is the tie.
   --------------------------------------------------------------------------- */

.rd-person {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  padding-top: 6px;
}

/* The identity block. Drawn as a 56px mark with a 13px gutter beside it; ours
   mounts the shared `Avatar` at 64 (`w-16`), which is the nearest rung on that
   component's Tailwind size bridge — the mark is the only thing on this page
   that can carry a real photograph, and adding a bridge rung to gain 8px would
   put a new arm in another file's pinned list for no legibility. */
.rd-person__who {
  display: flex;
  align-items: center;
  gap: 13px;
}

.rd-person__ident {
  flex: 1;
  min-width: 0;
}

/* The name is the page's subject and takes the display role at its own size —
   not `RdHeading`, which is a screen TITLE and would put "TheSpiciestDev" in
   the slot "Add A Friend" occupies two screens earlier. */
.rd-person__name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 900; font-stretch: 62%; letter-spacing: .01em;
  font-size: 19px;
  line-height: 1;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* `@handle · joined Mar 2024`. One line, and the page builds it so that a
   missing half never leaves the separator stranded. */
.rd-person__meta {
  margin-top: 4px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The stats card's inner grid. Drawn as three equal columns; ours is two,
   because the third figure has no source — see the page's own note. Two
   columns rather than three-with-a-hole: an empty cell in a three-up grid
   reads as a value that failed to load. */
.rd-person__stats {
  margin-top: 16px;
  padding: 13px 15px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

/* The scrolling middle. `min-height: 0` is what lets it actually scroll inside
   the shell's flex column rather than pushing the foot off the bottom. */
.rd-person__body {
  flex: 1;
  min-height: 0;
  margin-top: 16px;
}

/* Space above the second label. The first one takes the body's own margin. */
.rd-person__sec { margin-top: 16px; }

.rd-person__list { margin-top: 6px; }

/* The drawn `Shared drills` row: a name, a right-aligned note, a 42px rhythm
   and a hairline between rows — never after the last one, which would draw a
   line under a list with nothing below it. */
.rd-person__row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 42px;
  border-bottom: 1px solid var(--rd-line);
  text-decoration: none;
}

.rd-person__row:last-child { border-bottom: none; }

.rd-person__row-name {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rd-person__row-note {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
}

/* An earned badge. Two lines rather than the drill row's one-plus-note,
   because a description is prose and wraps. */
.rd-person__ach {
  padding: 9px 0;
  border-bottom: 1px solid var(--rd-line);
}

.rd-person__ach:last-child { border-bottom: none; }

.rd-person__ach-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg);
}

.rd-person__ach-desc {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

/* Every quiet sentence this screen says about an absence — no public drills,
   no badges, no profile. One role, because they are one kind of statement. */
.rd-person__empty {
  margin-top: 8px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  line-height: 1.5;
  color: var(--rd-fg2);
  text-wrap: pretty;
}

/* The foot. `margin-top: auto` puts the action at the bottom of the shell the
   way the drawing's `flex: 'none'` foot does, without pinning it — a short
   viewport scrolls to it rather than overlapping the content above. Same
   values as `.rd-add__foot`; see the header. */
.rd-person__foot {
  flex: none;
  margin-top: auto;
  padding-top: 14px;
}

/* Above the control it governs, which is the kit's standing rule. */
.rd-person__send-error { margin: 0; }

/* "Request sent". Quiet on purpose — the drawing spends the screen's one loud
   treatment on the send and none on its aftermath: *"the undo should never be
   as loud as the thing it undoes"*. `.rd-add__sent`'s values exactly. */
/* ⚠️ **PIXEL-TIED TO `.rd-add__sent*` IN s14-add.css.** Two surfaces render the
   same drawn foot — this page and the code landing — and the drawing gives them
   one treatment. The tie is asserted, not asked for: `user_profile.rs` compares
   the two rule bodies out of the shipped sheet and fails naming the property
   that drifted. Edit one, edit both. */
.rd-person__sent {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 48px;
  border-top: 1px solid var(--rd-line);
  padding-top: 11px;
}

/* The copy column. It was the row itself until `Undo` arrived beside it — the
   drawn sent state is a two-line block AND a control on one line, which is a
   row with a column in it. */
.rd-person__sent-copy {
  display: flex;
  flex-direction: column;
  gap: 3px;
  flex: 1;
  min-width: 0;
}

/* ⚠️ **QUIETER THAN THE THING IT UNDOES, and the drawing spends a decided-here
   item on why**: *"Legacy makes UNSEND REQUEST the identical volt fill ADD
   FRIEND was, so the screen shouts the same either way."* A word in `fg3`,
   never a fill, never danger — withdrawing a request destroys nothing that
   anybody agreed to. */
.rd-person__undo {
  flex: none;
  border: 0;
  background: none;
  padding: 0 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-fg3);
  cursor: pointer;
}

.rd-person__undo:disabled { cursor: default; opacity: .55; }

.rd-person__undo:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

.rd-person__sent-h {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 10.5px;
  color: var(--rd-fg2);
}

.rd-person__sent-p {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  line-height: 1.4;
  color: var(--rd-fg3);
}
/* ==== S14-FR · 14 People — Friends (rd-people.jsx) · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   `/profile/friends` (`pages/friends.rs`). Drawn: **14 People**, Stage Two —
   `ScreenFriends` in its list, requests and empty states, plus the part of
   `ScreenAddFriend` this screen has to host until that screen has a route.

   ⚠️ THE FOURTH PART OFF DRAWING 14, AND ALL FOUR ARE NAME-DISJOINT:
   `.rd-add*` (s14-add, the code landing), `.rd-chal*` (s14-chal, Challenges),
   `.rd-person*` (s14-person, the person page) and `.rd-fr*` here. None
   declares or overrides a selector another one names, so their relative order
   is not load-bearing.

   The shell, heading, chips, banner, text field, confirm modal and the one
   filled button are kit. What is page CSS is `PRow2` — the row shape the
   drawing uses for all three lists — its two per-row action treatments, the
   two-step empty state, and the labelled door row borrowed from
   `ScreenAddFriend`'s foot.

   ⚠️ EVERY PER-ROW CONTROL HERE IS AN OUTLINE OR A QUIET WORD, NEVER A FILL,
   and the drawing gives the arithmetic rather than the preference: *"Filled,
   Accept would multiply with the list — five requests puts five volt fills on
   a screen whose own argument is that the legacy landing fails at five.
   Anything drawn per row cannot take a fill."* The screen's single fill is
   `ADD A FRIEND`, and it is `RdButton`'s, not this file's.

   ⚠️ `.rd-fr__no` IS NEUTRAL, NOT DANGER, and that is a decision the shipped
   screen got wrong in the other direction: *"Declining a friend request is not
   destructive, it is just an answer."* `.rd-fr__remove` IS danger-toned,
   because removing a friend destroys a row that exists.
   --------------------------------------------------------------------------- */

.rd-fr {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  padding-top: 4px;
}

.rd-fr__chips { margin-top: 13px; }

/* The list region. `min-height: 0` is what lets it scroll inside the shell's
   flex column instead of pushing the foot off the bottom. */
/* ⚠️ A FLEX COLUMN so the empty state can be CENTRED IN IT. `rd-people.jsx:105`
   draws the empty case as `flex: 1` with `justifyContent: 'center'` inside the
   list region; a `flex: 1` child of a block box grows against nothing, so the
   region that holds it has to be the column. The rows are unaffected — a flex
   item does not grow unless told to, and `.rd-fr__row` is not. */
.rd-fr__list {
  flex: 1;
  min-height: 0;
  margin-top: 12px;
  display: flex;
  flex-direction: column;
}

/* The WARM refresh mark (docs/design/kit/L1-loading.md §4.1). A refetch behind
   rows that are already read changes exactly one thing on this screen — 43
   Loading States.html:50: "content that is already on screen is never dimmed,
   blanked, or covered while it refreshes". The list carries no freshness line
   of its own, so the 13px sweep rides the top of the container that carries
   `aria-busy`. */
.rd-fr__refresh {
  display: flex;
  justify-content: flex-end;
  padding: 4px 0;
}

/* `PRow2`. One shape for friends, requests and search results — *"the actions
   change, the row does not, so accepting somebody looks like the same object
   as finding them."* The hairline divides rows and never closes the list. */
.rd-fr__row {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 60px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-fr__row:last-child { border-bottom: none; }

/* The identity half, and on a friend row it is also the tap target. It is a
   separate element from the row because the row also hosts an action, and
   interactive elements cannot nest — the same constraint `event_detail.rs`'s
   drill row works around. */
.rd-fr__open {
  display: flex;
  align-items: center;
  gap: 11px;
  flex: 1;
  min-width: 0;
  text-decoration: none;
}

.rd-fr__ident {
  flex: 1;
  min-width: 0;
}

.rd-fr__name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .04em;
  font-size: 12.5px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The row's second line. Drawn as `@{tag} · {sub}`; ours is the handle alone,
   because the `sub` half — "18 drills · 6.4 best", "2 friends in common" —
   has no source on any of the three lists. Absent rather than faked. */
.rd-fr__tag {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── Per-row actions ────────────────────────────────────────────────── */

.rd-fr__acts {
  display: flex;
  gap: 6px;
  flex: none;
}

/* Accept: a volt OUTLINE. See the header for the arithmetic.

   ⚠️ CHAMFER 8, NOT RADIUS 8 — `48 Kit Rules.html:33`, 21 Aug 2026: *"The kit
   is chamfered; radius is not a style option … A new rounded control is a
   defect, not a variant."* The complete list of what may still be round is
   circular avatars, the sweep and skeleton arcs, OS-owned controls, device
   frames and map pins; a per-row button is none of those. These two shipped
   `border-radius: 8px` and now cut the same 8 as a chamfer.

   ⚠️ AND IT ROUTES THROUGH `RdChamferBox`, BECAUSE THESE ARE STROKED. 48's
   other half, in the same sentence: *"Every stroked chamfer routes through
   TOut (a CSS border under a clip-path renders the diagonal bare — recurred
   three times)."* So a `clip-path` added to the rules below would have been the
   fourth recurrence, not a fix: the outline IS the affordance here, and the cut
   would have eaten it on the diagonal.

   The construction is s47's whole-row control (`s47.css:41`), for the reason
   that file already gives — a `<button>` cannot nest and the chamfer is a
   two-layer clip. The `<button>` keeps the height, the hit box and the focus
   ring and paints NOTHING; the border is the box's outer layer, the fill is its
   inner, and the label rides `__face`, which is that inner. The stroke roles
   move with it: `--rd-acc` becomes `Stroke::Accent`, `--rd-ctrl` becomes
   `Stroke::Ctrl` — the header's ruling that No is neutral, not danger, is
   unchanged and now says itself in the enum. */
.rd-fr__accept,
.rd-fr__no {
  height: 31px;
  display: flex;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

/* The clip lives on `.rd-box`, and a `clip-path` eats an `outline` — which is
   exactly why the ring stays out here on the unclipped `<button>`, the same
   division `.rd-roev` makes (`s47.css`) and `.rd-btn` argues at length. */
.rd-fr__accept:focus-visible,
.rd-fr__no:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

.rd-fr__accept-face,
.rd-fr__no-face {
  display: flex;
  align-items: center;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 9.5px;
}

.rd-fr__accept-face {
  padding: 0 11px;
  color: var(--rd-acc);
}

.rd-fr__no-face {
  padding: 0 10px;
  color: var(--rd-fg2);
}

/* Add, on a search result. The drawn treatment is a word in the accent, not a
   bordered control — the row itself is the object and the word is what you do
   to it. */
.rd-fr__add,
.rd-fr__sent,
.rd-fr__remove {
  flex: none;
  border: 0;
  background: none;
  padding: 0 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 9.5px;
}

.rd-fr__add {
  color: var(--rd-acc);
  cursor: pointer;
}

/* Already asked. Quiet, and there is no Undo beside it because there is no
   unsend on the wire — the same disposal the person page's foot makes. */
.rd-fr__sent { color: var(--rd-fg3); }

/* UNDRAWN and kept: `RemoveFriend` ships and this row is its only door. Quiet,
   never a fill, and danger-toned because it is the one control on this screen
   that destroys something. */
.rd-fr__remove {
  color: var(--rd-danger);
  cursor: pointer;
}

/* ── The two-step empty state ───────────────────────────────────────── */

/* Drawn centred in the list region with the explanation under the headline.
   Three states share it — nobody yet, nothing waiting, nobody by that name —
   because all three are the same kind of statement.

   ⚠️ CENTRED, NOT TOP-PADDED. `rd-people.jsx:105-109` is `{ flex: 1, display:
   'flex', flexDirection: 'column', justifyContent: 'center', gap: 12,
   paddingBottom: 40 }` — the sentence sits in the MIDDLE of the empty column,
   above the pinned ADD A FRIEND fill. A 28px top pad stood in for the two
   missing declarations and put it just under the chips instead.

   ⚠️ DO NOT COPY THIS TO `.rd-af__empty` (s14-add.css), which shares the old
   values and is correct as it stands: `rd-people.jsx:147` draws Add A Friend's
   no-match block as `marginTop: 16, flex: 1` with NO `justifyContent` — it is
   deliberately top-anchored under the field it answers. Two states, two
   anchors. */
.rd-fr__empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 12px;
  padding: 0 0 40px;
}

.rd-fr__empty-h {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  line-height: 1.5;
  color: var(--rd-fg2);
}

.rd-fr__empty-p {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.5;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

/* ── Search ─────────────────────────────────────────────────────────── */

.rd-fr__search { margin-bottom: 14px; }

.rd-fr__search-go { margin-top: 10px; }

.rd-fr__search-error { margin-bottom: 12px; }

/* The drawn door row: a label, a subtitle and a chevron — *"Legacy puts a
   'Scan QR code' button next to an unlabelled QR square, so neither one says
   which is which."* A real button, so the keyboard and the screen reader get a
   control, wearing a row's geometry. */
.rd-fr__door {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  min-height: 48px;
  margin-top: 12px;
  padding: 0;
  border: 0;
  border-top: 1px solid var(--rd-line);
  background: none;
  text-align: left;
  cursor: pointer;
}

.rd-fr__door-copy {
  flex: 1;
  min-width: 0;
}

.rd-fr__door-name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .06em;
  font-size: 12px;
  color: var(--rd-fg);
}

.rd-fr__door-sub {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
}

.rd-fr__chev {
  flex: none;
  font-size: 15px;
  line-height: 1;
  color: var(--rd-fg3);
}

/* ── The foot ───────────────────────────────────────────────────────── */

/* The screen's one fill, over a hairline, pinned to the bottom of the shell.
   `margin-top: auto` rather than a fixed position — a short viewport scrolls
   to it instead of overlapping the list above. */
.rd-fr__foot {
  flex: none;
  margin-top: auto;
  padding-top: 11px;
  border-top: 1px solid var(--rd-line);
}
/* ==== S14-SCAN · 14 People — Scan QR (rd-relay-15aug.jsx) · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   The scanner, `/profile/scan?:from` (`pages/scan_qr.rs`).

   DRAWING: `rd-relay-15aug.jsx` §5.2 — `ScreenScanQR` and `SQ_FATAL`. The page
   was built before it was drawn and carried raw utility classes
   (`p-4 max-w-md mx-auto`, `text-gray-500`, `text-amber-700`, `text-blue-600`),
   which paint fixed-light in a dark app. This part replaces them.

   ⚠️ THE FIFTH NAME SPACE OFF DRAWING 14, and it is disjoint from the other
   four by construction — `.rd-add*` (s14-add, the code landing), `.rd-af*`
   (s14-add, Add A Friend), `.rd-chal*`, `.rd-person*`, `.rd-fr*`, and
   `.rd-scan*` here. None declares or overrides a selector another one names, so
   their relative order is not load-bearing.

   ⚠️ THE VIEWFINDER IS THE ONE PLACE IN THIS SHEET WHERE A COLOUR IS NOT A
   THEME TOKEN, and the reason is the ground rather than a preference. Everything
   painted over the camera sits on a camera image — dark in both themes, and not
   ours to recolour. `--rd-warn` resolves to #7A4E00 on paper, which over a live
   viewfinder is ~1.6:1 and effectively invisible; `--rd-acc` resolves to the
   same deep warm brown. So the overlay takes the DARK theme's own values, stated once
   as `--rd-scan-tone` on the reticle and never repeated. Everything BELOW the
   viewfinder — the panel, its instruction line, the typed-code row, every fatal
   state — is on the app's ground and takes ordinary tokens in both themes.
   --------------------------------------------------------------------------- */
.rd-scan {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
}

/* The viewfinder. A square, because a QR is one and because a full-height feed
   would push the instruction off a short phone — the drawing's `flex: 1` sits
   inside a fixed 852px frame that a browser does not guarantee.

   ⚠️ `flex: none` IS WHAT MAKES THE SQUARE A SQUARE, and its absence was the
   defect James reported on an iPad in landscape (28 Aug): *"the camera preview
   renders lopsided — a portrait camera frame drawn into a landscape box."*

   The chain, because every link of it reads as correct on its own — and because
   IT TAKES TWO DECLARATIONS ON THIS RULE, not one.

   FIRST, THE SHRINK. This element is a flex child of `.rd-scan`, a column,
   inside `.rd-shell__content`, which is
   `flex: 1; min-height: 0; overflow-y: auto` (kit-shell.css §6) — so the
   column's height is DEFINITE and its children are shrunk to fit it rather than
   being allowed to overflow. A flex child defaults to `flex-shrink: 1`, and
   this rule never opted out.

   ⚠️ THE SHRINK ALONE WOULD NOT HAVE DONE IT, and that is the half a first
   reading of this bug misses. A flex item's `min-height` is `auto` — its
   AUTOMATIC MINIMUM SIZE, a content-based floor that TRANSFERS THROUGH
   `aspect-ratio` from the definite `width`. At 448px wide and 1/1 that floor is
   448px of height, and a box with a 448px floor does not shrink to 380 however
   short the column gets. The ratio defends itself.

   SECOND, WHAT REMOVES THE FLOOR: `overflow: hidden`, three lines below. The
   automatic minimum size applies ONLY where overflow is visible; on a scroll
   container it is zero. So the declaration that crops the feed to the box —
   which is why it was written, and which is still needed for that — is also
   what unlatches the square. ⚠️ IT IS THEREFORE NOT COSMETIC AND NOT REMOVABLE
   AS A TIDY-UP, and neither is it the thing to delete to "fix" this: without it
   the feed spills out of the box. The pair is the defect, and `flex: none` is
   the answer to the pair.

   THEN THE DEFORMATION. Once the shrink sets a definite HEIGHT and `width` is
   definite already (100% of the 448px shell column), both axes are resolved and
   `aspect-ratio` is ignored — the box silently stops being square and becomes a
   landscape slot, e.g. 448×380 on an iPad whose panel and chrome leave less
   than 448px of column.

   The camera track does not deform with it. `object-fit: cover` then scales a
   portrait 720×1280 frame to cover that slot and throws away most of its
   height: a hugely magnified horizontal band, which is exactly what "a portrait
   frame in a landscape box" looks like. LANDSCAPE IS WHERE IT BITES because
   landscape is the SHORT orientation — the same iPad in portrait has the column
   height to spare and renders the square correctly, which is why the report is
   orientation-specific.

   So the box refuses to shrink, and the region it sits in scrolls
   (`overflow-y: auto`, above) when a screen is too short to hold the square and
   the panel together. A viewfinder that is the wrong shape is a broken scanner;
   a panel one flick below the fold is not. */
.rd-scan__view {
  flex: none;
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  /* The drawing's dim ground, and it is deliberately NOT a token: this is what
     shows through before the first frame arrives, so it must match the camera's
     own dark rather than the theme's surface. */
  background: #101010;
}

/* The feed itself. It fills the box and is cropped to it — and it carries NO
   `transform` and NO `aspect-ratio` of its own, which is the second half of the
   ruling above: the browser owns the frame's orientation, this sheet owns the
   box, and a rotation here would be this file guessing at a device's sensor
   mounting. A `rotate()` added to this rule would look correct on whichever
   device it was written against and wrong on every other one. */
.rd-scan__video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* MIRRORED ONLY FOR A FRONT CAMERA, and the page decides that from the track's
   own `facingMode` rather than from a device guess — see `scan_qr.rs`'s
   `active_facing_mode`. A rear camera is what this screen asks for and what it
   assumes when the browser will not say, so this class is the exception rather
   than the default: a mirrored rear feed makes somebody move the phone the
   wrong way.

   ⚠️ THIS IS A FLIP AND NEVER A ROTATION, and it changes nothing about the
   decode: `drawImage` reads the video's own frame, not its painted CSS box, so
   the canvas the QR is read from is unmirrored either way. */
.rd-scan__video--mirror { transform: scaleX(-1); }

/* The reticle: FOUR CORNERS, NOT A BOX. *"A closed rectangle over a camera feed
   reads as a frame you must fill exactly; corners read as an area, which is what
   a QR scanner actually wants."* No animation and no scanning line — decoding is
   continuous rather than a sweep, so there is nothing to animate honestly.

   168px is the drawing's own number, clamped by a percentage so it stays inside
   a narrow viewfinder rather than overhanging it. */
.rd-scan__reticle {
  --rd-scan-tone: #FFFFFF;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(168px, 62%);
  aspect-ratio: 1 / 1;
}

/* Amber, on the dark viewfinder ground — see the banner. The value is the dark
   theme's own `--rd-warn`. */
.rd-scan__reticle--warn { --rd-scan-tone: #FFB020; }

/* `--rd-volt` and not `--rd-acc`: volt is the one accent that is the SAME value
   in both themes, which is exactly what an overlay on a fixed dark ground
   needs. It is also the literal the drawing paints here. */
.rd-scan__reticle--found { --rd-scan-tone: var(--rd-volt); }

.rd-scan__corner {
  position: absolute;
  width: 34px;
  height: 34px;
  border: 0 solid var(--rd-scan-tone);
}

.rd-scan__corner--tl {
  top: 0;
  left: 0;
  border-top-width: 2.5px;
  border-left-width: 2.5px;
}

.rd-scan__corner--tr {
  top: 0;
  right: 0;
  border-top-width: 2.5px;
  border-right-width: 2.5px;
}

.rd-scan__corner--bl {
  bottom: 0;
  left: 0;
  border-bottom-width: 2.5px;
  border-left-width: 2.5px;
}

.rd-scan__corner--br {
  bottom: 0;
  right: 0;
  border-bottom-width: 2.5px;
  border-right-width: 2.5px;
}

/* The found wash — volt at .10, the drawing's `rgba(168,153,104,.10)`. It fills
   the reticle rather than the frame: what was read is inside the corners.

   ⚠️ RE-QUOTED FOR THE TAN REBRAND, not recomputed. The drawing said
   `rgba(206,255,81,.10)` while volt was lime; export 28 made the substitution
   itself at the same site — `rd-relay-15aug.jsx:74` ("background:
   'rgba(168,153,104,.10)'") — so this is the corpus's own arithmetic quoted
   back, not ours. It stays a literal rather than becoming
   `color-mix(in srgb, var(--rd-volt) 10%, transparent)` for the reason volt is
   theme-invariant: the wash is the same in both themes because the token it
   derives from is, and a mix here would buy nothing and cost a resolve. */
.rd-scan__wash {
  position: absolute;
  inset: 0;
  background: rgba(168, 153, 104, .10);
}

/* The panel under the feed. The drawing gives it its own 18px gutter and a surf
   ground; here the shell's gutter supplies the horizontal padding, so only the
   vertical rhythm and the drawn hairline are this file's. */
.rd-scan__panel {
  flex: none;
  padding: 16px 0 20px;
  border-top: 1px solid var(--rd-line);
}

/* ONE SLOT, and that is a ruling rather than a layout note. The bad-QR sentence
   REPLACES the instruction; it is never a second banner under it. Two lines of
   guidance on a screen whose whole job is "point the phone" is one line of
   guidance and one line of noise. */
.rd-scan__hint {
  margin: 8px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  line-height: 1.45;
  color: var(--rd-fg3);
}

.rd-scan__hint--warn { color: var(--rd-warn); }
.rd-scan__hint--found { color: var(--rd-acc); }

/* ALWAYS PRESENT, NEVER A FALLBACK. *"Somebody reading a code off a phone screen
   in sunlight will type it, and a scanner that hides the manual route makes that
   person feel like they are doing it wrong."*

   44px, because this is the state people actually tap it from. Full-width and
   left-aligned so it reads as a route rather than competing with the instruction
   above it — the drawing spends a decided-here item on exactly this, having
   first drawn it as bare accent text and then rejected that for putting the
   smallest target in the corpus on the most-used escape. */
.rd-scan__typed {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 44px;
  margin-top: 8px;
  padding: 0;
  border: 0;
  background: none;
  text-align: left;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .1em;
  font-size: 10.5px;
  color: var(--rd-acc);
  cursor: pointer;
}

.rd-scan__typed:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* ── The three fatal states ────────────────────────────────────────────────
   *"Each is fatal in the sense that no retry on this screen fixes it, so each
   one names the fix elsewhere and offers the manual route."* Centred in the
   content region, per the drawing's `justifyContent: 'center'`, with the drawn
   40px of optical lift off the bottom. */
.rd-scan__fatal {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-bottom: 40px;
}

.rd-scan__fatal-p {
  margin: 12px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--rd-fg2);
  text-wrap: pretty;
}

/* The manual route is the button on EVERY fatal state, and it is the only
   control on them: *"a retry here would relaunch a camera that is not
   coming."* */
.rd-scan__fatal-act { margin-top: 20px; }

/* The decode surface. Never painted — frames are drawn into it and read back as
   luma. A class rather than an inline `style:` attribute so the page carries no
   chrome the sheet cannot see. */
.rd-scan__canvas { display: none; }
/* ==== S13-PLAN · 13 Community — Schedule A Range Day (rd-rangeday.jsx) · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   `/rangebag/new` and `/rangebag/edit/:id` (`pages/range_day_form.rs`).
   Drawn: **13 Community**, Stage Three — `ScreenPlanDay`, in its filled,
   empty-drills and offline states.

   ⚠️ THE PAIR THIS BELONGS TO IS s21-bag.css. Batch 4 re-skinned Range Bag,
   whose scheduling ROW is one of this form's three doors, and the two must
   read as one journey: the row is a destination with a chevron and no fill,
   and the screen it opens spends its one fill on CREATE RANGE DAY. The
   selectors are disjoint — `.rd-bag*` there, `.rd-plan*` here — so this
   position re-orders no existing pair.

   The shell, heading, labels, text fields, chamfer box, buttons and the
   delete confirmation are kit. What is page CSS is the four shapes the kit
   has no vocabulary for: the paired date/time inputs under one drawn label,
   the attached-drill row, the pick list, and the rounds meter's inner face.

   ⚠️ THE OFFLINE STRIP WAS A KIT GAP WEARING PAGE CSS, AND IT IS GONE. It was
   the SECOND site to need it — `s21-bag.css` said the same thing about the same
   absence — and this block's own line was the one that fired: **"Two
   independent sites is what turns an absence into a gap"**. The designer agreed
   in those terms on 2026-08-17 (*"two independent shipped sites is exactly the
   trigger the row named"*), `BannerTone::Neutral` landed, and this block and the
   bag's both kept their promise. `01 Foundations` §07 draws the tone in both
   themes. The retired rule was `rd-plan__offline` — written without its leading
   dot on purpose, for the measured reason `s21-bag.css`'s header carries: a
   dotted retired name in a comment satisfies this page's own class sweep and
   reports a deleted rule as resolved.

   Two values did not survive the move, both deliberately: `--rd-fg2` text became
   the tone's `--rd-fg` (*"the distinction is the text colour, not the border"*),
   and `text-wrap: pretty` has no kit equivalent. Do not re-declare either here —
   a page rule that repaints one tone of a kit primitive is how four callers stop
   looking like one component.
   --------------------------------------------------------------------------- */

/* `position: relative` is load-bearing, not tidiness: the add-drill sheet is
   absolutely positioned and the drawing insets it to the SCREEN's content area,
   not to the viewport. Without a positioned ancestor here the scrim would
   escape the shell and cover the tab bar. Same rule, same reason, as `.rd-cb`
   in `s07-builder.css` — the sheet below is that sheet. */
.rd-plan {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  padding-top: 4px;
}

.rd-plan__fields {
  display: grid;
  gap: 11px;
  margin-top: 14px;
}

/* When and Time are TWO drawn fields on one row, ruled 26 Aug (C-56.9.2).
   This block carried ONE `When` label over two native pickers, because the v1
   drawing mocked a single combined control. Frame 7 of the v2 drawing splits
   them — `When` beside `Time — optional`
   (`components/rd-events-lab-shooter.jsx:141-145`) — which the browser wanted
   anyway: a date picker and a time picker are two natives, and the second one
   is now the optional half and has to say so in its own label. The row is
   unchanged; each label moved down into its own column. */
/* `min-width: 0` because this is a grid item holding two flex children: a grid
   track's default `min-width: auto` refuses to shrink below its content, so
   without it the date and time inputs push the whole form wider than the
   gutter on a narrow phone rather than shrinking. */
.rd-plan__when {
  min-width: 0;
}

/* No `margin-top` any more: the 5px used to separate the row from the one
   label above it, and that gap now belongs between each label and its own
   input, below. */
.rd-plan__when-row {
  display: flex;
  gap: 8px;
}

/* One labelled field. `min-width: 0` for the same reason the wrapper has it —
   a flex item's `min-width: auto` will not shrink below a native picker's
   intrinsic width, and two of them side by side on a narrow phone is exactly
   the case that overflows. */
.rd-plan__when-field {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* `flex: none` overrides `.rd-plan__input`'s `flex: 1`, which was written for
   the input as a row item and would stretch it down a column instead. */
.rd-plan__when-field .rd-plan__input {
  flex: none;
  margin-top: 5px;
}

/* The drawn field face: `t.field` inside a `t.line` boundary.
   Native date and time inputs cannot be composed out of `RdChamferBox` — they
   are one element with browser-drawn internals — so the face is declared here
   rather than borrowed, on `RdTextField`'s own values so the three read as one
   row of controls.

   ⚠️ **SQUARE, AND `border-radius` MAY NOT COME BACK.** This rule carried
   `border-radius: 10px` until 2026-08-26. It had no source: `01
   Foundations.html:85`, §03 The Chamfer, is absolute — *"Everything else stays
   square: no border-radius anywhere in this system."* The row this face is
   trying to match is `RdTextField` (`au5.css:39-48`), which carries none, so
   the radius was also the one thing making the three controls NOT read as one
   row. The 10 was very likely a misread of the CHAMFER radius (`rd-doors.jsx:43`
   draws `r={10}`) — a bottom-right cut, which is a different geometry and one
   a browser-drawn control cannot take anyway. Only `50%` circles/pills are
   exempt from §03. */
.rd-plan__input {
  flex: 1;
  min-width: 0;
  min-height: 42px;
  padding: 10px 13px;
  border: 1px solid var(--rd-line);
  background: var(--rd-field);
  color: var(--rd-fg);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
}

.rd-plan__input:focus {
  outline: none;
  border-color: var(--rd-acc);
}

/* ── What you plan to shoot ─────────────────────────────────────────── */

.rd-plan__card {
  flex: 1;
  min-height: 0;
  margin-top: 15px;
}

/* The label and its action share a baseline, which is the drawing's own
   `alignItems: 'baseline'` — the action reads as part of the section heading
   rather than as a control floating beside it. */
.rd-plan__card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

/* "Add drill" — accent text, not a button face. Per-section rather than
   per-row, but the same argument: the screen's one fill belongs to CREATE
   RANGE DAY. */
.rd-plan__add {
  flex: none;
  border: 0;
  background: none;
  padding: 0 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-acc);
  cursor: pointer;
}

/* The empty state, and it *"says what it costs"* rather than calling the
   section optional. Also carries the pick list's own empty sentence. */
.rd-plan__none {
  margin-top: 9px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.5;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

.rd-plan__rows { margin-top: 6px; }

/* An attached drill. **52px, not 40, and `gap: 8px`, not 10** — the row grew
   to hold the stepper's two 44px targets on its right edge, exactly the
   drawn geometry (`rd-rangeday.jsx:63` ("minHeight: 52")). A hairline
   between rows and none after the last — a line under a list with nothing
   below it reads as a container that failed to close. */
.rd-plan__row {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 52px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-plan__row:last-child { border-bottom: none; }

.rd-plan__row-copy {
  flex: 1;
  min-width: 0;
}

.rd-plan__row-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Drawn as `{r} rounds each · {r * reps} total` — the per-drill product that
   used to sit in `.rd-plan__row-n` moved in here to pay for the stepper's two
   44px targets (`rd-rangeday.jsx:58` ("THE PRODUCT MOVES TO THE SUBLINE")).
   See `drill_rounds_subline`. */
.rd-plan__row-sub {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* `.rd-plan__row-n` — the row's old right-hand figure column — is gone. The
   stepper (`.rd-repstep`, kit-rep-stepper.css) took its place on the right
   edge, and the figure it showed moved into `.rd-plan__row-sub` above. */

/* ── The picker: the Course Builder's sheet, on this page ───────────── */

/* ⚠️ RULED (design reply, export 15 §5): the range-day picker TAKES THE COURSE
   BUILDER'S DRILL LIBRARY. What stood here was a 220px scroll box of
   name-and-tick rows — `rd-plan__pick-row`, `rd-plan__pick-row--on` and
   `rd-plan__pick-mark`, all three retired, written without their leading dots
   for the measured reason this file's header already carries. Every value
   below is `s07-builder.css`'s, rule for rule: the sheet is the drawn
   `ScreenCourseStages` sheet (`rd-course-builder.jsx`) and this page mounts the
   same shape rather than a third one.

   ⚠️ WHY THESE ARE `.rd-plan*` RULES AND NOT A CALL INTO `.rd-cb*`. A page that
   names another page's part is outside its own owner's reach and outside its
   own class sweep (`range_day_form.rs` reads `rd-plan` names only). The
   structure is shared; the block name is this part's. Where the two parts
   disagree in future, this one is the range day's and s07's is the builder's —
   which is the point of the split. */

.rd-plan__sheet {
  position: absolute;
  inset: 0;
  z-index: 2;
}

/* The way out. The drawing draws no close control, so the scrim IS the control
   and is a real button with a name. */
.rd-plan__scrim {
  position: absolute;
  inset: 0;
  width: 100%;
  padding: 0;
  border: 0;
  background: rgba(0, 0, 0, .72);
  cursor: pointer;
}

.rd-plan__sheet-card {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 12px;
}

.rd-plan__sheet-in {
  padding: 15px 15px 14px;
}

.rd-plan__sheet-sub {
  margin: 7px 0 10px;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  line-height: 1.45;
  text-wrap: pretty;
}

.rd-plan__search {
  padding: 8px 11px;
  margin-bottom: 8px;
}

/* ⚠️ The transparent-borderless-inside-the-box rules moved to `.rd-search__input`
   (`au5.css`) with `RdSearchField`, including the placeholder colour — with
   `opacity: 1`, which this copy was missing and which is what stops Firefox
   fading --rd-fg3 below the contrast the token was chosen for. What survives is
   the sheet's own 12.5px. */
.rd-plan__search-in {
  font-size: 12.5px;
}

.rd-plan__sheet-none {
  padding: 10px 2px 2px;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.45;
  text-wrap: pretty;
}

.rd-plan__picks {
  display: grid;
  gap: 6px;
  max-height: 244px;
  overflow-y: auto;
}

/* One pickable drill. A button, because it toggles — the mark at its left is
   one control in two states, not two affordances.

   ⚠️ **THE TINT IS THE PICKED STATE NOW, NOT THE ROW'S FILL.** `ScreenPlanPick`
   gives the row `background: on ? t.surf2 : 'transparent'` — every row used to
   carry `--rd-surf2` here, which spent the one signal the drawing reserves for
   "this one is picked" on the whole list. The fill moved to `--on` below and
   the resting row went bare; the hairline still says where a row ends. */
.rd-plan__pick {
  display: flex;
  align-items: center;
  gap: 9px;
  min-height: 42px;
  width: 100%;
  padding: 8px 11px;
  background: transparent;
  border: 1px solid var(--rd-line);
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.rd-plan__pick--on {
  background: var(--rd-surf2);
}

.rd-plan__pick:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* ⚠️ **THE PICKED MARK — 13px, 1.5px, drawn.** `ScreenPlanPick`'s row opens on
   `width: 13, height: 13, border: 1.5px solid (on ? t.acc : t.ctrl),
   background: on ? t.acc : 'transparent'`, and James ruled 27 Aug that this is
   the one add-drill picker shape — this sheet and the Pro course builder's
   picker both. It replaces the `Add` / `Added` word that sat at the right of
   this row (`rd-plan__pick-act` and `rd-plan__pick-act--on`, both retired here
   and written without their leading dots for the measured reason this file's
   header carries — a dotted retired name satisfies the page's own class sweep
   and reports a deleted rule as resolved).

   Hand-rolled rather than called, because there is no kit square-mark
   primitive to call: `kit/row.rs` carries no selected state and neither
   `RdSegmented` nor `RdChipSelector` is a list row. The two live precedents
   for the same absence are page-local for the same reason and this block is
   cut to match them — `rd-ev-pick__box` / `--on` (`adm1-pr3.css:199`, the
   admin event form's drill picker: 16px, 1.5px, accent fill when on) and
   `rd-chal-n__check` (`s14-chal.css:561`), both written without their leading
   dots because they are ANOTHER part's live names and a dotted mention here
   would offer this part a class it does not own. No token is minted: `--rd-ctrl` is
   the resting border every control in the product takes, `--rd-acc` the fill.

   Presentational only. The row's `aria-pressed` is what states the pick to a
   screen reader, so this element is empty, unlabelled and not the target.

   ⚠️ **BOTH ARMS NAME THIS RULE — there is no `rd-plan-t__pick-box`.** Every
   other pick-row shape in this part is mirrored under the `-t__` prefix, and
   that rule is a CROSS-PART one: it exists so this page never names
   `s07-builder.css`'s classes. Phone and tablet here are one page and one
   part, the mark is one 13px shape with nothing to diverge (the tablet row's
   own divergences — 54px, `gap: 12`, 12.5px name — all live on rules it still
   owns), and a `-t__` twin would be two rules that must stay identical to keep
   a ruling whose whole content is *"one shape"*. The sweep resolves it from
   either arm, because the name is this part's. */
.rd-plan__pick-box {
  flex: none;
  width: 13px;
  height: 13px;
  border: 1.5px solid var(--rd-ctrl);
  background: transparent;
}

.rd-plan__pick-box--on {
  border-color: var(--rd-acc);
  background: var(--rd-acc);
}

/* Name and meta sit on ONE line, as drawn — the name takes the slack and the
   meta is pushed to the right edge at its own size. They were a stacked pair
   inside a `rd-plan__pick-text` wrapper (retired with the word above); the
   wrapper's `flex: 1` lives on the name now, which is the element that
   actually has to give. */
.rd-plan__pick-name {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .05em;
  font-size: 11px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* `6 rds · yours` — the rounds the meter will count and who the drill belongs
   to, which is the whole of what the drawn row's second line says. */
.rd-plan__pick-sub {
  flex: none;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9px;
  text-align: right;
}

/* The sheet's last line — `ScreenPlanPick`'s *"Tap outside when you're done."*
   Quiet, `--rd-fg3`, 10.5px as drawn; the 12px is the drawn gap above it.
   Not a control and never given the look of one: the scrim is the dismissal,
   and a bordered box here would read as the commit act the drawing's own
   comment refuses.

   ⚠️ **NO `--rd-sheet-clear` HERE, AND THE 40 IS STILL EXACT.** The drawing
   ends this foot on `padding: 12px 18px 40px`, the ruled home-indicator
   clearance (James, 25 Aug). This sheet already has it, twice over and from
   the kit rather than from a number retyped here: `.rd-plan__sheet` is
   `inset: 0` inside `.rd-plan`, which sits in `.rd-shell__content` — and
   `.rd-shell__home-clear` (`kit-shell.css` §7a) ends that region
   `--rd-home-clear` (28px, or the device's real inset) above the viewport
   bottom. Add `.rd-plan__sheet-card`'s drawn `bottom: 12px` and the card's
   own bottom edge lands at exactly 40. `kit-shell.css` §0 names
   `.rd-plan__sheet-card` in the carve-out for this reason and says what to do
   if that ever changes: *"If one of them is ever re-anchored to the viewport,
   it takes the token on that day."* Taking the token now would double-count
   it — 40 of dead air under a line of grey text. */
.rd-plan__sheet-foot {
  margin-top: 12px;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.45;
  text-wrap: pretty;
}

/* ── The foot ───────────────────────────────────────────────────────── */

.rd-plan__foot {
  flex: none;
  margin-top: auto;
  padding-top: 14px;
  display: grid;
  gap: 10px;
}

/* The meter's inner face. The box is `RdChamferBox`; this is what sits inside
   it. Volt-edged once there is a number, hairline while there is none — the
   stroke is chosen in the page, because it is a state and not a style. */
.rd-plan__meter {
  padding: 11px 14px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.rd-plan__meter-copy {
  min-width: 0;
}

.rd-plan__meter-lab {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .14em;
  font-size: 9.5px;
  color: var(--rd-fg2);
}

.rd-plan__meter-sub {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

/* ⚠️ THE NUMERAL CARRIES `rd-num` IN BOTH STATES, and it is not decoration:
   the figure changes as drills are ticked, and proportional figures reflow the
   line on every change. The colour also changes at the zero boundary, so a
   reflow there reads as an event rather than as a total updating. Same
   argument `friend_code.rs`'s countdown records. */
.rd-plan__meter-n {
  flex: none;
  font-size: 26px;
  line-height: 1;
  color: var(--rd-fg3);
}

.rd-plan__meter-n--set { color: var(--rd-acc); }

.rd-plan__save-error { margin: 0; }

/* Cancel: quiet, and a real control. It returns to wherever you came from,
   which is the same route the back row uses. */
.rd-plan__cancel {
  width: 100%;
  min-height: 44px;
  border: 0;
  background: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 10px;
  color: var(--rd-fg2);
  cursor: pointer;
}

/* `DeleteEvent`'s only door for a personal range day. Furthest from the thumb,
   danger-toned and unfilled — the same placement `range_bag.rs` gives its
   reset row.

   ⚠️ **QUIET, NOT SHOUTED — re-dressed 2026-08-27.** This wore `.rd-plan__cancel`'s
   display face (uppercase, 800/78%, .1em, 10px) in the danger colour, which
   made the act that ASKS the question look like the act that PERFORMS it. The
   corpus row "Edit Mode's Delete, Stacked" (`56 (Tablet) Events Lab -
   Shooter.html`) rules the pair: *"Destruction is the red act inside the modal
   only; the act that opens it is a quiet word."* Drawn as body Archivo at 11px
   in `#FF5A3C` with `10px 0` of its own — which is `--rd-danger` exactly, so
   nothing new is declared. `min-height: 44px` stays: a quiet word is still a
   touch target.

   ONE THING FROM THE DRAWING IS NOT TAKEN: its left alignment. The drawn line
   is left-aligned because everything in that 300px column is; here the class is
   shared by both arms and sits directly under a centred `.rd-plan__cancel`, so
   a left edge would split the pair rather than match the column. The voice is
   what the ruling is about, not the edge. */
.rd-plan__danger {
  width: 100%;
  min-height: 44px;
  padding: 10px 0;
  border: 0;
  background: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 400;
  font-size: 11px;
  color: var(--rd-danger);
  cursor: pointer;
}

/* ---- tablet: the stepper's own row, and the day/meter column beside it ---- */
/* ⚠️ ADDED 2026-08-18 — `ScreenTabPlanDay`, `rd-tablet-relay.jsx:197`. Reuses
   `.rd-plan__when*`, `.rd-plan__input`, `.rd-plan__card-head`, `.rd-plan__add`,
   `.rd-plan__none`, `.rd-plan__meter*`, `.rd-plan__cancel`
   and `.rd-plan__danger` UNCHANGED — none of those is width-bound, so the
   tablet body calls them straight rather than declaring width-scoped copies.
   What follows is only what has no phone equivalent: the two-column body, and
   the drill row's tablet shape, which the phone cannot wear because it gave
   its right edge to the stepper's two 44px targets
   (`rd-tablet-relay.jsx:189`, "ON THE PHONE THE STEPPER COST SOMETHING"). */


/* The outer row — main column, then the 300px day/meter column
   (`rd-tablet-relay.jsx:201`, "flex: 1, minWidth: 0, display: 'flex',
   overflow: 'hidden'"). Direct child of `RdTabletShell`'s content region. */
.rd-plan-t__body {
  flex: 1;
  min-width: 0;
  display: flex;
  overflow: hidden;
}

/* `rd-tablet-relay.jsx:202` ("padding: '20px 26px'"). Owns its own vertical
   scroll — nothing above it does (kit-tablet-shell.css §7,
   `.rd-tshell__content` is `overflow: hidden`), the same discipline
   `kit-tablet-panes.css` states for the reason this screen does not use
   `RdPanes` at all (`panes.rs`'s own doc lists this exact 300px column as one
   of the four structural side-columns that never call it). */
.rd-plan-t__main {
  flex: 1;
  min-width: 0;
  padding: 20px 26px;
  overflow-y: auto;
  overflow-x: hidden;
  box-sizing: border-box;
}

/* `rd-tablet-relay.jsx:231` ("width: 300, flex: 'none', borderLeft"). */
.rd-plan-t__side {
  width: 300px;
  flex: none;
  border-left: 1px solid var(--rd-line);
  padding: 20px 22px;
  display: flex;
  flex-direction: column;
  gap: 18px;
  overflow-y: auto;
  overflow-x: hidden;
  box-sizing: border-box;
}

/* Pushes the meter (and the undrawn Cancel/Delete below it) to the bottom of
   the column — `rd-tablet-relay.jsx:242` ("marginTop: 'auto'"), *"The reason
   the stepper exists, in view while you use it."* `RdChamferBox` has no outer
   `class` prop (only `inner_class`, `chamfer_box.rs`), hence the wrapper. */
.rd-plan-t__meter-wrap {
  margin-top: auto;
}

/* ── The Drill Library pane, permanent, fixed 340 ────────────────────── */

/* ⚠️ `07 (Tablet) Course Builder`'s right-hand pane, taken whole (design reply,
   export 15 §5) — every value below is `.rd-cb-tab__lib*`'s, rule for rule. The
   phone's picker sheet above cites the same ruling and the same source; see its
   note for why these are `.rd-plan*` names rather than a call into s07's part.

   ⚠️ THE FIXED SIDE IS `border-left`, not `border-right` — the drawn
   `borderLeft: 1px solid t.line`, and the rule `kit-tablet-panes.css` states:
   the divider is drawn on whichever side the fixed pane's OWN edge is. This
   pane sits BETWEEN the main column and the 300px day/meter column, so both
   fixed columns carry one, and the drawn day column keeps the right edge the
   drawing gives it. */
.rd-plan-t__library {
  flex: none;
  width: 340px;
  box-sizing: border-box;
  border-left: 1px solid var(--rd-line);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.rd-plan-t__lib-head {
  flex: none;
  padding: 14px 16px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-plan-t__search {
  margin-top: 8px;
  padding: 9px 11px;
}

/* Same as the sheet's: everything but the type size is `.rd-search__input`'s
   now. The pane runs one step tighter at 12px. */
.rd-plan-t__search-in {
  font-size: 12px;
}

/* The pane owns its own scroll: `.rd-plan-t__body` is `overflow: hidden` and
   nothing above it scrolls either (kit-tablet-shell.css §7). */
.rd-plan-t__lib-rows {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
}

.rd-plan-t__lib-none {
  padding: 20px 16px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

/* Full-height rows, hairline-separated — the pane is a list, not a stack of
   cards, which is the phone sheet's shape and not this one's. */
.rd-plan-t__pick {
  appearance: none;
  width: 100%;
  min-height: 54px;
  padding: 0 16px;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  background: none;
  display: flex;
  align-items: center;
  gap: 12px;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: inherit;
}

.rd-plan-t__pick:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-plan-t__pick-text { flex: 1; min-width: 0; }

.rd-plan-t__pick-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
}

.rd-plan-t__pick-sub {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* `rd-plan-t__pick-act` and `rd-plan-t__pick-act--on` — the pane's `Add` /
   `Added` word — are retired with the phone's pair (James, 27 Aug: the mark is
   the state). Written without their leading dots, like every other retired
   name in this part, so the page's own class sweep is not satisfied by a
   deleted rule. The mark this row wears now is `.rd-plan__pick-box` above:
   ONE rule, both arms, for the reason set out there. */

/* The attached-drill row. 58px, not the phone's 52 — the drawn
   `minHeight: 58` (`rd-tablet-relay.jsx:216`) — and `gap: 16`, not 8: three
   items share the row now (copy, stepper, total) rather than two, and none of
   them is fighting the stepper's two 44px targets for the right edge the way
   the phone's row is. */
.rd-plan-t__row {
  display: flex;
  align-items: center;
  gap: 16px;
  min-height: 58px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-plan-t__row:last-child { border-bottom: none; }

.rd-plan-t__row-copy {
  flex: 1;
  min-width: 0;
}

.rd-plan-t__row-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
}

/* `{r} rounds each`, unconditionally — see `tablet_drill_rounds_each`'s own
   doc for why this is neither of the phone's two sublines. */
.rd-plan-t__row-sub {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
}

/* The total's OWN column — the phone's subline-product treatment moved back
   out to the row, in its drawn 52px width, right-aligned
   (`rd-tablet-relay.jsx:227`, "width: 52, flex: 'none', textAlign: 'right'").
   `rd-num` (kit) supplies the family and the tabular figures; this supplies
   only the drawn width, alignment and tone. */
.rd-plan-t__row-total {
  width: 52px;
  flex: none;
  text-align: right;
  font-size: 15px;
  color: var(--rd-fg2);
}
/* ==== S21-BAG · 21 Range Bag · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   `/rangebag` (`pages/range_bag.rs`). Drawn: **21 Range Bag** — `ScreenBag` in
   its packed, scrolled, empty and offline states, plus `ScreenAddItem` and
   `ScreenBagReset`.

   The shell, heading, labels, banner, buttons, text fields and the reset
   confirmation are kit. What is page CSS is the four shapes the kit has no
   vocabulary for: the packed meter, the item row and its inverted check, the
   category chip row, and the curated pool's 44-on-38 add target.

   ⚠️ ONE VOLT-FILLED ROLE PER SCREEN, AND IT BELONGS TO ADD AN ITEM. The
   drawing states the rule and then states its consequence, which is the whole
   reason the check below is what it is: *"Packed is an INVERTED fill, never
   volt … ten packed boxes in volt would take the whole page and leave the
   primary with nothing."* So the check fills with `--rd-fg` — ink — and the
   tick is cut out of it in the ground colour. The progress bar is a 3px
   hairline for the same reason: it carries the accent, but not enough of it to
   count as a filled role.

   ⚠️ THE OFFLINE STRIP WAS A KIT GAP WEARING PAGE CSS, AND IT IS GONE. The
   drawing calls for a GREY banner — *"Being on a range with no bars is the
   normal condition this product was designed for and it is not an error"* — and
   `BannerTone` had no neutral, so `rd-bag__offline` was built on `RdBanner`'s
   own base values (12px 14px, `--rd-surf`, 12.5/1.5) with a promise to delete
   itself the day the tone landed.

   The tone landed on 2026-08-17 (`01 Foundations` §07, both themes) and the
   block kept the promise: the page calls `BannerTone::Neutral` and this file
   declares nothing for the strip.

   ⚠️ THE RETIRED NAME ABOVE IS WRITTEN WITHOUT ITS LEADING DOT, ON PURPOSE, AND
   THIS PARAGRAPH MUST NOT SPELL IT EITHER. Each screen's
   `every_class_this_screen_names_resolves` sweeps its page source for
   class-shaped tokens and then looks for a dot followed by that name ANYWHERE in
   the assembled sheet — comments included. A retired class written dotted in a
   comment therefore SATISFIES that lookup, and the sweep reports a rule that no
   longer renders as resolved.

   Measured here rather than theorised, and in both directions on the same
   afternoon: this file's first draft of these notes spelled the dot and the
   bag's sweep went green over a deleted rule, while `s13-plan.css` — which had
   no such comment — failed correctly and is what surfaced the defect. A warning
   that quotes what it warns about is the same bug wearing an explanation, which
   is why the name is not repeated here.

   The one value that did NOT survive the move is this block's `--rd-fg2` text.
   The tone is `--rd-fg`, deliberately: *"the distinction is the text colour, not
   the border."* If a future reader misses the grey, that is the ruling, not a
   regression.
   --------------------------------------------------------------------------- */

.rd-bag {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  padding-top: 4px;
}

.rd-bag__loading {
  padding: 28px 0;
  display: flex;
  justify-content: center;
}

.rd-bag__banner { margin-top: var(--rd-banner-gap); }

/* ---- empty ---- */

.rd-bag__empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 13px;
  padding-bottom: 30px;
}

.rd-bag__empty-lead {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  line-height: 1.5;
  color: var(--rd-fg2);
}

.rd-bag__empty-body {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

.rd-bag__empty-acts {
  display: grid;
  gap: 8px;
  margin-top: 4px;
}

/* ---- the meter ---- */

/* Pinned under the title and OUTSIDE the scroll region: *"the one fact you
   need is the one you should never have to scroll for."* `flex: none` is what
   holds it there while `.rd-bag__list` takes the remaining height. */
.rd-bag__meter {
  flex: none;
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 11px 0 3px;
}

.rd-bag__bar {
  flex: 1;
  height: 3px;
  background: var(--rd-line);
  position: relative;
}

/* Filled by insetting the RIGHT edge, which is how the drawing does it — the
   inline `right` percentage is the only geometry on this screen a stylesheet
   cannot own, because it is the data. */
.rd-bag__bar-fill {
  position: absolute;
  inset: 0;
  background: var(--rd-acc);
}

.rd-bag__count {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 9px;
  color: var(--rd-fg2);
}

/* ---- the list ---- */

.rd-bag__list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

.rd-bag__group { margin-top: 13px; }

.rd-bag__rows { margin-top: 4px; }

.rd-bag__item { border-bottom: 1px solid var(--rd-line); }

.rd-bag__item:last-child { border-bottom: 0; }

/* 34px, one line of notes — dense enough that *"a realistic ten-item bag is two
   thumb-flicks end to end rather than a form you work down."* */
.rd-bag__row {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 34px;
  padding: 5px 0;
}

/* 55%, and the state is kept rather than cleared. A checkbox that silently
   refuses a tap is the worst version of this screen; one that visibly cannot
   be tapped is the honest one. */
.rd-bag__row--off { opacity: 0.55; }

/* The inverted check — see the header for why it is not volt. */
.rd-bag__check {
  width: 21px;
  height: 21px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1.4px solid var(--rd-fg3);
  background: var(--rd-hollow-fill);
  cursor: pointer;
  padding: 0;
}

.rd-bag__check--on {
  border-color: var(--rd-fg);
  background: var(--rd-fg);
}

/* The tick is cut out of the ink fill, so it takes the page's ground colour
   and never the accent. */
.rd-bag__check svg {
  width: 11px;
  height: 8px;
  stroke: var(--rd-bg);
}

.rd-bag__check:disabled { cursor: default; }

/* The row body is a button because it opens the edit panel — an undrawn
   affordance kept for the capability, so it is styled as text and not as a
   control. */
.rd-bag__body {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  padding: 0;
  text-align: left;
  font: inherit;
  cursor: pointer;
}

.rd-bag__name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.2;
  color: var(--rd-fg2);
}

/* Packed reads BRIGHTER, not struck through. A checklist that crosses out what
   is done makes the packed half unreadable at exactly the moment you are
   scanning it to confirm the packed half. */
.rd-bag__name--on { color: var(--rd-fg); }

/* One line, truncated — the same truncation the shipped row already applied.

   ⚠️ 10, THE DRAWN NUMBER, AND THE SAME DATUM `.rd-bag-tab__note` CARRIES.
   This shipped at 9.5 — neither the drawn 10 nor a floor — so one field had two
   treatments in one file: 9.5 here and 11 there. Both now trace to the one
   drawn 10; the tablet's 11 is that 10 raised to §05's mixed-case floor, which
   is stated for the tablet and not for the phone. */
.rd-bag__note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
  margin-top: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Undrawn and quiet: the affiliate link, reduced to a glyph so it does not
   compete with the name it sits beside. */
.rd-bag__store {
  flex: none;
  font-size: 12px;
  color: var(--rd-fg3);
  text-decoration: none;
  padding: 6px;
}

.rd-bag__store:hover { color: var(--rd-acc); }

/* ---- the edit expansion (undrawn) ---- */

.rd-bag__edit {
  display: grid;
  gap: 9px;
  padding: 11px 0 13px 32px;
}

.rd-bag__edit-acts {
  display: flex;
  gap: 8px;
}

.rd-bag__delete {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 9.5px;
  color: var(--rd-danger);
  background: transparent;
  border: 0;
  padding: 8px 0;
  cursor: pointer;
  text-align: left;
}

/* ---- scheduling ---- */

.rd-bag__sched {
  margin-top: 16px;
  padding-top: 13px;
  border-top: 1px solid var(--rd-line);
}

.rd-bag__sched-row {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  min-height: 48px;
  border: 0;
  background: transparent;
  padding: 0;
  text-align: left;
  font: inherit;
  cursor: pointer;
}

.rd-bag__sched-copy {
  flex: 1;
  min-width: 0;
}

.rd-bag__sched-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: 0.06em;
  font-size: 12.5px;
  color: var(--rd-fg);
}

/* The subtitle takes the accent only when there IS a day — an accent on
   "Nothing scheduled" would advertise the absence as news. */
.rd-bag__sched-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  margin-top: 3px;
}

.rd-bag__sched-sub--set { color: var(--rd-acc); }

.rd-bag__sched-chev {
  width: 7px;
  height: 12px;
  flex: none;
  stroke: var(--rd-fg3);
}

/* ---- reset ---- */

/* Quiet PLACEMENT — last thing on the screen, furthest from the thumb — and
   deliberately NOT quiet styling. The drawing's own correction: drawn first as
   two lines of the dimmest grey with no affordance, *"it looked like a footnote
   and read as untappable."* 44px, a real danger tone, a real hit target. */
.rd-bag__reset {
  margin-top: 4px;
  padding-top: 13px;
  border-top: 1px solid var(--rd-line);
}

.rd-bag__reset-btn {
  display: flex;
  flex-direction: column;
  justify-content: center;
  width: 100%;
  min-height: 44px;
  border: 0;
  background: transparent;
  padding: 0;
  text-align: left;
  font: inherit;
  cursor: pointer;
}

.rd-bag__reset-lead {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 10.5px;
  color: var(--rd-danger);
}

.rd-bag__reset-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
  margin-top: 4px;
}

.rd-bag__foot {
  flex: none;
  padding-top: 11px;
  padding-bottom: 11px;
}

/* ---- add (ScreenAddItem, as a panel) ---- */

.rd-bag__add {
  border-top: 1px solid var(--rd-line);
  margin-top: 13px;
  padding-top: 15px;
  padding-bottom: 12px;
}

.rd-bag__add-field { margin-top: 13px; }

.rd-bag__cats {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 7px;
}

/* A chip row rather than a dropdown, *"because there are eight of them and they
   all fit."* Not `RdChipSelector`: that primitive is a single-select over a
   fixed option list keyed by INDEX, and this row's options are computed at
   render from the user's own data — a selector whose indices move under it is
   a selector that picks the wrong thing. Same chip geometry, local state. */
.rd-bag__cat {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: 9.5px;
  color: var(--rd-fg2);
  background: var(--rd-chip);
  border: 1px solid var(--rd-line);
  padding: 7px 8px;
  cursor: pointer;
}

.rd-bag__cat--on {
  background: var(--rd-chip-on-fill);
  border-color: var(--rd-acc);
  color: var(--rd-chip-on-label);
}

.rd-bag__pool {
  margin-top: 15px;
}

.rd-bag__pool-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

.rd-bag__pool-tag {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

.rd-bag__pool-rows { margin-top: 6px; }

.rd-bag__pool-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 38px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-bag__pool-row:last-child { border-bottom: 0; }

.rd-bag__pool-copy {
  flex: 1;
  min-width: 0;
}

.rd-bag__pool-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
}

.rd-bag__pool-cat {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 8.5px;
  color: var(--rd-fg3);
  margin-top: 3px;
}

/* ⚠️ 44 ON A 38px ROW, WHICH IS THE POINT AND NOT A ROUNDING ERROR. *"The
   painted box is the row, the hit box is the platform minimum"* — the same rule
   the chip spec already uses. The button is the hit target and carries no
   paint; the chamfer box inside it is what is seen. */
.rd-bag__pool-add {
  flex: none;
  width: 44px;
  min-height: 44px;
  border: 0;
  background: transparent;
  padding: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.rd-bag__pool-add-inner {
  width: 44px;
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  font-size: 17px;
  color: var(--rd-acc);
}

.rd-bag__add-foot {
  display: grid;
  gap: 8px;
  padding-top: 10px;
}

/* ---------------------------------------------------------------------------
   §T · THE TABLET RENDERING

   Drawn: **`21 (Tablet) Range Bag`** — component `ScreenTabBag`
   (`rd-tablet-solo.jsx:26`), rows read together as *"The Whole Bag At Once"*.
   Rust: `pages/range_bag.rs`, the `DeviceClass::Tablet` arm.

   ⚠️ A SECOND BLOCK IN THIS PART, `s11.css`'s own reasoning: one screen, two
   devices, one file, one owner. `.rd-bag-tab__*` is disjoint from `.rd-bag__*`
   above — except that the reused `ScreenAddItem` panel (see the page's module
   doc) keeps the PHONE's own `.rd-bag__add*` / `.rd-bag__cat*` /
   `.rd-bag__pool*` classes verbatim, because it is the same markup rather than
   a re-dress. Its NUMBERS are not the phone's, though — see §T.5 at the foot.

   ⚠️ THE PACKED TREATMENT IS UNCHANGED — inverted fill, not volt.
   `.rd-bag-tab__check` repeats `.rd-bag__check`'s own inverted-fill idiom
   (`--rd-fg` fill when packed, the tick cut from `--rd-bg`) rather than the
   kit's default checkbox, at the drawn 20px.

   ⚠️ THE UNCHECKED BOUNDARY IS `--rd-ctrl`, WHICH IS THE ONE PLACE THIS
   REPEATS THE PHONE AND SHOULD NOT. `ScreenTabBag`'s row draws the empty box
   `1.5px solid t.ctrl`, and the palette header states the rule the token's own
   comment restates — *"line divides, ctrl bounds anything tappable"*. This
   shipped at `--rd-fg3` because §T was written as "the phone's colours"; the
   phone's drawn value really is `fg3`, so only the tablet arm was adrift.

   ⚠️ TYPE FLOORS. `01 Foundations` §05: 11px mixed-case, 9.5px tracked
   uppercase. The drawn category heading (9) and item sub-line (10) both sit
   under a floor and are raised to it.
   --------------------------------------------------------------------------- */

.rd-bag-tab {
  flex: 1;
  min-width: 0;
  padding: 18px 26px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  overflow-y: auto;
  overflow-x: hidden;
}

.rd-bag-tab__empty {
  max-width: 52ch;
  margin: 40px auto 0;
  text-align: center;
}

.rd-bag-tab__empty-title {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 900;
  /* 62%, the corpus's own role for a 900-weight heading (`.rd-h1`,
     `.rd-tshell__title`, kit-tablet-rail.css) — not the drawing's raw 68,
     which is not in the allowed stretch set (`cli/src/css.rs`). */
  font-stretch: 62%;
  letter-spacing: .01em;
  font-size: 24px;
  color: var(--rd-fg);
  line-height: 1.1;
}

.rd-bag-tab__empty-body {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg2);
  line-height: 1.6;
  margin: 12px 0 0;
}

.rd-bag-tab__empty-acts {
  display: grid;
  gap: 10px;
  max-width: 320px;
  margin: 20px auto 0;
}

/* The hairline — it does not count against the screen's one volt-filled role,
   which belongs to Add An Item / Add To Bag. No count sits beside it; the
   count is the header's own subtitle now. */
.rd-bag-tab__bar {
  flex: none;
  height: 2px;
  background: var(--rd-line);
}

.rd-bag-tab__bar-fill { height: 100%; background: var(--rd-acc); }

.rd-bag-tab__grid {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 4px 26px;
  align-content: start;
}

.rd-bag-tab__group { break-inside: avoid; }

/* Raised from the drawn 9 to the tracked floor of 9.5. */
.rd-bag-tab__group-head {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-fg3);
  padding: 10px 0 6px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-bag-tab__item { border-bottom: 1px solid var(--rd-line); }

.rd-bag-tab__item:last-child { border-bottom: 0; }

.rd-bag-tab__row {
  min-height: 48px;
  display: flex;
  align-items: center;
  gap: 13px;
}

.rd-bag-tab__row--off { opacity: 0.55; }

.rd-bag-tab__check {
  width: 20px;
  height: 20px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  /* `ctrl`, not `fg3` — the box is the whole affordance. See §T's header. */
  border: 1.5px solid var(--rd-ctrl);
  background: var(--rd-hollow-fill);
  cursor: pointer;
  padding: 0;
}

.rd-bag-tab__check--on { border-color: var(--rd-fg); background: var(--rd-fg); }

.rd-bag-tab__check svg { width: 11px; height: 8px; stroke: var(--rd-bg); }

.rd-bag-tab__check:disabled { cursor: default; }

.rd-bag-tab__body {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  padding: 0;
  text-align: left;
  font: inherit;
  cursor: pointer;
}

.rd-bag-tab__name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
}

.rd-bag-tab__name--on { color: var(--rd-fg3); }

/* Raised from the drawn 10 to the mixed-case floor of 11. */
.rd-bag-tab__note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

.rd-bag-tab__store {
  flex: none;
  font-size: 12px;
  color: var(--rd-fg3);
  text-decoration: none;
  padding: 6px;
}

.rd-bag-tab__store:hover { color: var(--rd-acc); }

.rd-bag-tab__edit {
  display: grid;
  gap: 9px;
  padding: 9px 0 13px 33px;
}

.rd-bag-tab__edit-acts { display: flex; gap: 8px; }

.rd-bag-tab__delete {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 9.5px;
  color: var(--rd-danger);
  background: transparent;
  border: 0;
  padding: 8px 0;
  cursor: pointer;
  text-align: left;
}

.rd-bag-tab__foot { flex: none; padding-top: 4px; }

/* Quiet danger text, never a button — the drawn foot. */
.rd-bag-tab__unpack {
  background: none;
  border: 0;
  padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-danger);
  cursor: pointer;
}

/* The header's own "+ Add Item" / "Cancel" toggle — the shell's `action`
   geometry is its own (`kit-tablet-shell.css`); this only supplies the word's
   type, matching the shell's other bare-word actions. */
.rd-bag-tab__add-act {
  appearance: none;
  width: 100%;
  height: 100%;
  border: 0;
  background: none;
  font: inherit;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 11px;
  color: var(--rd-acc);
  cursor: pointer;
}

/* ── §T.5 · the reused `ScreenAddItem` panel, at ×1.25 ────────────────────
   ⚠️ THE ONLY PHONE COMPONENT THIS SCREEN REUSES, AND THE READING RULE SAYS
   WHAT ITS NUMBERS MEAN. `01 Foundations` §05: *"A phone component reused
   inside a tablet frame keeps its 312-scale numbers in the drawing and builds
   at ×1.25."* The panel above is the phone's markup verbatim, so the drawing
   for it is the phone's drawing and every number in it is a 312-scale number.
   It shipped unscaled, which is the one case §05 exists to name — and
   `s15-tablet.css`'s `DateBlock` block is the same rule already applied to the
   other reused phone component in this app.

   The rounding follows §05's own worked example, which reads a drawn 9.5 as
   "a legal 12 on the tablet":

     add       margin-top 13 -> 16    padding-top 15 -> 19   padding-bottom 12 -> 15
     field     margin-top 13 -> 16
     chips     gap 6 -> 7.5           margin-top 7 -> 9
     chip      9.5 -> 12              padding 7/8 -> 9/10
     pool      margin-top 15 -> 19    rows margin-top 6 -> 7.5
     pool tag  9.5 -> 12
     pool row  min-height 38 -> 47.5  gap 10 -> 12.5
     pool name 12.5 -> 15.5
     pool cat  8.5 -> 10.5            margin-top 3 -> 4
     pool +    17 -> 21
     foot      gap 8 -> 10            padding-top 10 -> 12.5

   ⚠️ AND IT IS WHAT PUT TWO VALUES UNDER THE TABLET TYPE FLOORS. §T's header
   states them — 11px mixed-case, 9.5px tracked uppercase — and raises the
   `.rd-bag-tab__*` values to them, but the reused panel was never walked: it
   printed `.rd-bag__pool-cat` at 8.5 tracked uppercase, under the floor
   outright, and `.rd-bag__cat` at an unscaled 9.5. The ×1.25 above clears both
   (10.5 and 12) without a floor having to be spent separately.

   ⚠️ 44 DOES NOT SCALE. `.rd-bag__pool-add` is the platform minimum hit box,
   not a drawn number — the comment on the phone's own rule makes that the
   point. A 55px tap target is not a bigger promise than a 44px one.

   ⚠️ THE MODIFIER, NOT AN `@media`. The device is resolved in Rust by
   `use_device_class()` and the tablet arm emits `rd-bag__add--tab` — the same
   `--tab` idiom `s55-sns.css` §T and `s10-par.css` §T use, and the reason this
   part still carries no width query. */

.rd-bag__add--tab {
  margin-top: 16px;
  padding-top: 19px;
  padding-bottom: 15px;
}

.rd-bag__add--tab .rd-bag__add-field { margin-top: 16px; }

.rd-bag__add--tab .rd-bag__cats {
  gap: 7.5px;
  margin-top: 9px;
}

.rd-bag__add--tab .rd-bag__cat {
  font-size: 12px;
  padding: 9px 10px;
}

.rd-bag__add--tab .rd-bag__pool { margin-top: 19px; }

.rd-bag__add--tab .rd-bag__pool-tag { font-size: 12px; }

.rd-bag__add--tab .rd-bag__pool-rows { margin-top: 7.5px; }

.rd-bag__add--tab .rd-bag__pool-row {
  gap: 12.5px;
  min-height: 47.5px;
}

.rd-bag__add--tab .rd-bag__pool-name { font-size: 15.5px; }

.rd-bag__add--tab .rd-bag__pool-cat {
  font-size: 10.5px;
  margin-top: 4px;
}

.rd-bag__add--tab .rd-bag__pool-add-inner { font-size: 21px; }

.rd-bag__add--tab .rd-bag__add-foot {
  gap: 10px;
  padding-top: 12.5px;
}
/* ==== S04-AUTHOR · 04 Authoring — the shooter's drill form · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   `/training/drills/new`, `/training/drills/:id/edit`,
   `/training/drills/queued/:op_key` (`pages/drill_form.rs`) and
   `/training/drills/:id/variant` (`pages/drill_variant.rs`). Drawn:
   **04 Authoring** — `ScreenName`, `ScreenSteps`, `ScreenTargets`, and, at the
   foot of this file, `ScreenEditGate` (row 07) and `ScreenVariant` (row 09).

   THREE BLOCKS, ONE FILE, and they are name-disjoint: `.rd-af*` is the form,
   `.rd-eg*` is the gate that fires from inside it, `.rd-av*` is the variant
   screen it routes to. One drawing, one owner, one lane — the same argument
   `s15.css`/`s15-stand.css` split on, resolved the other way because the gate
   is markup INSIDE the form's own page and separating it would put two files
   on one component.

   ⚠️ NOT TO BE CONFUSED WITH `adm-drillf.css`, which is ADM-DRILLF — the
   ADMIN drill builder, the power tool, owner lane4. Two forms author drills in
   this product and they are different surfaces for different people. The
   selectors are disjoint (`.rd-drillf*` there, `.rd-af*` here) and neither
   overrides the other.

   The shell, heading, labels, text fields, banners, buttons, stats, chamfer
   boxes and the two confirmations are kit. What is page CSS is the five shapes
   the kit has no vocabulary for: the derived block's three-column grid, the
   step card with its index gutter and two measurement fields, the target chip
   row, the scoring summary row, and the three audience cards.

   ⚠️ THE DRAWING IS A THREE-STEP WIZARD AND THIS IS ONE COLUMN. The reasoning
   is in the component's module doc and is not repeated here; what it means for
   this sheet is that there is no step-counter rule, no per-screen `back`, and
   `.rd-af__section` carries the rhythm that the drawn screen boundaries carried.

   ⚠️ ONE VOLT-FILLED ROLE, AND IT IS THE PRIMARY SAVE. So `.rd-af__chip--on`
   takes the chip-on tokens rather than a volt fill of its own, the selected
   audience card is an accent STROKE and an accent tag, ADD ANOTHER STEP is
   `Kind::Neutral`, and the scoring row's action word is accent TEXT. A form
   this long with three volt things on it has no primary at all.

   (That role's WORD now moves with the audience — `Share With Friends` when
   friends is selected, per the 2 Sep design reply. One volt thing either way.)
   --------------------------------------------------------------------------- */

.rd-af {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  padding-top: 4px;
}

.rd-af__loading {
  padding: 40px 0;
  display: flex;
  justify-content: center;
}

.rd-af__eyebrow { margin-bottom: 9px; }

.rd-af__lede { margin: 13px 0 0; }

.rd-af__banner { margin-top: var(--rd-banner-gap); }

/* The rhythm the drawn screen boundaries used to carry. */
.rd-af__section { margin-top: 20px; }

.rd-af__field { margin-top: 16px; }

.rd-af__sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  margin: 7px 0 9px;
}

.rd-af__labelrow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 6px;
}

.rd-af__optional {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* The quiet explanatory line under a control. Used five times and always for
   the same job: a fact the author will not guess, set at the smallest legible
   size so it does not compete with the field it explains. */
.rd-af__note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.45;
  color: var(--rd-fg3);
  margin: 8px 2px 0;
}

/* The two dashboard guards. Not a warning tone: a dashboard-authored drill with
   named targets or a custom profile is a legitimate drill being edited by the
   simpler of two forms, and the sentence is a statement about what saving will
   leave alone. A warning rule beside a true statement makes the statement look
   like a risk. */
.rd-af__locked {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.45;
  color: var(--rd-fg2);
  background: var(--rd-surf);
  border-left: 2px solid var(--rd-line);
  padding: 9px 11px;
  margin-top: 8px;
}

/* ---- steps ---- */

.rd-af__nosteps {
  padding: 20px 16px;
  text-align: center;
}

.rd-af__nosteps-lead {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 10px;
  color: var(--rd-fg2);
}

.rd-af__nosteps-body {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.45;
  color: var(--rd-fg3);
  margin-top: 7px;
}

.rd-af__steps {
  display: grid;
  gap: 7px;
}

/* Full-width, never grey: *"Add-step is a full-width button, not a floating
   lime square in the corner with nothing to anchor it."* */
.rd-af__addstep { margin-top: 9px; }

/* The drawn `AStep` card: an index gutter, a body, and the controls. */
.rd-af__step {
  padding: 10px 12px;
  display: flex;
  gap: 10px;
  align-items: flex-start;
}

.rd-af__step-n {
  flex: none;
  font-size: 10px;
  font-weight: 400;
  color: var(--rd-fg3);
  padding-top: 5px;
  width: 12px;
}

.rd-af__step-body {
  flex: 1;
  min-width: 0;
}

.rd-af__step-measures {
  display: flex;
  gap: 8px;
  margin-bottom: 6px;
}

/* Chip-sized, because in the drawn read-only card these two ARE chips. The
   label above each is what a chip does not need and a field does — without it
   two bare numeric boxes side by side say nothing about which is which. */
.rd-af__step-measure {
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.rd-af__step-measure-label {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 8px;
  color: var(--rd-fg3);
}

.rd-af__step-input {
  width: 62px;
  font-size: 12.5px;
  font-weight: 400;
  background: var(--rd-field);
  border: 1px solid var(--rd-line);
  color: var(--rd-fg);
  padding: 5px 7px;
}

.rd-af__step-text {
  width: 100%;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.35;
  color: var(--rd-fg2);
  background: var(--rd-field);
  border: 1px solid var(--rd-line);
  padding: 7px 9px;
}

.rd-af__step-acts {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.rd-af__step-act {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  line-height: 1;
  color: var(--rd-fg3);
  background: transparent;
  border: 0;
  padding: 5px 4px;
  cursor: pointer;
}

.rd-af__step-act:disabled {
  opacity: 0.35;
  cursor: default;
}

.rd-af__step-act--rm { color: var(--rd-danger); }

/* ---- the derived block ---- */

/* Recessed, not surface: it is the one thing on the form the author does NOT
   fill in, and sitting a step back from the fields is how that reads without a
   caption having to say it twice. */
.rd-af__derived { margin-top: 16px; }

.rd-af__derived-inner {
  padding: 11px 13px;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 10px;
}

.rd-af__derived-note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.45;
  color: var(--rd-fg3);
  text-align: center;
  margin-top: 9px;
}

/* ---- targets, scoring, unit ---- */

.rd-af__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 7px 0 0;
}

/* ⚠️ **TWO LAYERS AND THE 13px CONTROL CUT** — the drill form's chips are chips,
   and they drew a square box like every other stroked chip on the console before
   the 27 Aug sweep. `01 Foundations` §03 (re-ruled 26 Aug 2026): 13 on controls.
   `48 Kit Rules` — *"a stroked chamfer must be two layers"* — is why the stroke
   moves off `border` and onto the outer layer's fill; a `border` under a
   `clip-path` renders the diagonal bare. Full derivation in
   `kit-pro-filter-bar.css` §3.

   ⚠️ **THE FILLED `--on` IS WHY THE FACE IS NOT `--rd-hollow-fill` HERE.** Every
   other converted chip is hollow in both states; this one is `--rd-chip` at rest
   and `--rd-chip-on-fill` lit (the block banner: one volt-filled role and it is
   SAVE DRILL, so `--on` takes the chip-on tokens rather than a volt fill of its
   own). The FILL therefore rides the SURFACE layer and the stroke rides the
   outer — which is exactly the split the two-layer construction exists to make
   possible. */
.rd-af__chip {
  position: relative;
  isolation: isolate;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: 10px;
  color: var(--rd-fg2);
  border: 0;
  background: var(--rd-line);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--rd-chamfer-control)), calc(100% - var(--rd-chamfer-control)) 100%, 0 100%);
  min-width: 34px;
  min-height: 30px;
  /* The drawn `6px 9px` plus the 1px the border used to occupy. */
  padding: 7px 10px;
  cursor: pointer;
}

/* The surface, inset by the stroke and cut at `inner_chamfer_px(13, 1)`. */
.rd-af__chip::before {
  content: "";
  position: absolute;
  inset: 1px;
  z-index: -1;
  background: var(--rd-chip);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 12.41px), calc(100% - 12.41px) 100%, 0 100%);
}

.rd-af__chip--on {
  background: var(--rd-acc);
  color: var(--rd-chip-on-label);
}

.rd-af__chip--on::before {
  background: var(--rd-chip-on-fill);
}

/* An inset ring on the SURFACE layer. This chip had NO focus rule at all and got
   the browser default, which `clip-path` would now clip away to nothing — so the
   ring is not a restyle, it is the thing that keeps the control reachable. */
.rd-af__chip:focus-visible {
  outline: none;
}

.rd-af__chip:focus-visible::before {
  box-shadow: inset 0 0 0 2px var(--rd-acc);
}

/* The numeric escape beside the chips. Deliberately the quietest control in the
   section — the chips are the answer for nearly everyone, and this is for the
   drill that wants seven targets. Its presence is not optional (the wire allows
   1–20 and the chips are six); its prominence is. */
.rd-af__other {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-top: 9px;
}

.rd-af__other-label {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

.rd-af__other-input {
  width: 60px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--rd-fg);
  background: var(--rd-field);
  border: 1px solid var(--rd-line);
  padding: 5px 7px;
}

.rd-af__scoring {
  display: block;
  width: 100%;
  border: 0;
  background: transparent;
  padding: 0;
  margin-top: 14px;
  text-align: left;
  font: inherit;
  cursor: pointer;
}

.rd-af__scoring-inner {
  padding: 12px 13px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 52px;
}

.rd-af__scoring-copy {
  flex: 1;
  min-width: 0;
}

.rd-af__scoring-head {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 10px;
  color: var(--rd-fg);
}

/* The five values, always visible — *"Most authors never change them and should
   not have to open anything to confirm that."* `rd-num` supplies the tabular
   figures so `A 5 · C 3 · D 1` does not shift as a value is edited. */
.rd-af__scoring-values {
  font-size: 10.5px;
  font-weight: 400;
  color: var(--rd-fg3);
  margin-top: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Accent TEXT, not a filled control — see the header on the one-volt rule. */
.rd-af__scoring-act {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 8.5px;
  color: var(--rd-acc);
}

.rd-af__zones {
  display: grid;
  gap: 5px;
  margin-top: 8px;
}

.rd-af__zone {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Letter and full name together, *"so A and Alpha are visibly the same thing —
   the app currently uses each in a different place."* Fixed width on the key so
   the five names start on one edge. */
.rd-af__zone-key {
  width: 22px;
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 10px;
  color: var(--rd-fg3);
}

.rd-af__zone-name {
  flex: 1;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg2);
}

.rd-af__zone-pen {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 8px;
  color: var(--rd-danger);
}

.rd-af__zone-boxinner {
  width: 62px;
  padding: 0;
}

/* Right-aligned, as drawn: a column of numbers reads as a column when the
   digits line up on their last place and not their first. */
.rd-af__zone-input {
  width: 100%;
  font-size: 12.5px;
  font-weight: 400;
  color: var(--rd-fg);
  background: transparent;
  border: 0;
  padding: 6px 9px;
  text-align: right;
}

/* ---- the textarea and the select, inside chamfer boxes ---- */

.rd-af__area-inner {
  padding: 0;
  min-height: 50px;
}

.rd-af__area {
  width: 100%;
  border: 0;
  background: transparent;
  color: var(--rd-fg2);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.4;
  padding: 10px 11px;
  resize: vertical;
}

.rd-af__select-inner {
  padding: 0;
  min-height: 44px;
  display: flex;
  align-items: center;
  margin-top: 6px;
}

.rd-af__select {
  width: 100%;
  border: 0;
  background: transparent;
  color: var(--rd-fg);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  padding: 10px 12px;
  cursor: pointer;
}

/* The Dry Fire rule: a dry fire drill is a Drill, so this picker opens on that
   option and is disabled beside it. Same borrowed treatment as the dashboard
   form's two locked fields (`adm-drillf.css`) — the dimmest foreground token and
   `cursor: default`, because the design corpus draws no disabled control
   anywhere and rules against greying one in three places. The chamfer box keeps
   its own ground: the statement being made is about the CONTROL, not the field.
   Flagged in the PR for James. */
.rd-af__select:disabled {
  color: var(--rd-fg3);
  cursor: default;
}

/* ---- who can see it ---- */

/* ⚠️ **`.rd-af__share` AND `.rd-af__check` ARE GONE, AND THAT IS THE POINT OF
   THIS BLOCK.** They dressed a single checkbox — "Share to community" — which
   was the only control a two-value wire could carry. The wire grew a third
   tier and the designer drew the replacement on 2 Sep 2026: OPTION CARDS,
   explicitly not the kit's segmented control, because *"each state carries a
   consequence that has to be readable before it is chosen, and a segment label
   cannot say who your friends are."* The classes could not be left behind as
   dead rules either — `rangeday check css` resolves class literals in rsx
   against this sheet, and a rule nothing names is a rule nobody deletes.

   Drawn: `ScreenShare` (04 Mobile row 07) and `ScreenTabShare` (04 Tablet row
   03). ONE markup at both widths; `--wide` is the whole difference between
   them, because the tablet frame changes the LAYOUT and nothing else —
   *"the width makes them a side-by-side comparison, the same grammar as the
   diagram routes."*

   ⚠️ CHAMFER 10, NOT RADIUS 10, AND IT IS NOT DECLARED HERE. The drawn box is
   `TOut … r={10}`, the corpus's stroked-chamfer primitive, so the cut and the
   stroke are both `RdChamferBox` props at the call site — the same
   construction `.rd-eg__opt` in this sheet already carries, and for the same
   reason recorded there: a CSS border under a `clip-path` renders the diagonal
   bare, which would erase the accent stroke that IS the selection. */

.rd-af__vis-lede {
  margin: 10px 0 14px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--rd-fg2);
}

.rd-af__vis {
  display: grid;
  gap: 8px;
}

/* *"three cards side by side"* — the tablet's one change. Wraps rather than
   crushing: three 1fr columns of a narrow pane would set the sub-lines two
   words wide, and the sub-line is the entire argument for cards over segments. */
.rd-af__vis--wide {
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 14px;
  align-items: stretch;
}

/* The `<button>` keeps the hit box and the focus ring and paints nothing — the
   stroke and fill are the chamfer box's two layers, the padding rides the
   inner face. Same reason as `.rd-eg__opt`: a `<button>` cannot nest. */
.rd-af__vis-opt {
  width: 100%;
  text-align: left;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

/* `clip-path` clips an `outline`, so the ring stays on the unclipped
   `<button>`, outside the box. */
.rd-af__vis-opt:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

.rd-af__vis-face {
  padding: 12px 13px;
  height: 100%;
}

.rd-af__vis-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

.rd-af__vis-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .08em;
  font-size: 10.5px;
  color: var(--rd-fg);
}

/* ⚠️ `var(--rd-acc)`, NOT `var(--rd-accent)`. The latter is declared nowhere in
   this app and `.rd-eg__opt-tag` in this same sheet still reads it — flagged
   there, not swept, and deliberately not copied into a new rule. */
.rd-af__vis-tag {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .1em;
  font-size: 8.5px;
  color: var(--rd-acc);
  flex: none;
}

/* The consequence, and the reason this is a card. Never clamped: a sub-line cut
   off at one line is a segmented control with extra steps. */
.rd-af__vis-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
  margin-top: 5px;
}

/* The tablet frame sets the same three strings larger — 13.5 / 11.5 against the
   phone's 10.5 / 10.5 — because the pane is wide, not because the words differ. */
.rd-af__vis--wide .rd-af__vis-name {
  font-size: 13.5px;
  letter-spacing: .02em;
  font-stretch: 76%;
}

.rd-af__vis--wide .rd-af__vis-sub {
  font-size: 11.5px;
  line-height: 1.5;
}

/* Under the cards on both frames. This is the sentence that makes stepping back
   DOWN a thing an author will try, so it is quiet but it is not optional. */
.rd-af__vis-caveat {
  margin: 10px 2px 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.45;
  color: var(--rd-fg3);
  max-width: 68ch;
}

/* ---- the foot ---- */

.rd-af__foot {
  margin-top: 24px;
  display: grid;
  gap: 9px;
}

/* ⚠️ ABOVE the button, not below it, and the drawing says why: *"Below the last
   button is where the home indicator lives, and a caption there gets struck
   through by it."* The shipped form put this line underneath. */
.rd-af__foot-note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.45;
  color: var(--rd-fg3);
  text-align: center;
}

.rd-af__diagram {
  margin-top: 22px;
  padding-top: 16px;
  border-top: 1px solid var(--rd-line);
}

/* Quiet placement, real styling — the same correction `s21-bag.css` records for
   the range bag's reset. Last thing on the form, danger tone, 44px target. */
.rd-af__danger {
  margin-top: 20px;
  padding-top: 14px;
  padding-bottom: 12px;
  border-top: 1px solid var(--rd-line);
}

.rd-af__danger-btn {
  width: 100%;
  min-height: 44px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 10.5px;
  color: var(--rd-danger);
  background: transparent;
  border: 0;
  padding: 0;
  text-align: left;
  cursor: pointer;
}

/* ==========================================================================
   ROW 07 — `ScreenEditGate`, the drill that has been shot                 */

/* ⚠️ NOT `RdConfirmModal`, AND THE DIFFERENCE IS THE POINT. That kit component
   is a heading, a body and two buttons. The drawn card is a heading, a body,
   TWO DESCRIBED OPTIONS — one of them marked Recommended — and a third way out
   at the foot. *"Three ways out, and the screen says which it prefers."* Folded
   into a two-button confirm, the option the drawing prefers is the one that
   disappears.

   What IS borrowed, deliberately, is the scrim idiom: `position: absolute;
   inset: 0` over the nearest positioned ancestor, so a page that hosts this
   needs `position: relative` for the same reason `adm-plat.css` records. The
   shooter shell already provides it.

   The scrim is drawn at .74 rather than the confirm's .82 — the form behind is
   supposed to stay READABLE. Row 07's drawn frame dims the targets-and-scoring
   panel to 30% and leaves it there, because what is being decided about is the
   edit you can still see. */
.rd-eg {
  position: absolute;
  inset: 0;
  z-index: 40;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 16px;
}

.rd-eg__scrim {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, .74);
}

.rd-eg__card {
  position: relative;
  width: 100%;
  max-width: 420px;
  padding: 16px 15px 14px;
  display: grid;
  gap: 0;
}

.rd-eg__body {
  margin: 11px 0 14px;
}

.rd-eg__opts {
  display: grid;
  gap: 7px;
}

/* One option per box, and the boxes are buttons because they are the choice.
   `text-align: left` because they carry prose, not a label.

   ⚠️ CHAMFER 9, NOT RADIUS 9 — `48 Kit Rules.html:33`, 21 Aug 2026: *"The kit
   is chamfered; radius is not a style option … A new rounded control is a
   defect, not a variant."* Nothing on 48's round list (circular avatars, sweep
   and skeleton arcs, OS-owned controls, device frames, map pins) covers an
   option box. The cut keeps the 9 the radius held.

   ⚠️ AND IT ROUTES THROUGH `RdChamferBox`: *"Every stroked chamfer routes
   through TOut (a CSS border under a clip-path renders the diagonal bare —
   recurred three times)."* These boxes are stroked at rest and the Recommended
   one is stroked HARDER — the stroke is how the recommendation is said — so a
   bare diagonal would erase the one difference between the two options.
   Construction: the `<button>` keeps the hit box, the focus ring and the
   disabled state and paints nothing; the border is the box's outer layer, the
   `--rd-surf` fill its inner, and the padding rides `__opt-face`, which is that
   inner. Same reason as `s47.css:41` — a `<button>` cannot nest. */
.rd-eg__opt {
  width: 100%;
  text-align: left;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

/* `clip-path` clips an `outline`, so the ring stays on the unclipped
   `<button>`, outside the box. */
.rd-eg__opt:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

.rd-eg__opt-face {
  padding: 10px 12px;
}

/* ⚠️ THE ACCENT IS A RECOMMENDATION, NOT A PRIMARY. The drawn Recommended
   option takes an accent STROKE and an accent tag; the volt fill on this screen
   belongs to nothing, because the screen's own preferred answer is a link to
   another surface and its foot button is Neutral. Same reasoning this sheet's
   banner already records: a card with three volt things on it has no primary. */
/* ⚠️ `.rd-eg__opt--rec` IS GONE FROM THIS SHEET AND FROM THE MARKUP, and it is
   not a lost distinction — it MOVED. Both halves of what the modifier said are
   now `RdChamferBox` props at the call site (`Stroke::Accent`, and the heavier
   border), because after the chamfer move neither one is a property of this
   element any more: the stroke is painted by the box's OUTER layer, which page
   CSS does not name. It could not stay as a dead class either — `rangeday check
   css` requires every class literal in rsx to resolve in this sheet, so a
   marker with no rule is a red check, not a comment.

   Two silent corrections rode along, both forced by the move:
     · `var(--rd-accent)` WAS UNDEFINED — no `--rd-accent:` declaration exists
       anywhere in this app. The recommendation's stroke was falling back to the
       initial `border-color`, i.e. `currentColor`, so it was never the accent it
       claimed. `Stroke::Accent` resolves `--rd-acc`, the token that exists.
       ⚠️ `.rd-eg__opt-tag` below still reads `var(--rd-accent)` and is NOT
       touched here — same defect, different element, and it is flagged rather
       than swept because it is not this item's.
     · 1.4px → 1.5px, per the architect's collapse of 1.4 and 1.5 to one border
       width (`chamfer_box.rs`, `RdChamferBoxProps::border`). */


.rd-eg__opt:disabled {
  opacity: .55;
  cursor: default;
}

.rd-eg__opt-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

/* The in-flight mark rides INSIDE the name, beside the unchanged word, at the
   drawn 10px gap (`rd-loading.jsx:197`) — not as a sibling of the name in the
   head row, where `justify-content: space-between` would fling it to the far
   edge into the seat the Recommended tag holds on the other option. */
.rd-eg__opt-name {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .08em;
  font-size: 10px;
  color: var(--rd-fg);
}

.rd-eg__opt-tag {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .1em;
  font-size: 8.5px;
  color: var(--rd-accent);
}

.rd-eg__opt-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
  margin-top: 4px;
}

.rd-eg__foot {
  margin-top: 11px;
}

/* ==========================================================================
   ROW 09 — `ScreenVariant`, same drill, new distance                      */

.rd-av {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  padding-top: 4px;
}

.rd-av__loading {
  padding: 40px 0;
  display: flex;
  justify-content: center;
}

.rd-av__banner {
  margin: var(--rd-banner-gap) 0 0;
}

.rd-av__lede {
  margin: 12px 0 15px;
}

.rd-av__field {
  margin-bottom: 10px;
}

/* ⚠️ OLD AND NEW SHARE ONE STRUCTURE, AND THAT IS THE DRAWING'S INSTRUCTION:
   *"Old value and new value are the comparison this screen exists for, so they
   share one structure: same label row, same box, same sub-caption baseline. The
   old one is quieter, not smaller."* So both columns get the same box height
   and the same caption rhythm; only the stroke, the fill and the ink differ. */
.rd-av__dist {
  display: flex;
  gap: 9px;
  align-items: flex-start;
  margin-top: 10px;
}

.rd-av__dist-new,
.rd-av__dist-was {
  flex: 1;
  min-width: 0;
}

.rd-av__dist-inner {
  padding: 9px 11px;
  min-height: 46px;
  display: flex;
  align-items: center;
}

.rd-av__dist-input {
  width: 100%;
  border: 0;
  background: transparent;
  color: var(--rd-fg);
  font-size: 16px;
  padding: 0;
  outline: none;
  -moz-appearance: textfield;
  appearance: textfield;
}

.rd-av__dist-input::-webkit-outer-spin-button,
.rd-av__dist-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* Quieter, not smaller: same 16px as the live field, at fg3. */
.rd-av__dist-old {
  font-size: 16px;
  color: var(--rd-fg3);
}

.rd-av__dist-cap {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.45;
  color: var(--rd-fg3);
  margin-top: 5px;
}

/* The withheld-control note (a multi-distance source). Same tone as the form's
   two dashboard guards: a fact about the drill, not a warning about the author. */
.rd-av__note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.45;
  color: var(--rd-fg3);
  margin: 10px 2px 0;
}

.rd-av__carry {
  padding: 11px 13px;
  display: grid;
  gap: 6px;
  margin-top: 12px;
}

.rd-av__carry-row {
  display: flex;
  justify-content: space-between;
  gap: 10px;
}

.rd-av__carry-row + .rd-av__carry-row {
  padding-top: 6px;
  border-top: 1px solid var(--rd-line);
}

.rd-av__carry-what {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg2);
}

.rd-av__carry-fate {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg);
  text-align: right;
}

/* *"Your scores — start fresh"* is the one row that is not a carry, so it reads
   at the label's weight rather than the value's. Drawn that way. */
.rd-av__carry-fate--quiet {
  color: var(--rd-fg2);
}

.rd-av__lineage {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.45;
  color: var(--rd-fg3);
  margin: 9px 2px 0;
}

.rd-av__foot {
  margin-top: auto;
  padding-top: 20px;
  padding-bottom: 12px;
}

/* ==========================================================================
   §T-author · THE TABLET RENDERING — the wizard becomes a navigable sequence
   ==========================================================================

   Drawn: **`04 (Tablet) Authoring`**, rows "Steps In A Rail" and "Diagram,
   Then Review" — component `ScreenTabAuthor`. Rust: `pages/drill_form.rs`,
   the `DeviceClass::Tablet` arm.

   A FOURTH BLOCK IN THIS FILE, on the same footing as the three its header
   already names: one drawing, one owner, one lane, and `.rd-af-tab*` is
   name-disjoint from `.rd-af*`, `.rd-eg*` and `.rd-av*`. (`rd-af__` and
   `rd-af-tab__` are two prefixes and not one — nothing here is reached by a
   selector written for the phone column, and nothing here reaches one.)

   ⚠️ THE PHONE COLUMN'S CLASSES ARE BORROWED WHOLE, NOT REDECLARED. This is
   the whole claim of the arm: same machinery, new regions. The step cards, the
   zone rows, the chips, the scoring summary, the derived block, the share
   toggle, the notes and the two locked lines are the SAME markup and the same
   rules as the phone renders — every one of them is a context-free rule with
   no `.rd-af` ancestor in its selector, so a re-layout can mount them anywhere.
   Four rules below reach into two of them (§4, §5) to make the drawn column
   changes, and nothing else here declares or overrides a `.rd-af__*` selector.

   ⚠️ WHY THIS FILE AND NOT `train.css`. The five earlier tablet sections in
   this arc live in `train.css` (drawings 03-08) because their phone halves sit
   in other lanes' parts. This drawing's phone half is HERE, and the tablet arm
   borrows a dozen of its classes — so filing the two halves apart would put one
   component's rules in two owners' files for no gain. `s07-builder.css` already
   holds its own tablet column (`.rd-cb-tab__library`) on the same reasoning.

   ANATOMY — the drawing's own split:

     [ .rd-tshell__content, a flex row ]
       [ .rd-af-tab__rail  300px, fixed, 1px right rule ]
       [ .rd-af-tab__pane  fluid: banners, the step's content, the foot ]

   ⚠️ 300 IS THE DRAWN RAIL AND NOT `RdPanes`. `ScreenTabAuthor` writes
   `width: 300, flex: none, borderRight 1px t.line`, and this is not a list
   pane: it is five fixed rows that are the screen's own progress. Same call
   `s17-tablet.css` records for the leaderboard's 300px standing column, whose
   note names the structural-300 family this joins.

   ⚠️ THE SCROLL IS A STATED DEVIATION, for `kit-tablet-panes.css`'s reason.
   The drawing writes `overflow: hidden` on both columns, which is right for a
   still frame and wrong in a shipped app: nothing above these elements scrolls
   (`.rd-tshell` and `.rd-tshell__content` are both clipped), so a twelve-step
   drill would be unreachable with no scrollbar anywhere to say so. Each column
   owns its own scroll. Five rows never need the rail's, and it is there for the
   short viewport rather than for the row count.

   ⚠️ TYPE FLOORS. `01 Foundations` §05: 11px mixed-case, 9.5px tracked
   uppercase. The rail's value line is drawn at 10.5px mixed-case, which is
   under the floor; it is transcribed rather than floored up for the reason
   `s17-tablet.css` records about its own 9px labels — the same value is drawn
   on the secondary line of every tablet row in the corpus, and floor-correcting
   one screen would make this rail disagree with the eleven beside it. Flagged,
   not fixed. Everything else here is the kit's or the phone column's and clears
   its own floor.
   -------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
   1. The two regions.
   --------------------------------------------------------------------------- */
.rd-af-tab {
  flex: 1;
  min-width: 0;
  display: flex;
  overflow: hidden;
}

/* `ScreenTabAuthor`, the left column — 300, flex none, 1px right rule. */
.rd-af-tab__rail {
  flex: none;
  width: 300px;
  box-sizing: border-box;
  min-height: 0;
  overflow-y: auto;
  border-right: 1px solid var(--rd-line);
}

/* The content column — the drawn `padding: '22px 28px'`, a flex column so the
   foot can be pushed to the bottom by `margin-top: auto`. */
.rd-af-tab__pane {
  flex: 1;
  min-width: 0;
  min-height: 0;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  padding: 22px 28px;
  overflow-y: auto;
}

.rd-af-tab__content {
  flex: 1;
  min-height: 0;
}

.rd-af-tab__banners:not(:empty) {
  margin: 0;
}

/* Loading, and the not-your-drill refusal. Each fills the content region on its
   own — there is no rail to put beside a form that has not loaded. */
.rd-af-tab__state {
  flex: 1;
  min-width: 0;
  min-height: 0;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 24px 28px;
  overflow-y: auto;
}

/* The no-claim fallback inside that state box: one 26px mark on the horizontal
   centre line (43 Loading States.html:52). The box itself is a column that
   other states fill edge to edge, so the centring is the mark's own and not
   the box's. */
.rd-af-tab__wait {
  display: flex;
  justify-content: center;
}

/* ---------------------------------------------------------------------------
   2. The rail's rows — the drawn 62px row with a value line under it.
   --------------------------------------------------------------------------- */
/* A `button`, because a past row navigates. The drawn row is a div in a still
   frame; a tappable thing that is not a button is unreachable by keyboard, and
   this rail IS the way back to step two.

   `border-left: 3px solid transparent` on every row, coloured only on the
   current one, so the selection does not move the row's text by 3px as it
   arrives — the drawn frames set the same transparent border for the same
   reason. */
.rd-af-tab__step {
  width: 100%;
  box-sizing: border-box;
  min-height: 62px;
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 0 18px;
  text-align: left;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  border-left: 3px solid transparent;
  cursor: pointer;
}

/* ⚠️ A ROW AHEAD OF THE AUTHOR IS DISABLED IN THE MARKUP, so the cursor has to
   say so too — a `pointer` over a control that refuses is the tappable-element-
   with-no-destination the corpus forbids, wearing a hand. */
.rd-af-tab__step:disabled {
  cursor: default;
}

/* THE SELECTION IDIOM, unchanged from every other screen that has one: volt on
   the left edge, `surf2` behind. */
.rd-af-tab__step--on {
  border-left-color: var(--rd-acc);
  background: var(--rd-surf2);
}

.rd-af-tab__step-n {
  flex: none;
  width: 18px;
  font-size: 11px;
  font-weight: 400;
  color: var(--rd-fg3);
}

/* *"A tick replaces the index once a step is done, so progress reads without a
   bar."* The tick is the accent as TEXT, so `--rd-acc` and never `--rd-volt`. */
.rd-af-tab__step--done .rd-af-tab__step-n {
  color: var(--rd-acc);
}

.rd-af-tab__step-body {
  flex: 1;
  min-width: 0;
}

.rd-af-tab__step-name {
  display: block;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg);
}

/* Dim, and it is the whole of how the sequence communicates itself: *"the drawn
   state communicates this by colour — past and current full, future dim."* */
.rd-af-tab__step--ahead .rd-af-tab__step-name {
  color: var(--rd-fg3);
}

/* *"Completed steps show their own value — the review screen's job, done
   continuously."* One line, clamped: a long drill name must not make one rail
   row twice the height of the four beside it.

   ⚠️ NO `display` OR `overflow` HERE. `rd-clamp-1` is a kit utility and the kit
   parts are assembled ABOVE every page section, so a page rule declaring either
   of those at equal specificity silently unclamps it — `kit-clamp.css` says so
   in its own header. */
.rd-af-tab__step-val {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  word-break: break-word;
}

/* ---------------------------------------------------------------------------
   3. The foot — Next at the drawn 220, Back as a quiet word beside it.
   --------------------------------------------------------------------------- */
.rd-af-tab__foot {
  margin-top: auto;
  display: flex;
  align-items: center;
  gap: 14px;
  padding-top: 16px;
}

.rd-af-tab__next {
  width: 220px;
}

/* *"Back"* is drawn as a tracked uppercase word at `t.fg3`, not a second
   button: two buttons of equal weight at the foot of a wizard make going
   backwards look like half the offer. */
.rd-af-tab__back {
  min-height: 44px;
  padding: 0 4px;
  background: none;
  border: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-fg3);
  cursor: pointer;
}

/* ---------------------------------------------------------------------------
   4. Step 02 — the steps table, three-up.
   --------------------------------------------------------------------------- */
/* *"Steps render as three columns: index, distance and rounds, then the
   sentence. The phone stacks those. The sentence is the part authors actually
   write and it deserves the width."*

   ⚠️ THIS IS THE WHOLE OF THAT CHANGE, AND IT IS THREE DECLARATIONS OVER THE
   PHONE'S OWN CARD. `.rd-af__step` is already a flex row whose first child is
   the index; all that is missing at width is that its BODY stops stacking. A
   second step card in the Rust would have been a second set of inputs writing
   one signal. */
.rd-af-tab__steps .rd-af__step-body {
  display: flex;
  align-items: flex-start;
  gap: 18px;
}

/* The drawn measurement column: 130px, fixed, beside the sentence rather than
   above it. */
.rd-af-tab__steps .rd-af__step-measures {
  flex: none;
  width: 130px;
  margin-bottom: 0;
}

/* `width: 100%` is the phone rule and it is the wrong axis inside a flex row —
   100% of the body, next to a 130px sibling, overflows. */
.rd-af-tab__steps .rd-af__step-text {
  flex: 1;
  min-width: 0;
  width: auto;
}

/* The drawn header pair (`rd-tablet-authoring.jsx:159-162`): the label and the
   running rounds figure share one baseline at a 16px gap. Not
   `.rd-af__labelrow` — that one is `space-between`, which is right for a label
   with an `Optional` marker at the far edge and wrong for two things that read
   as one line. */
.rd-af-tab__steps-head {
  display: flex;
  align-items: baseline;
  gap: 16px;
}

.rd-af-tab__rounds {
  font-size: 12px;
  font-weight: 400;
  color: var(--rd-fg2);
}

/* *"add-step on a tablet is an inline accent text row, 48px min-height"*
   (`rd-tablet-authoring.jsx:173`). A bare row, not a bordered control: the step
   cards above it are the boxes, and a fourth box that adds a fifth would read
   as another step rather than as the act of making one. */
.rd-af-tab__addstep {
  display: flex;
  align-items: center;
  min-height: 48px;
  padding: 0;
  background: none;
  border: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-accent);
  cursor: pointer;
}

/* ---------------------------------------------------------------------------
   5. Step 03 — the zones, two-up.
   --------------------------------------------------------------------------- */
/* *"Zones render two-up with the value right-aligned … Five zones fit in one
   glance, so the relationship between Alpha and Miss is visible — which is the
   thing a new author gets wrong."*

   Written as a compound with the class it modifies rather than relying on
   source order: the two selectors are one element and the page must not depend
   on which of its own rules the assembler appends last.

   ⚠️ THE DRAWN DANGER TONE ON NEGATIVES IS NOT ADOPTED, and the reason is that
   ours are INPUTS. `ScreenTabAuthor` colours a printed `−10` in danger tone;
   this row is a number field the author edits, and a red value in a field reads
   as a validation failure rather than as a penalty zone. The penalty is already
   labelled in words on the row (`.rd-af__zone-pen`). Flagged, not invented. */
.rd-af__zones.rd-af-tab__zones {
  grid-template-columns: 1fr 1fr;
  gap: 0 30px;
  max-width: 620px;
}

.rd-af-tab__zones .rd-af__zone {
  min-height: 50px;
  border-bottom: 1px solid var(--rd-line);
}

/* ---------------------------------------------------------------------------
   6. The panes' own measures — 420 for a field, 620 for prose.
   --------------------------------------------------------------------------- */
/* *"name field max-width ~420"*. A text input stretched across 900px is a
   target the eye has to travel; the pane is wide so that the STEPS can be, not
   so that every control is. */
.rd-af-tab__field {
  max-width: 420px;
}

.rd-af-tab__section {
  margin-top: 20px;
}

.rd-af-tab__section:first-child {
  margin-top: 0;
}

/* ---------------------------------------------------------------------------
   7. Step 04 — the diagram, before there is a drill to hang one on.
   --------------------------------------------------------------------------- */
/* UNDRAWN. A media row needs a saved `drill_id`, so on a create this step has
   no slot to mount — see the component's module doc. A sentence, at the same
   measure as the rest of the pane's prose. */
.rd-af-tab__later {
  max-width: 62ch;
}

.rd-af-tab__later-lead {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 11px;
  color: var(--rd-fg2);
}

.rd-af-tab__later-body {
  margin-top: 8px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  line-height: 1.55;
  color: var(--rd-fg3);
}

.rd-af-tab__diagram {
  max-width: 620px;
}

/* ---------------------------------------------------------------------------
   8. Step 05 — review.
   --------------------------------------------------------------------------- */
.rd-af-tab__review {
  max-width: 620px;
}

/* *"Review keeps its four derived numbers"* — drawn as a row at gap 36, not as
   the phone's three-column recessed box. `RdStat` renders each one, so the
   figures here are the same primitive the derived block uses. */
.rd-af-tab__figures {
  display: flex;
  flex-wrap: wrap;
  gap: 18px 36px;
  margin-top: 18px;
}

/* *"Save is a 280px button, not a full-width one — the pane is wide, the
   decision is not."* */
.rd-af-tab__save {
  width: 280px;
  max-width: 100%;
  margin-top: 18px;
}
/* ==== S05-EDITOR · 05 Stage Editor · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   `/training/drills/:id/stage-editor` AND `/admin/drills/:id/stage-editor`
   (`pages/stage_editor.rs` — one implementation, two mounts). Drawn:
   **05 Stage Editor** — `ScreenShelfCanvas`, `ScreenShelfObject`,
   `ScreenShelfDesktop`, and the leave-confirm of row 02.

   ⚠️ THIS BLOCK PAINTS BOTH CONSOLES, which is the one thing to know before
   editing it. The shooter editor renders inside `ShellLayout` and the admin one
   inside `DashboardLayout`; the component is deliberately layout-agnostic (its
   back route is a passed value for the same reason), so nothing here may assume
   either chrome. That is why there are no shell classes, no gutter and no
   assumptions about what is above the bar.

   ⚠️ THE CANVAS INK IS NOT IN THIS FILE AND MUST NOT COME INTO IT. The paper,
   its edge, the grid and the label ink are constants in the component, shared
   with `drill2d/render.rs` so the author sees what every read surface draws —
   iOS and Android included. A token would make the editor the only place in the
   product where a stage looks like this. The chrome AROUND the canvas is
   tokens; the paper is not, deliberately, and the component says so too.

   ⚠️ WHAT THIS REPLACES. `.stage-editor-canvas`, `.stage-editor-toolbar`,
   `.stage-editor-palette`, `.se-btn`, `.se-danger` and `.se-tile` leave
   `_legacy-pre-kit.css` with this change — a rewrite owns the CSS it strands,
   and those six were the whole of the editor's fixed-light chrome (`#fff`
   grounds, `#d1d5db` borders, `#374151` type).

   THE SHELF IS THE DOCUMENT'S CENTRAL ARGUMENT: *"The canvas gets the screen.
   Every tool, every element and every action lives in one shelf along the
   bottom."* So the layout here is three rows in a column — a bar that does not
   scroll, a canvas that takes everything left, and a shelf that is always open
   and never grows past its own content.
   --------------------------------------------------------------------------- */

.rd-se {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  background: var(--rd-bg);
}

/* ---- the bar ---- */

/* Drawn identically on phone and on web: *"Wider because there is room, not
   different because there is room."* It never scrolls and never changes with
   the shelf, so the way out is in the same place in every state. */
.rd-se__bar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--rd-line);
  background: var(--rd-bg);
}

.rd-se__back {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: 0;
  background: transparent;
  padding: 0;
  cursor: pointer;
}

.rd-se__chev {
  width: 9px;
  height: 15px;
  stroke: var(--rd-acc);
}

.rd-se__title {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: 0.04em;
  font-size: 13px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rd-se__baracts {
  flex: none;
  display: flex;
  align-items: center;
  gap: 6px;
}

/* Undo and Redo, moved off the six-glyph toolbar — and drawn as WORDS
   (`rd-editor-shelf.jsx:237-238`), not as two arrow glyphs in boxes. The
   document's whole thesis is *"the shipped toolbar is six unlabelled glyphs and
   that is the most reported confusion"*; a bordered 34×34 square holding `↶`
   was the last of those six still on the screen. So: display text, no box, and
   the tap target comes from padding rather than from a drawn edge. */
.rd-se__baract {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 10px;
  line-height: 1;
  min-height: 34px;
  padding: 0 6px;
  color: var(--rd-fg2);
  background: transparent;
  border: 0;
  cursor: pointer;
}

.rd-se__baract:disabled {
  opacity: 0.35;
  cursor: default;
}

/* The screen's one volt-filled role. *"Done in the bar is the only way out with
   the work kept"* — so nothing else on the editor may take this treatment, and
   the Check tab's closing sentence points here rather than offering a second
   save. */
/* In flight, the sweep joins the label at 10px of gap and `Done` does not move
   (kit-loading.css §8, rd-loading.jsx:197). */
.rd-se__done {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 10px;
  color: var(--rd-on-volt);
  background: var(--rd-volt);
  border: 0;
  min-height: 34px;
  padding: 9px 14px;
  cursor: pointer;
}

.rd-se__done:disabled { opacity: 0.5; cursor: default; }

.rd-se__banner { padding: var(--rd-banner-gap) 14px 0; }

.rd-se__loading {
  padding: 40px 0;
  display: flex;
  justify-content: center;
}

/* ---- the canvas ---- */

/* *"On a phone the drawing surface gets under half the screen … People are
   building forty-element scenes in that window."* `flex: 1` between a bar and a
   shelf that both refuse to grow is the whole fix. */
.rd-se__canvas {
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: 10px 14px;
  /* Pointer events own the canvas: no scroll-while-drag. Carried across from
     `.stage-editor-canvas` unchanged, because it is behaviour rather than
     paint and the drag handlers still depend on it. */
  touch-action: none;
}

.rd-se__canvas svg {
  display: block;
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
}

/* ---- the shelf ---- */

/* Always open, never taller than it needs to be, and it scrolls INSIDE itself
   rather than pushing the canvas off the top.

   ⚠️ **236px, A FIXED HEIGHT AND NOT A VIEWPORT FRACTION.**
   `rd-editor-shelf.jsx:38` is `const SH = { h: 236 }` against a 676-tall device
   — about 35%. The old `max-height: 46%` was well past that on a tall phone,
   and the document's whole argument is that the canvas gets the screen. Held as
   a `clamp` rather than a bare 236 so a short phone (a 568-tall SE) does not end
   up with the shelf owning 42% of it; the drawn number is the top of the range
   and the case a normal phone lands on. */
.rd-se__shelf {
  flex: none;
  max-height: clamp(180px, 35%, 236px);
  display: flex;
  flex-direction: column;
  min-height: 0;
  border-top: 1px solid var(--rd-line);
  background: var(--rd-surf);
}

/* The object mood's header. Duplicate and Delete live HERE and not in a tab:
   *"Nothing that removes work should be inside a tab you might land on by
   accident."* */
.rd-se__shelfhead {
  flex: none;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px 8px;
}

.rd-se__shelfhead-copy {
  flex: 1;
  min-width: 0;
}

.rd-se__selname {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 10px;
  color: var(--rd-fg);
}

/* *"The header also says how to get out: tap empty stage to stop working on it.
   One sentence, always present."* */
.rd-se__selhint {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
  margin-top: 3px;
}

.rd-se__headact {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 8.5px;
  color: var(--rd-fg2);
  background: transparent;
  border: 1px solid var(--rd-line);
  min-height: 30px;
  padding: 7px 9px;
  cursor: pointer;
}

.rd-se__headact--rm {
  color: var(--rd-danger);
  border-color: var(--rd-danger);
}

/* ⚠️ TABS ARE WORDS. *"the shipped toolbar is six unlabelled glyphs and that is
   the most reported confusion in the whole editor."* The strip scrolls
   horizontally rather than wrapping, so the tabs stay one row and their order
   — which IS the journey from empty ground to a finished drill — is readable
   left to right. */
.rd-se__tabs {
  flex: none;
  display: flex;
  gap: 2px;
  padding: 0 14px;
  overflow-x: auto;
  border-bottom: 1px solid var(--rd-line);
}

.rd-se__tab {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 9.5px;
  color: var(--rd-fg3);
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  min-height: 38px;
  padding: 10px 12px;
  cursor: pointer;
}

/* ⚠️ **THE LABEL IS `--rd-fg`, THE RULE IS `--rd-acc`.**
   `rd-editor-shelf.jsx:66` draws the live tab as `color: on ? t.fg : t.fg3` with
   the accent spent only on the 2px rule underneath. Painting both in accent
   spends the loudest colour on the screen twice for one state, and the label
   then competes with `Done` — the screen's one volt-filled role. */
.rd-se__tab--on {
  color: var(--rd-fg);
  border-bottom-color: var(--rd-acc);
}

.rd-se__pane {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 12px 14px 14px;
}

/* ---- Add ---- */

.rd-se__cat { margin-bottom: 12px; }

/* The Add pane's own rule, and it exists for one job: the last category must
   not push a gap under itself, because the pane already owns its bottom
   padding and the two together read as the shelf having stopped early.
   (Caught by this screen's class sweep, which failed on `.rd-se__add`
   resolving to nothing — the class was in the markup and in no rule.) */
.rd-se__add .rd-se__cat:last-child { margin-bottom: 0; }

.rd-se__cat-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 8.5px;
  color: var(--rd-fg3);
  margin-bottom: 6px;
}

/* ⚠️ WRAPS, AND DOES NOT SCROLL SIDEWAYS. The shipped palette was a horizontal
   scroller and the drawing names the consequence: *"'Plate Rack' is cut off at
   the edge with no indication there is more."* A wrapping grid has no hidden
   right-hand side. */
.rd-se__tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(66px, 1fr));
  gap: 6px;
}

.rd-se__tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  min-height: 68px;
  padding: 6px 4px;
  background: var(--rd-chip);
  border: 1px solid var(--rd-line);
  cursor: pointer;
}

/* The artwork is the catalog's, on the paper it will sit on — a target glyph
   drawn straight onto a dark shelf loses the parts of itself that are dark. */
.rd-se__tile-art {
  width: 34px;
  height: 34px;
  background: var(--rd-paper);
}

.rd-se__tile-label {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 8px;
  color: var(--rd-fg3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

/* ---- Layers ---- */

.rd-se__layers { display: flex; flex-direction: column; }

.rd-se__layer {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 44px;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  background: transparent;
  padding: 0;
  text-align: left;
  font: inherit;
  cursor: pointer;
}

.rd-se__layer:last-child { border-bottom: 0; }

.rd-se__layer-copy {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.rd-se__layer-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg);
}

.rd-se__layer-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
  margin-top: 2px;
}

.rd-se__layer-lock {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 8px;
  color: var(--rd-fg3);
}

.rd-se__empty {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
  padding: 10px 0;
}

/* ---- Check ---- */

/* ⚠️ NOT A DANGER TONE, and that is the drawing's own ruling: *"None of it
   blocks saving — it is advice, not validation."* A danger rule down the side
   of a sentence that does not stop anything teaches a reader to ignore the
   danger rule that does. The mark below is `--rd-warn` and it is an outline,
   which is the pair `rd-editor-shelf.jsx:201` draws (`t.acc` filled / `t.warn`
   ring) — deliberately not `--rd-ok`, since a row that has not fired is not a
   success. */
.rd-se__check { display: flex; flex-direction: column; gap: 8px; }

/* The row's mark is a 14px CIRCLE and not a rule down its side
   (`rd-editor-shelf.jsx:201`): filled in the accent when the row passes, a
   1.4px warn-coloured RING when it does not. Two states of one mark, so the
   column of dots is readable before any of the sentences are — which is the
   whole point of giving an advisory list a positive first row. */
.rd-se__check-row {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--rd-fg);
  padding: 2px 0;
}

.rd-se__check-row::before {
  content: '';
  flex: none;
  width: 14px;
  height: 14px;
  margin-top: 1px;
  border-radius: 8px;
}

.rd-se__check-row--ok {
  color: var(--rd-fg2);
}

.rd-se__check-row--ok::before {
  background: var(--rd-acc);
}

/* Warn, not danger, and the ring rather than a fill: the drawing spends the
   filled mark on the row that passed. A solid warn dot beside a solid accent
   dot would read as two results of equal weight. */
.rd-se__check-row--flag::before {
  border: 1.4px solid var(--rd-warn);
}

.rd-se__check-foot {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.45;
  color: var(--rd-fg3);
  margin-top: 4px;
}

.rd-se__removeall {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--rd-line);
}

.rd-se__removeall-btn {
  width: 100%;
  min-height: 44px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 10px;
  color: var(--rd-danger);
  background: transparent;
  border: 0;
  padding: 0;
  text-align: left;
  cursor: pointer;
}

/* ---- Place ---- */

.rd-se__place { display: flex; flex-direction: column; gap: 10px; align-items: center; }

.rd-se__steps { display: flex; gap: 6px; }

.rd-se__step {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 9px;
  color: var(--rd-fg2);
  background: var(--rd-chip);
  border: 1px solid var(--rd-line);
  min-height: 30px;
  padding: 7px 11px;
  cursor: pointer;
}

.rd-se__step--on {
  background: var(--rd-chip-on-fill);
  border-color: var(--rd-acc);
  color: var(--rd-chip-on-label);
}

/* A 3×3 grid with the four arrows on its edges. *"a fingertip is about 9 mm and
   a target on a phone canvas is smaller than that"* — so every pad key is 44px,
   which is the platform minimum and the reason the pad exists at all. */
.rd-se__pad {
  display: grid;
  grid-template-columns: repeat(3, 44px);
  grid-template-rows: repeat(3, 44px);
  gap: 4px;
}

.rd-se__nudge {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 15px;
  line-height: 1;
  color: var(--rd-fg2);
  background: var(--rd-chip);
  border: 1px solid var(--rd-line);
  cursor: pointer;
}

.rd-se__nudge--up { grid-column: 2; grid-row: 1; }
.rd-se__nudge--left { grid-column: 1; grid-row: 2; }
.rd-se__nudge--right { grid-column: 3; grid-row: 2; }
.rd-se__nudge--down { grid-column: 2; grid-row: 3; }

/* *"Each tap moves a stated amount"* — so the amount is stated, next to the
   coordinates it is moving. */
.rd-se__readout {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 10px;
  color: var(--rd-fg3);
  text-align: center;
}

/* ---- Size / Turn / Order ---- */

.rd-se__prop { margin-bottom: 12px; }

.rd-se__prop-label {
  display: block;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 9px;
  color: var(--rd-fg3);
  margin-bottom: 6px;
}

.rd-se__slider {
  width: 100%;
  accent-color: var(--rd-acc);
}

.rd-se__propacts {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 8px;
}

.rd-se__propact {
  flex: 1 1 auto;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 9px;
  color: var(--rd-fg2);
  background: transparent;
  border: 1px solid var(--rd-line);
  min-height: 40px;
  padding: 10px 11px;
  cursor: pointer;
}

.rd-se__propact[aria-pressed='true'] {
  border-color: var(--rd-acc);
  color: var(--rd-acc);
}

/* *"The angles that matter are buttons, and the stepper is there for the
   rest."* */
.rd-se__angles {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 12px;
}

.rd-se__angle {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: 0.06em;
  font-size: 10px;
  color: var(--rd-fg2);
  background: var(--rd-chip);
  border: 1px solid var(--rd-line);
  min-height: 40px;
  min-width: 48px;
  padding: 10px 11px;
  cursor: pointer;
}

.rd-se__order { display: flex; flex-direction: column; gap: 10px; }

.rd-se__text {
  width: 100%;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
  background: var(--rd-field);
  border: 1px solid var(--rd-line);
  padding: 9px 11px;
}

/* ==========================================================================
   §T-editor · THE TABLET RENDERING — nothing covers the canvas
   ==========================================================================

   Drawn: **`05 (Tablet) Stage Editor`**, rows "Palette, Canvas, Inspector" and
   "Nothing Selected, And Leaving" — component `ScreenTabEditor`. Rust:
   `pages/stage_editor.rs`, the `DeviceClass::Tablet` arm.

   ⚠️ **CORRECTED 2026-08-29 — THIS BLOCK PAINTS BOTH CONSOLES, like the one
   above.** It used to read: *"THIS BLOCK PAINTS ONE CONSOLE… the admin stage
   editor is pinned to the phone rendering at every width
   (`stage_editor::rendering_for`), so nothing below ever resolves against the
   Pro chrome."* That pin was the defect James reported: Pro → Org Library → a
   drill → *draw its stage* opened the PHONE editor, shelf stacked under the
   canvas, on a 1194px console. The premise was a fact about the SHELL and was
   applied to the COLUMNS — `RdTabletShell` is indeed the shooter shell and must
   not mount in the Pro console, but nothing below is a shell rule. So the Rust
   arm now builds `.rd-se-tab` once and mounts it in whichever frame it is
   handed (`stage_editor::tablet_frame_for`): `RdTabletShell` for the shooter,
   `.rd-pro-page` + `ProTop` for the console.

   Nothing in this block changed for it, and that is the point — every rule here
   is a rule about the three columns and their contents, none reaches for a
   shell class, and the ANATOMY below holds under either frame because the two
   frames' rails are the same 208 (`--rd-pro-rail-w`, `adm0-chrome.css:83`).

   ⚠️ THE PHONE'S CLASSES ARE BORROWED WHOLE, NOT REDECLARED. This is the whole
   claim of the arm — the shelf's two moods become two permanent columns, and
   the controls inside them are the same controls. The tab strip
   (`.rd-se__tabs` / `.rd-se__tab`), the palette tiles (`.rd-se__tiles`,
   `.rd-se__tile*`), the nudge pad, the sliders, the angle buttons, the layers
   list, the check rows, the category headings, the back chevron, `Done`, the
   undo/redo pair and the error banner are all the SAME rules the phone renders.
   Four rules below reach into three of them to make the column changes; nothing
   else here declares or overrides a `.rd-se__*` selector.

   ⚠️ THE PAPER IS STILL NOT IN THIS FILE. `.rd-se-tab__paper` declares a box
   and no ground: the scene's own background rect paints `CANVAS_FILL`, which is
   the READ RENDERER's literal ink, shared with `drill2d/render.rs` and embedded
   by the iOS and Android renderers. A `background:` here would be a second,
   tokenised paper under the first — the exact leak this file's header forbids,
   arriving through a new region rather than through a re-skin.

   ANATOMY — the drawing's own three regions, and their arithmetic:

     [ .rd-tshell__content, a flex ROW      — shooter    ]
     [ .rd-pro-page,        a flex COLUMN   — console    ]
       [ .rd-se-tab  flex:1, a row of three ]
         [ .rd-se-tab__palette    208, fixed, 1px right rule ]
         [ .rd-se-tab__centre     fluid: toolbar strip, then the plan on surf2 ]
         [ .rd-se-tab__inspector  230, fixed, 1px left rule ]

   ⚠️ THE TWO HOSTS RUN ON DIFFERENT AXES, WHICH IS THE ONE LINE THIS ARM OWES
   THE SECOND MOUNT. `flex: 1` claims the inline axis under the shooter's row
   and the BLOCK axis under the console's column, and a column flex item takes
   `min-height: auto` — it refuses to shrink below the palette's and the
   inspector's own content, so the region would grow past the console page and
   `.rd-pro-page`'s `overflow: auto` would scroll the whole editor instead of
   the two columns scrolling themselves. `min-height: 0` below is that fix, and
   it is inert under the shooter's row (where the cross axis is already
   stretched to the content region's bounded height) — one declaration, correct
   in both hosts, rather than a host-scoped selector.

   ⚠️ 520 IS ARITHMETIC AND NOT A PREFERENCE, and this is the twin of
   `stage_editor::TABLET_CANVAS_W`'s comment: 1194 (the drawn canvas) less the
   208 rail, less the 208 palette below, less the 230 inspector below, less this
   column's 2×14 padding leaves 520. The HEIGHT is not here at all — it is
   derived per scene from the live viewBox (`tablet_canvas_height`), because our
   scenes are 393×375 and legacy ones are anything, and a fixed 4:3 box would
   letterbox nearly every real diagram with dead paper.
   ========================================================================== */

/* §T.1 — the three regions */

.rd-se-tab {
  flex: 1;
  min-width: 0;
  /* See the ANATOMY note: `min-height: 0` is what makes ONE region correct in
     both hosts — the shooter's flex ROW and the console's flex COLUMN. */
  min-height: 0;
  display: flex;
  overflow: hidden;
}

/* The header's control cluster. The drawn header takes one action; ours takes
   the shipped bar's two, because no rail row reaches the drill form this editor
   was opened from — see the arm's own comment. */
.rd-se-tab__acts {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ---- left · the palette, permanent ---- */

/* *"Palette permanent, left, 208px."* The same 208 the rail takes, and the
   coincidence is the drawing's: both are a column of fixed choices beside
   something fluid. */
.rd-se-tab__palette {
  width: 208px;
  flex: none;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-right: 1px solid var(--rd-line);
}

.rd-se-tab__pal-tabs { flex: none; }

/* Reach 1 of 4. The shelf strip carries the shelf's own 14px side padding and
   scrolls sideways; four tabs in a 208px column want neither — they divide the
   column, at the drawn 8.5px. */
.rd-se-tab__pal-tabs .rd-se__tabs {
  padding: 0;
  gap: 0;
  overflow-x: hidden;
}

.rd-se-tab__pal-tabs .rd-se__tab {
  flex: 1;
  min-width: 0;
  padding: 10px 4px;
  font-size: 8.5px;
  letter-spacing: 0.06em;
}

.rd-se-tab__pal-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 12px;
}

/* Reach 2 of 4. *"Glyph tiles in a 2-up grid."* The phone's grid is
   `auto-fill, minmax(66px, 1fr)`, which happens to resolve to two columns at
   this width today — and would silently become three the day the minimum
   moves. The drawn count is stated rather than inherited. */
.rd-se-tab__pal-body .rd-se__tiles {
  grid-template-columns: 1fr 1fr;
  gap: 9px;
}

/* ---- centre · the toolbar and the plan ---- */

.rd-se-tab__centre {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* *"Undo and redo stay a two-icon toolbar and do not grow."* Ours were already
   two; they move off the bar to the strip over the plan, where the drawing puts
   them, and the count reads out at the far end. */
.rd-se-tab__toolbar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 9px 14px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-se-tab__count {
  margin-left: auto;
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 10.5px;
  color: var(--rd-fg3);
}

/* The working ground the plan sits on — `surf2`, so the paper reads as paper
   against it rather than as the page. `touch-action: none` carries across from
   `.rd-se__canvas`: it is behaviour rather than paint, and the drag handlers
   depend on it in both renderings. */
.rd-se-tab__stage {
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: auto;
  padding: 14px;
  background: var(--rd-surf2);
  touch-action: none;
}

/* ⚠️ `flex: none`, for the drawing's own stated reason: *"a flex child with
   default shrink silently gives back whatever width it is told to have."* */
.rd-se-tab__paper {
  width: 520px;
  flex: none;
}

/* Reach 3 of 4. `.rd-se__canvas svg` fits the plan INSIDE whatever room is
   left (`max-width/height: 100%`, `width: auto`); here the box is the plan's
   own aspect, so the svg takes the box exactly and `xMidYMid meet` has nothing
   to letterbox. */
.rd-se-tab__paper svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* ---- right · the inspector, permanent ---- */

/* *"Inspector permanent, right, 230px, and this is the part the phone genuinely
   cannot do. On a phone the numeric inspector is a sheet that covers what it
   edits."* */
.rd-se-tab__inspector {
  width: 230px;
  flex: none;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 16px 18px;
  border-left: 1px solid var(--rd-line);
}

.rd-se-tab__insp-head { margin-bottom: 16px; }

.rd-se-tab__kicker {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 9px;
  color: var(--rd-fg3);
}

.rd-se-tab__objname {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 72%;
  letter-spacing: 0.01em;
  font-size: 15px;
  color: var(--rd-fg);
  margin-top: 6px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rd-se-tab__nums {
  display: grid;
  gap: 10px;
  margin-bottom: 16px;
}

.rd-se-tab__num {
  display: flex;
  align-items: center;
  gap: 10px;
}

.rd-se-tab__num-k {
  width: 62px;
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 9px;
  color: var(--rd-fg3);
}

.rd-se-tab__num-v {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  min-height: 34px;
  padding: 0 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--rd-fg);
  background: var(--rd-field);
  border: 1px solid var(--rd-line);
}

/* One of the shelf's panes, stacked out of its tab and under its own word. */
.rd-se-tab__group { margin-bottom: 16px; }

/* Reach 4 of 4. The last control in a group must not push a gap under itself:
   `.rd-se__prop` carries a bottom margin for a pane whose siblings follow it,
   and here the group's own margin already does that job. Same shape as
   `.rd-se__add .rd-se__cat:last-child` one section up. */
.rd-se-tab__group .rd-se__prop:last-child { margin-bottom: 0; }

/* *"The empty inspector is a sentence, not four greyed inputs."* */
.rd-se-tab__nosel {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  line-height: 1.55;
  color: var(--rd-fg3);
  margin-bottom: 16px;
}

/* *"Delete object at the foot."* `margin-top: auto` is the drawn placement: it
   sits at the bottom of the column and not under whichever control happens to
   be last. */
.rd-se-tab__foot {
  margin-top: auto;
  padding-top: 16px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}

/* Quiet text, never a button ground — the drawn note about the leave modal's
   discard says the same thing about the same weighting, and this is the one
   control in the column that removes work. It is still a real `<button>`: the
   act is a press, and a `<div>` with an `onclick` is unreachable by keyboard. */
.rd-se-tab__footact {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 9.5px;
  color: var(--rd-fg3);
  background: transparent;
  border: 0;
  min-height: 34px;
  padding: 0;
  cursor: pointer;
}

.rd-se-tab__footact--rm { color: var(--rd-danger); }
/* ==== S09-COURSE · 09 Multi-Shooter — the course squad · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   `/training/courses/:id/squad` (`pages/squad_course_run_page.rs`) — the third
   of the three routes the drawing names, and the one S2 deliberately left
   alone to keep that wave a clean bisect point.

   ⚠️ THE SETUP PHASE HAS NO RULES HERE, AND THAT IS THE POINT. It is drawn —
   **09 Multi-Shooter**, `ScreenSquadSetup` with `kind='Course'` — and the
   sibling route already built it. This page composes the SAME `.rd-squad__*`
   classes rather than a second set: the drawing's own `kind` prop says the two
   screens differ by one eyebrow and one meta line, and two stylesheets for one
   screen is how the eyebrow ends up being the only thing that still matches.
   `squad_score_entry.rs`'s own comment predicted this file — *"the course
   squad is its own route and its own screen, so this one is always a drill"* —
   and this is the other half of that sentence.

   What IS here is the half the corpus does not draw: scoring a whole squad on
   ONE stage at once, and the per-shooter standing that rolls up from it.
   `ScreenSquadBoard` is the drill squad's board, and it draws a different
   interaction — one shooter at a time, whose turn it is, an entry that expands
   in place. A course squad enters every shooter for the current stage together
   and advances the stepper. **UNDRAWN, flagged, and built from the board's
   vocabulary rather than from a new one**: the same hairline stack, the same
   ranked row shape, the same numeric column.
   --------------------------------------------------------------------------- */

.rd-cs {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
}

/* The no-claim fallback: one 26px mark centred on empty ground, with the
   shell's chrome real around it (43 Loading States.html:52). */
.rd-cs--wait {
  align-items: center;
  justify-content: center;
  padding: 48px 0;
}

.rd-cs__chips {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-bottom: 9px;
}

.rd-cs__sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--rd-fg2);
  margin-top: 7px;
}

.rd-cs__banner { margin-top: var(--rd-banner-gap); }

/* ── UNDRAWN: the stage the squad is on ──────────────────────────────── */

.rd-cs__stage { margin-top: 15px; }

.rd-cs__stage-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 6px;
}

.rd-cs__stage-name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .04em;
  font-size: 13px;
  color: var(--rd-fg);
}

.rd-cs__stage-count {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 11px;
  color: var(--rd-fg3);
  flex: none;
}

.rd-cs__rows {
  display: grid;
  gap: 9px;
  margin-top: 4px;
}

.rd-cs__row-inner {
  padding: 11px 13px 12px;
  box-sizing: border-box;
}

.rd-cs__row-name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .04em;
  font-size: 12px;
  color: var(--rd-fg);
  margin-bottom: 8px;
}

.rd-cs__foot {
  flex: none;
  margin-top: 14px;
  padding-top: 11px;
  padding-bottom: 12px;
  border-top: 1px solid var(--rd-line);
}

/* The drawn foot note on the board says the same thing about the same
   guarantee, and it is worth saying here for the same reason: a squad at a bay
   needs to know the stage is banked before it moves on. */
.rd-cs__foot-note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
  margin-top: 8px;
  text-align: center;
  text-wrap: pretty;
}

.rd-cs__complete { margin-top: var(--rd-banner-gap); }

/* ── UNDRAWN: the standing ───────────────────────────────────────────── */

.rd-cs__standing { margin-top: 20px; }

.rd-cs__standing-list { margin-top: 5px; }

.rd-cs__standing-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 46px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-cs__standing-row--last { border-bottom: none; }

/* Tabular, and bare rather than prefixed: the `1.` the shipped row printed was
   a label doing a column's job — the same finding `s17.css` records about the
   leaderboard's `#`. */
.rd-cs__standing-pos {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 11px;
  color: var(--rd-fg3);
  width: 12px;
  text-align: right;
  flex: none;
}

.rd-cs__standing-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Both outer columns are numeric and both are tabular, for the reason the
   leaderboard's are: a standing re-sorts under the reader after every stage,
   and proportional figures move the column edge on every digit that changes. */
.rd-cs__standing-val {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 15px;
  color: var(--rd-fg);
  flex: none;
}

.rd-cs__standing-unit {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8.5px;
  color: var(--rd-fg3);
  flex: none;
}
/* ==== S10-PAR · 10 Dry Fire — the par timer · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   `/dry-fire/:drill_id` (`pages/par_timer.rs`, body in
   `pages/par_timer_body.rs`). Drawn: **10 Dry Fire**, `ScreenDryTimer` in its
   three states — ready, running (wait / go), and the settings that only exist
   in the first — plus `DryNum`.

   ⚠️ IT IS THE DRAWING'S SCREEN AGAIN SINCE C-00.5 (ruled, James, 27 Aug).
   This block read *"THIS IS NOT THE DRAWING'S SCREEN, EXACTLY"*: the sheet was
   written for `/training/drills/:drill_id/par-timer`, **a drill's** par timer
   reached from the Live Fire drill list, which shared a name with the map's Par
   Timer under Dry Fire — *"the only beeper"* — and answered a different back
   row. The ruling took that door and its route out of the build, so one address
   is left and it is the drawn one. Not one declaration changed with it:
   everything below the chrome was always `ScreenDryTimer`'s, because the
   running state was the same job on both.

   ⚠️ THE RUNNING STATE IS SIZED FOR A BENCH A METRE AWAY, and that is the
   drawing's whole argument for the screen: *"at a 1.25 second par you cannot
   look at the screen"*, so the number you are accumulating is 96px and
   everything that needs reading carefully happens before GO or after STOP.
   The shipped page had this exactly inverted — the ELAPSED time at 64px and
   the rep count at 16px in grey, so the big number was the one that resets
   every rep and the small one was the session.

   Four fixed hexes leave with that band: `#fbbf24`, `#4ade80`, `#60a5fa`,
   `#d1d5db`. The phase is a word now, in `--rd-acc` when the beep has gone.
   --------------------------------------------------------------------------- */

.rd-par {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  padding-top: 4px;
}

.rd-par__head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex: none;
}

.rd-par__title {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 900;
  font-stretch: 62%;
  letter-spacing: .01em;
  font-size: 22px;
  line-height: 1;
  color: var(--rd-fg);
  flex: 1;
  min-width: 0;
}

/* Collapsed to two lines. *"You read this once; after that it is a paragraph
   sitting on top of the button you are pressing fifty times."*

   ⚠️ THE CLAMP ITSELF IS `.rd-clamp-2`, APPLIED AT THE CALL SITE in
   `pages/par_timer_body.rs`. "Collapsed to two lines" is still this rule's
   decision; the four declarations that implement it are the kit's, on the tablet
   kit's ruling — rd-tablet-kit.jsx:21 ("Line clamp. Lives in the KIT, not in a page file").

   ⚠️ AND IT IS A DISCLOSURE NOW, NOT A PERMANENT TRUNCATION. The drawn screen
   clamps behind an `open` flag (`rd-dryfire.jsx`, `ScreenDryTimer`'s `open`
   prop); the build shipped the clamp and not the flag, so the rest of a long
   description was unreachable. The paragraph IS the control — the drawing adds
   no chevron and no "More" word — so the element is a `button` and everything
   below the first four declarations is the reset that makes a button look like
   the paragraph it replaced. A `div` with an `onclick` would render the same
   pixels and be unreachable from a keyboard. */
.rd-par__desc {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.45;
  color: var(--rd-fg3);
  margin-top: 6px;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  text-align: left;
  cursor: pointer;
}

/* ⚠️ NO `display` AND NO `overflow` ON THE RULE ABOVE, AND order.txt SAYS WHY
   BY NAME. `.rd-clamp-2` sits at kit-clamp.css and every page section sits
   below it, so a page rule declaring either property on the same element beats
   the utility on source order at equal specificity — `.rd-par__desc` is one of
   the four sites that note lists as having had its clamp declarations removed
   for exactly this reason. The closed state therefore takes its `display` from
   the utility, and the open state — where the utility is not applied at all —
   takes it from here. Two mutually exclusive classes, never both. */
.rd-par__desc--open {
  display: block;
}

/* ── Running ─────────────────────────────────────────────────────────── */

.rd-par__run {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
}

.rd-par__reps {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 96px;
  line-height: .9;
  letter-spacing: -.03em;
  color: var(--rd-fg2);
}

/* The beep has gone. Both the count and the word go accent together — one
   state change, said twice, because at arm's length only one of them is
   legible. */
.rd-par__reps--go { color: var(--rd-acc); }

.rd-par__state {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .16em;
  font-size: 11px;
  color: var(--rd-fg3);
}

.rd-par__state--go { color: var(--rd-acc); }

.rd-par__meta {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 11px;
  color: var(--rd-fg3);
  margin-top: 8px;
}

/* KEPT PAST THE DRAWING. `ScreenDryTimer`'s running frame has no elapsed
   readout at all, and its argument for that is sound at a 1.25 s par — but our
   par goes to 30 s, where a live count is the only thing saying the timer is
   still alive rather than hung. Quiet, below the state word, and small enough
   that it cannot compete with the rep count for the glance the drawing is
   protecting. */
.rd-par__elapsed {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 13px;
  color: var(--rd-fg3);
  margin-top: 2px;
}

.rd-par__foot { flex: none; padding-bottom: 12px; }

/* ── Ready: the numbers, and GO ──────────────────────────────────────── */

.rd-par__nums {
  display: flex;
  gap: 14px;
  margin-top: 15px;
  flex: none;
  flex-wrap: wrap;
}

.rd-par__num-row {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-top: 5px;
}

/* `DryNum`'s value box — *"the same filled-plus-ctrl-boundary treatment the
   Hit Factor time box uses, because it is the same job: a number you tap to
   change."* Ours is stepped rather than tapped (there is no keypad on this
   screen and inventing one is a different item), so the box is a readout
   between two steppers and takes the resting boundary either way. */
.rd-par__num-box {
  min-width: 56px;
  height: 41px;
  padding: 0 11px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  /* `fontSize: 24` on the drawn box (`rd-dryfire.jsx:36`). The height and the
     `0 11px` padding above transcribe the same line; the type size had drifted
     to 22px with nothing recording a reason. */
  font-size: 24px;
  color: var(--rd-fg);
}

.rd-par__step {
  appearance: none;
  border: 0;
  width: 34px;
  height: 34px;
  flex: none;
  padding: 0;
  cursor: pointer;
  background: var(--rd-chip);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 17px;
  color: var(--rd-fg2);
}

.rd-par__step:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

.rd-par__unit {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9px;
  color: var(--rd-fg3);
  flex: none;
}

/* The suggested par is a hint on the field, not a second number in a band
   above it: *"Legacy prints 1.25 twice with nothing saying how they relate."* */
.rd-par__hint {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 10px;
  color: var(--rd-fg3);
  margin-top: 8px;
}

.rd-par__spacer { flex: 1; min-height: 12px; }

.rd-par__go {
  appearance: none;
  border: 0;
  padding: 0;
  background: none;
  width: 100%;
  cursor: pointer;
  display: block;
}

.rd-par__go:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 3px; }

.rd-par__go-inner {
  height: 149px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  box-sizing: border-box;
}

.rd-par__go-word {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 900;
  font-stretch: 62%;
  letter-spacing: .02em;
  font-size: 52px;
  line-height: .9;
  color: var(--rd-on-volt);
}

.rd-par__go-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .14em;
  font-size: 8.5px;
  color: var(--rd-on-volt);
  opacity: .65;
}

/* ── UNDRAWN: randomize par ──────────────────────────────────────────── */

/* `ScreenDryTimer` has no randomize control — its window is the par, full
   stop. Ours has shipped one since the par-timer-randomize item, it changes
   what the beeper does, and a re-skin does not get to delete a working
   setting. It renders as the quietest row on the screen and it says what it
   does, which the shipped bare checkbox did not. */
.rd-par__opt {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  margin-top: 12px;
  padding-top: 11px;
  border-top: 1px solid var(--rd-line);
  flex: none;
}

.rd-par__opt-copy { flex: 1; min-width: 0; }

.rd-par__opt-name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 11px;
  color: var(--rd-fg);
}

.rd-par__opt-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 10px;
  color: var(--rd-fg3);
  margin-top: 3px;
}

.rd-par__opt-btn {
  appearance: none;
  border: 0;
  background: none;
  padding: 0;
  flex: none;
  cursor: pointer;
}

.rd-par__opt-btn:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

.rd-par__opt-btn-inner {
  height: 29px;
  padding: 0 11px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-fg2);
}

.rd-par__opt-btn-inner--on { color: var(--rd-acc); }

/* ---------------------------------------------------------------------------
   §T · THE TABLET RENDERING

   Drawn: **`10 (Tablet) Dry Fire`** — component `ScreenTabDryFire`; doc rows
   "The List Gains Something" and "The Timer Gains Nothing". Rust:
   `pages/par_timer.rs`, the `DeviceClass::Tablet` arm.

   `.rd-par-tab__*` is disjoint from `.rd-par__*` above, same reason s11.css's
   own §T states it: one screen on two devices, one owner, one lane — where is
   the par timer painted has one answer. The pre-run arm REUSES the phone's
   `.rd-par__nums` / `.rd-par__opt` / `.rd-par__go` rules unchanged (the module
   doc's §5 — those controls are undrawn on both devices and the tablet
   drawing supplies no alternative for them), so only the running screen and
   the pre-run screen's own new facts/honesty rows get a tablet prefix here.

   ⚠️ TYPE FLOORS. `01 Foundations` §05: 11px mixed-case, 9.5px tracked
   uppercase — iPad pixels run larger than the phone's 13px physical floor.
   Every value below clears both.

   ⚠️ THE RUNNING BLOCK IS THE FENCE THIS SLICE CLOSES. `RdTabletShell` mounts
   with `rail: None` for this one state — see `par_timer.rs`'s module doc §4
   for the prose-versus-frame ruling. Nothing here assumes a rail; the
   `.rd-par-tab__run` wrapper fills the WHOLE content region (there is no
   `.rd-tshell__col` competing with a rail for width) and centres on both
   axes, because there is no header either.
   --------------------------------------------------------------------------- */

/* ── The pre-run screen: one centred column, same measure as the phone ─── */

.rd-par-tab__solo {
  flex: 1;
  min-width: 0;
  box-sizing: border-box;
  display: flex;
  justify-content: center;
  padding: 24px 18px 32px;
  overflow-y: auto;
}

.rd-par-tab__solo-inner {
  width: 100%;
  max-width: 420px;
  display: flex;
  flex-direction: column;
}

.rd-par-tab__desc {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  line-height: 1.5;
  color: var(--rd-fg3);
  margin-bottom: 14px;
}

/* The stat row — Par alone (`par_timer.rs`'s module doc §3: Last Session and
   Best are the drawing's fixtures, and this page fetches neither). A `flex`
   row rather than one bare figure so a second real fact never has to change
   the wrapper's shape if this page ever learns one. */
.rd-par-tab__facts {
  display: flex;
  gap: 18px;
  margin-bottom: 14px;
}

.rd-par-tab__fact { flex: none; }

.rd-par-tab__fact-lab {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

.rd-par-tab__fact-val {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 22px;
  line-height: 1.2;
  margin-top: 3px;
  color: var(--rd-fg);
}

/* ⚠️ `.rd-par-tab__honesty` LIVED HERE and is deliberately gone. The drawn
   ruling is *"The device says so, in the detail pane, once."*
   (`10 (Tablet) Dry Fire.html:19`) — this part painted a SECOND copy on the
   tablet pre-run timer, so the shooter was told the same thing on two
   consecutive screens. The one drawn home is the two-pane list's detail pane
   (`.rd-dry-t__device`, `s10-dry.css`), and the sentence itself is
   `DEVICE_HONESTY` in `pages/dry_fire.rs`. Recorded rather than silently
   deleted: a selector that reappears here is the drift coming back. */

/* ── The running screen: no rail, no header, one number ──────────────── */

.rd-par-tab__run {
  flex: 1;
  min-width: 0;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 32px;
}

.rd-par-tab__kicker {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 900;
  font-stretch: 62%;
  letter-spacing: .02em;
  font-size: 16px;
  line-height: 1.1;
  color: var(--rd-fg);
  text-align: center;
}

.rd-par-tab__kicker-meta {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  color: var(--rd-fg3);
  margin-bottom: 6px;
}

/* THE ONE NUMBER — sized to read from a bench, not a desk. Bigger than the
   phone's 96px (`.rd-par__reps`): the tablet sits further from the eye than a
   phone propped in a pocket, and this is the whole of the screen's argument
   for existing rail-less at all.

   200px is the drawn figure — `fontSize: 200` on the running frame's count
   (`rd-tablet-solo.jsx:205`). It had shipped at 168px with no note saying why,
   and the doc's own *"bigger does not make it better"* note
   (`10 (Tablet) Dry Fire.html:57`) argues against inflating PAST the drawing,
   not against reaching it. */
.rd-par-tab__reps {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 200px;
  line-height: .9;
  letter-spacing: -.03em;
  color: var(--rd-fg2);
}

.rd-par-tab__reps--go { color: var(--rd-acc); }

.rd-par-tab__state {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .16em;
  font-size: 13px;
  color: var(--rd-fg3);
}

.rd-par-tab__state--go { color: var(--rd-acc); }

/* KEPT PAST THE DRAWING, same reason as the phone's own `.rd-par__elapsed`:
   a 30 s par needs something saying the timer is alive. Quiet, below the
   state word, and nowhere near the rep count's size. */
.rd-par-tab__elapsed {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 15px;
  color: var(--rd-fg3);
  margin-top: 2px;
}

/* Stop's box. `<div style={{ width: 300, marginTop: 20 }}><TBtn kind="d">`
   (`rd-tablet-solo.jsx:207`) — *"full width in a 300px column, not stretched
   across the surface"* (`10 (Tablet) Dry Fire.html:57`). The button fills its
   box, which is `RdButton`'s own default; the 300 is the box, exactly the
   shape `.rd-dry-t__act` (`s10-dry.css:367`) already uses for the drawn
   `width: 300` on Start.

   ⚠️ THE WIDTH ONLY. The drawn `kind="d"` is danger-toned and the phone
   document rules the same control outlined — *"STOP is outlined, not
   danger-coloured"* (`10 (Mobile) Dry Fire.html:153`). Two documents, one
   control, opposite rulings; the tone is held for the designer (audit R7 doc
   10, Q1) and the build stays Outline on both devices until it is ruled. */
.rd-par-tab__foot {
  flex: none;
  margin-top: 18px;
  width: 300px;
  max-width: 100%;
}
/* ==== S10-DRY · 10 Dry Fire — the drill list (phone + tablet) · owner: 1-track3 ==== */
/*
   `/dry-fire` (`pages/dry_fire.rs`). Drawn: **10 Dry Fire**, `ScreenDryList`
   (`rd-dryfire.jsx:46-88`) on the phone, and the chrome of `ScreenTabDryFire`
   (`rd-tablet-solo.jsx:198-247`) on the tablet.

   Batch 19. §1 and §2 are slice S1's phone list; **§T is slice S4's drawn
   two-pane tablet screen** and replaced S1's single-column stand-in. The Done
   screen is S3's and appends its own section; the running screen is not here
   and never will be — it is `s10-par.css`'s, which owns the par timer.

   NAME SPACE: `.rd-dry*`. Checked against the whole pool on the day this part
   landed — `grep -rn 'rd-dry' apps/web/styles/` returned nothing anywhere, and
   the neighbour that shares the document (`s10-par.css`) owns `.rd-par*`. So no
   name here contends with a name anywhere else, this part re-orders no existing
   pair, and nothing it declares is decided by its position in `order.txt`. The
   two parts split one drawn document by SCREEN, which is the same split
   `48 Kit Rules` makes: the list is a destination and the timer is an activity.

   ⚠️ NO WIDTH `@media`. THE DEVICE IS CHOSEN IN RUST, by `use_device_class()`,
   once per screen and above the branch (`pages/dry_fire.rs`). A width query
   here would be a SECOND answer to the same question, invisible from the page
   and disagreeing with it at exactly the widths nobody tests. `s55-sns.css`'s
   banner argues this at length and the argument is unchanged.

   ⚠️ NO `border-radius`. Every corner the rows draw is the kit row's, and the
   kit row's corner is a chamfer. This part draws no box of its own at all — it
   spaces things and it types two numbers.

   ⚠️ THE ROW ITSELF IS `kit-row.css`'s, NOT THIS PART'S. `RdRowLink` supplies
   the ground, the padding, the chamfer, the name face and the sub-line face;
   what is below is only the two things spec 16 §9.1 rules are per-screen slots
   mounted into `.rd-row__trail`. A rule here that restated a row's own geometry
   would be the drift the primitive exists to stop.
*/

/* ── §1 · The phone screen ──────────────────────────────────────────────── */

/* The drawn `paddingTop: 6` on the content column (`rd-dryfire.jsx:49`). */
.rd-dry {
  padding-top: 6px;
}

/* `marginTop: 7` under the H1 (`rd-dryfire.jsx:51`). The face and the size are
   `RdText`'s; only the gap is the screen's. */
.rd-dry__lede {
  margin-top: 7px;
}

/* `marginTop: 13` above the list (`rd-dryfire.jsx:57`).

   ⚠️ NO `border-bottom` HERE, and the drawing has one. `rd-dryfire.jsx:59`
   separates its rows with a hairline and suppresses it on the last; the kit row
   is a carded row with its own ground and its own gap, and painting a rule
   between two cards would be a second separator over the one the primitive
   already draws. The drawn hairline belongs to a drawn row that is not a card —
   recorded for Design rather than reproduced here. */
.rd-dry__rows {
  margin-top: 13px;
}

/* The skeleton takes the list's own place, so the screen does not jump when the
   rows arrive. `43 Loading States`: the stack claims "a list goes here", never
   a count. */
.rd-dry__skel {
  margin-top: 13px;
}

/* ── §2 · The row's right edge — a slot, not a row rule ─────────────────── */

/* `<div style={{ flex: 'none', textAlign: 'right' }}>` (`rd-dryfire.jsx:65`):
   the par over its label, hard against the row's trailing edge.

   Mounted into `.rd-row__trail` by `RdRowLink`'s `trail` slot — one of the
   per-screen treatments spec 16 §9.1 keeps as slots. Marks and words only:
   there is no control in here, because the whole row is the anchor. */
.rd-dry__edge {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  flex: none;
}

/* `{ ...mono, fontSize: 15, color: t.fg2 }` — the drawn par, two decimals.
   `tabular-nums` is what makes a column of them line up; the drawing gets that
   from its mono face and this app gets it from the numeral set, which is the
   same call `s10-par.css` makes for the rep count. */
.rd-dry__par {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-stretch: 75%;
  font-size: 15px;
  line-height: 1;
  color: var(--rd-fg2);
}

/* `{ ...tdisp(700, 78, '.1em'), fontSize: 8.5, color: t.fg3, marginTop: 2 }` —
   the word under the number. It is a LABEL and not a unit: the drawing spells
   it `Par` in title case, and the display face is uppercased. */
.rd-dry__parlab {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8.5px;
  line-height: 1;
  color: var(--rd-fg3);
  margin-top: 2px;
}

/* ── §T · The tablet — `ScreenTabDryFire` (`rd-tablet-solo.jsx:198-247`) ─── */

/* Slice S4. S1's single-column stand-in (`rd-dry__tablet`, one declaration,
   shaped so that replacing it would be a deletion — and its selector is not
   spelled with its dot here, because `pages/dry_fire.rs` asserts this file
   declares no rule for it) is gone: the drawn screen is
   `TabPanes listW={360}` and that is `RdPanes { width: ListWidth::Medium }`,
   whose own docs already record 360 as Dry Fire's number, citing this frame.

   ⚠️ NO GUTTER AND NO WRAPPER. `RdPanes` renders a FRAGMENT and its two panes
   already scroll, carry the fixed width and draw the divider rule
   (`kit-tablet-panes.css`). Everything below is what goes INSIDE a pane, which
   is the caller's per-screen half by that primitive's own contract — a rule
   here that restated a pane's geometry would be the drift the primitive exists
   to stop. The `-t__` infix keeps the two devices' names apart inside one
   `.rd-dry*` space; §1's names are the phone's and are not reused. */

/* The list pane's row stack. No padding of its own — the drawn rows carry
   `padding: '12px 16px'` each, so the hairline between them runs the full pane
   width as drawn. */
.rd-dry-t__rows {
  display: flex;
  flex-direction: column;
}

/* The skeleton and the two empty states, inset so they do not sit against the
   pane's own divider rule. */
.rd-dry-t__state {
  padding: 18px 16px;
}

/* `{ minHeight: 66, padding: '12px 16px', borderBottom: 1px line,
   borderLeft: 3px solid transparent }` (`rd-tablet-solo.jsx:217`).

   ⚠️ A `<button>`, so the reset is this rule's job: the drawn row is a div in a
   frame that has no keyboard. `text-align: left` and the transparent ground are
   what a UA stylesheet would otherwise decide. No `border-radius` — the 3px
   left edge is a selection MARK and a chamfer on it would read as a chip. */
.rd-dry-t__row {
  display: flex;
  flex-direction: column;
  gap: 3px;
  width: 100%;
  min-height: 66px;
  box-sizing: border-box;
  padding: 12px 16px;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  border-left: 3px solid transparent;
  background: transparent;
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
}

/* `borderLeft: 3px solid acc, background: surf2` on the selected row
   (`rd-tablet-solo.jsx:217`). The accent is a 3px rule and nothing else is
   recoloured: the name stays `fg`, which is what keeps a selected row a
   selected row rather than a lit control. */
/* ⚠️ `--rd-acc` AND NOT `--rd-volt`: the token pair splits on ROLE, not on
   screen — *"a border is a stroke, so it is --rd-acc"* (`_legacy-tokens.css`).
   `--rd-accent` is not a token in this app at all; it exists only as two
   flagged misspellings in `s04-author.css`, which fall back to `currentColor`
   and were never the accent they claimed. */
.rd-dry-t__row--sel {
  border-left-color: var(--rd-acc);
  background: var(--rd-surf2);
}

/* `{ display: 'flex', justifyContent: 'space-between', gap: 10 }`
   (`rd-tablet-solo.jsx:218`) — the name, and the par hard against the trailing
   edge. */
.rd-dry-t__row-top {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
}

/* `{ ...tdisp(700, 78, '.01em'), fontSize: 13, color: t.fg }`.

   `tdisp` carries `textTransform: 'uppercase'` in its own definition
   (`rd-train-kit.jsx:29`), so the transform is the drawing's and not a
   decoration added here — the same reading `.rd-tdrills__name` takes. */
.rd-dry-t__name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .01em;
  font-size: 13px;
  line-height: 1.25;
  color: var(--rd-fg);
  min-width: 0;
}

/* `{ ...mono, fontSize: 12, color: t.fg2, flex: 'none' }` — the row's par. Two
   decimals typed by the page; `tabular-nums` is what lines a column of them up,
   the same call §2 makes for the phone. */
.rd-dry-t__rowpar {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-stretch: 75%;
  font-size: 12px;
  line-height: 1;
  color: var(--rd-fg2);
  flex: none;
}

/* `{ ...tbody, fontSize: 10.5, color: t.fg3, marginTop: 4, ...tclamp(1) }` —
   the description. The one-line clamp is `.rd-clamp-1`, applied at the call
   site, because the clamp is the kit's utility and not this screen's rule.

   ⚠️ 11px AND NOT THE DRAWN 10.5. `01 Foundations.html:133`: *"Mixed-case text
   never goes below 11px on the tablet… Where an existing tablet drawing shows
   mixed-case below 11, build 11: the scale outranks the drawing."* The same
   raise `.rd-tdrills__sub` makes on the same drawn number.

   ⚠️ AND NO `display`/`overflow` HERE. `kit-clamp.css` warns that a page rule
   declaring either at equal specificity wins the tie and silently unclamps the
   line. Type and colour only. */
.rd-dry-t__why {
  font-size: 11px;
  line-height: 1.4;
  color: var(--rd-fg3);
}

/* `{ ...tbody, fontSize: 10, color: t.fg3, marginTop: 3 }` — the last session.

   ⚠️ `fg3` AND NOT THE ACCENT, where the phone row's same line is drawn on the
   accent (`rd-dryfire.jsx:63`). The drawn tablet row is the quieter one because
   the accent on this device is already spent on the 3px selection edge two
   pixels to its left, and two accents in one row is a row with no emphasis.
   Transcribed, not harmonised.

   ⚠️ 11px and not the drawn 10, per the same §05 floor as the line above. */
.rd-dry-t__last {
  font-size: 11px;
  line-height: 1.4;
  color: var(--rd-fg3);
}

/* `{ padding: '22px 28px', display: 'flex', flexDirection: 'column', gap: 16 }`
   (`rd-tablet-solo.jsx:227`). */
.rd-dry-t__detail {
  display: flex;
  flex: 1;
  min-height: 0;
  flex-direction: column;
  gap: 16px;
  padding: 22px 28px;
  box-sizing: border-box;
}

.rd-dry-t__head {
  display: flex;
  flex-direction: column;
}

/* `{ ...tdisp(900, 68, '.01em'), fontSize: 24, color: t.fg }`.

   ⚠️ font-stretch 62, NOT the drawn 68 — the identical call `train.css` and
   `profile-community.css` already make, with the same citation. 68 is outside
   `ALLOWED_STRETCH` (62, 72, 75-78, 100) and `rangeday check css` refuses it by
   design: *"a new width is a designer decision, not a call-site choice."* 62 is
   the set's member for this size band — *"62 is right at 30px and slightly too
   tight below about 18px"* — and this title is 24px, the 30px end of it. */
.rd-dry-t__title {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 900;
  font-stretch: 62%;
  letter-spacing: .01em;
  font-size: 24px;
  line-height: 1.1;
  color: var(--rd-fg);
  margin: 0;
}

/* `{ ...tbody, fontSize: 13, color: t.fg2, marginTop: 9, lineHeight: 1.55,
   maxWidth: '52ch' }` — the measure is drawn and it is the point of the pane. */
.rd-dry-t__body {
  font-size: 13px;
  line-height: 1.55;
  color: var(--rd-fg2);
  max-width: 52ch;
  margin: 9px 0 0;
}

/* `{ display: 'flex', gap: 34 }` (`rd-tablet-solo.jsx:232`). Two metrics where
   the drawing has three — `Best` has no field to read, and the page says so. */
.rd-dry-t__metrics {
  display: flex;
  gap: 34px;
}

.rd-dry-t__metric {
  display: flex;
  flex-direction: column;
}

/* `{ ...tdisp(700, 78, '.12em'), fontSize: 9, color: t.fg3 }`.

   ⚠️ 9.5px and not the drawn 9. The sub-11 exemption is exact: *"Condensed
   uppercase labels with tracking may go to 9.5px"* (`01 Foundations.html:133`),
   and 9.5 is the floor of it, not a range below it. 700/78/.12em at 9.5 is the
   `Tab Label` token verbatim (`01 Foundations.html:131`). */
.rd-dry-t__mlab {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 9.5px;
  line-height: 1;
  color: var(--rd-fg3);
}

/* `{ ...mono, fontSize: 22, color: t.fg, marginTop: 5 }`.

   ⚠️ THE ACCENT ARM OF THE DRAWN TERNARY IS NOT HERE, and it is not an
   omission: `color: k === 'Best' ? t.acc : t.fg` paints exactly one metric on
   the accent, and that metric is the one this screen has no source for. With
   `Best` out, every value left is `fg` and a modifier class would be dead the
   day it landed. */
.rd-dry-t__mval {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-stretch: 75%;
  font-size: 22px;
  line-height: 1;
  color: var(--rd-fg);
  margin-top: 5px;
}

/* `{ ...tbody, fontSize: 11.5, color: t.fg3, lineHeight: 1.55,
   maxWidth: '58ch', marginTop: 'auto' }` — the device-honesty sentence.

   The `margin-top: auto` is the drawn one and it is load-bearing: the sentence
   sits at the FOOT of the pane, above the act, so it is the last thing read
   before the shooter starts. `10 (Tablet) Dry Fire.html:19` rules it there. */
.rd-dry-t__device {
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--rd-fg3);
  max-width: 58ch;
  margin: auto 0 0;
}

/* `<div style={{ width: 300 }}><TBtn dense>` (`rd-tablet-solo.jsx:243`). The
   button fills its box, which is `RdButton`'s own default; the 300 is the box. */
.rd-dry-t__act {
  width: 300px;
  max-width: 100%;
}

/* The nothing-selected sentence. OURS · UNDRAWN — the frame always carries a
   selection, and with the first-row fallback the only way here is an empty
   list, whose list pane is already saying so. Same face as the sentence above
   it, because it is a state and not a heading. */
.rd-dry-t__none {
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--rd-fg3);
  max-width: 58ch;
  margin: 0;
}

/* ==== Done screen ==================================================== */
/*
   `/dry-fire/:drill_id/done` (`pages/dry_fire_done.rs`). Drawn: **10 Dry
   Fire** row 05, `ScreenDryDone` (`rd-dryfire.jsx:149-191`) — batch 19, slice
   S3.

   ⚠️ APPENDED AS ONE BLOCK AT THE FOOT OF THE PART, DELIBERATELY. The list
   sections above were being edited in another worktree while this landed
   (slice S4's two panes), so this slice adds and re-orders nothing: every
   declaration below is a new `.rd-dry-done*` name, and none of them is decided
   by its position in the file or in `order.txt`. No new part — `check css`
   still counts 99.

   NAME SPACE: `.rd-dry-done*`. It shares the `.rd-dry` prefix with the list
   above by DOCUMENT rather than by accident (both are `10 Dry Fire`), and no
   selector here matches a list element: `.rd-dry-done` is a whole class, not
   `.rd-dry` with a modifier, so the two never cascade into one another.

   ⚠️ NO WIDTH `@media`, for the same reason §1 gives — the device is chosen in
   Rust by `use_device_class()`, once, above the branch.
*/

/* The drawn content column (`rd-dryfire.jsx:153`): a flex column that owns the
   whole content region, so the actions can sit at the foot of the screen rather
   than under the copy. `min-height: 0` for the reason `kit-shell.css` states at
   length — a flex child that refuses to shrink walks the controls off the
   bottom on a short viewport. */
.rd-dry-done {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding-top: 4px;
}

/* The tablet mounts the same column inside `RdTabletShell`, whose content
   region supplies no gutter of its own (`kit-tablet-shell.css`: "the screen
   supplies its own insets"). Undrawn on that device — see the page's module
   doc — so the inset matches the list's single-column stand-in rather than
   inventing a second number. */
.rd-dry-done--tablet {
  padding: 18px 22px;
}

/* `<div style={{ display: 'flex' }}>` around the chip (`rd-dryfire.jsx:157`),
   and the drawing explains itself: a bare chip as a direct child of this column
   stretches to the full cross-axis width and stops reading as a chip at all. */
.rd-dry-done__chiprow {
  display: flex;
}

/* `marginTop: 12` above the count block (`rd-dryfire.jsx:158`). */
.rd-dry-done__count {
  margin-top: 12px;
}

/* `{ ...mono, fontSize: 72, color: t.acc, lineHeight: .9, letterSpacing:
   '-.03em' }` — the receipt's one big number (`rd-dryfire.jsx:159`).

   `--rd-acc` and never `--rd-volt`: substrate §1.1 binds TEXT to acc, and volt
   is the accent as a fill. `tabular-nums` for the same reason `.rd-dry__par`
   takes it — this app's numerals come from the numeral set where the drawing's
   come from a mono face.

   ⚠️ 72px HERE AND 96px ON THE TIMER (`s10-par.css`, `.rd-par__reps`), and the
   difference is the drawing's. The running count is read from a bench with a
   gun in your hands; this one is read while holding the phone. */
.rd-dry-done__reps {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 72px;
  line-height: .9;
  letter-spacing: -.03em;
  color: var(--rd-acc);
}

/* `{ ...tdisp(800, 78, '.14em'), fontSize: 11, color: t.fg2, marginTop: 6 }` —
   `Reps at 1.25 s` (`rd-dryfire.jsx:160`). A label role: uppercased by the
   type, not by the string. */
.rd-dry-done__repslab {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .14em;
  font-size: 11px;
  line-height: 1.2;
  color: var(--rd-fg2);
  margin-top: 6px;
}

/* `marginTop: 18` above the stat box (`rd-dryfire.jsx:162`). The box itself is
   `RdChamferBox` — every stroked chamfer routes through it (`48 Kit
   Rules.html:33`), and its geometry is the primitive's, never a radius here. */
.rd-dry-done__box {
  margin-top: 18px;
}

/* The box's inner grid: `padding: '13px 15px', display: 'grid',
   gridTemplateColumns: '1fr 1fr', gap: 12` (`rd-dryfire.jsx:163`).

   ⚠️ TWO COLUMNS WITH ONE CELL IN THEM TODAY, and that is on purpose. The
   drawn second stat (`Best week`) is HELD with the list's streak footer — one
   product question, one answer, see the page's module doc — and keeping the
   drawn track means the cell arrives without this rule moving. A one-column
   grid would put `Session` in the middle of the box, which the drawing never
   does. */
.rd-dry-done__stats {
  padding: 13px 15px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

/* `{ ...tbody, fontSize: 11, color: t.fg3, marginTop: 14, lineHeight: 1.5,
   textWrap: 'pretty' }` — the receipt sentence (`rd-dryfire.jsx:178`). The face
   and the size are `RdText`'s; the gap and the ragging are the screen's. */
.rd-dry-done__receipt {
  margin-top: 14px;
  line-height: 1.5;
  text-wrap: pretty;
}

/* `<div style={{ flex: 1, minHeight: 10 }} />` (`rd-dryfire.jsx:183`) — the
   drawn spacer that plants the two buttons at the foot of the screen. */
.rd-dry-done__gap {
  flex: 1;
  min-height: 10px;
}

/* `{ flex: 'none', paddingBottom: 12, display: 'grid', gap: 8 }`
   (`rd-dryfire.jsx:184`): `Go Again` over `Done`, full width, in the drawn
   order. Order is the markup's, not this rule's — the primary is the first
   thing a thumb reaches from the bottom. */
.rd-dry-done__actions {
  flex: none;
  padding-bottom: 12px;
  display: grid;
  gap: 8px;
}
/* ==== S11 · 11 Hit Factor · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   `/training/hit-factor` (`pages/hit_factor.rs`), tab 3. Drawn: **11 Hit
   Factor** — `HFResult`, `ScreenHitFactor`'s foot, `ScreenHFSave`'s field and
   `ScreenHFLog`'s rows.

   ⚠️ THE RULE FOR THIS SCREEN IS A HIERARCHY, NOT A PAINT. The drawing states
   it in one line — *"the number is the screen. It is at the top, it is the
   biggest thing, and it updates as you type"* — and the shipped page failed it
   structurally, not cosmetically: the result rendered BELOW the inputs, only
   once a hit factor existed, in a grey box a third the size of the Save
   button. So the only rule in this file that matters is that `.rd-hf__value`
   is the largest thing on the page and the first thing under the shell.

   The zone steppers and the time field are NOT here. They are `ScoreInputs`
   (`.rd-scorein__*`, lane-3's SCORE-INPUTS block in adm0-picker.css), shared
   with the two scoring screens, and a calculator that styled them privately
   would give one of three callers a different control for the same job.

   ⚠️ THE RESULT NEVER LEAVES THE SCREEN, so it never conditionally renders.
   `--rd-fg3` and an em-dash are what "no time yet" looks like; a result that
   appears when it becomes computable is a result you have to hunt for, and it
   is the reason the shipped page could hold a hit factor and not show one.

   ⚠️ WHAT IS NOT HERE. The drawn keypad (`HFPad`) belongs to a zone count you
   tap, and our zone counts are `ScoreInputs`' steppers — a keypad without the
   field that opens it is a control with nothing behind it. Flagged on the page
   rather than half-built here.
   --------------------------------------------------------------------------- */

.rd-hf {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  padding-top: 8px;
}

/* ── The result ──────────────────────────────────────────────────────── */

.rd-hf__result { flex: none; }

.rd-hf__result-row {
  display: flex;
  align-items: flex-end;
  gap: 12px;
}

.rd-hf__result-main { flex: 1; min-width: 0; }

/* 54px, the drawn size, and tabular figures because the value changes under
   the reader on every stepper press — proportional digits move the decimal
   point sideways while you are watching it. */
.rd-hf__value {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 54px;
  line-height: .95;
  letter-spacing: -.02em;
  margin-top: 2px;
  color: var(--rd-acc);
}

/* No time yet. Not an error — a hit factor without a time is a division that
   has not happened, and the line beneath says so in words. */
.rd-hf__value--none { color: var(--rd-fg3); }

/* Penalties outweigh the hits. The legacy app rendered a large positive number
   for this, which is the failure the whole result block exists to prevent. */
.rd-hf__value--under { color: var(--rd-danger); }

.rd-hf__points {
  flex: none;
  text-align: right;
  padding-bottom: 4px;
}

.rd-hf__points-lab {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

.rd-hf__points-val {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 19px;
  line-height: 1.1;
  color: var(--rd-fg);
}

.rd-hf__points-val--under { color: var(--rd-danger); }

/* The intermediate, spelled out. Points over time is the whole definition, and
   showing it turns a number you trust into a number you can check. */
.rd-hf__working {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
  margin-top: 7px;
}

/* ── Save ────────────────────────────────────────────────────────────── */

.rd-hf__save {
  flex: none;
  margin-top: 16px;
  padding-top: 11px;
  border-top: 1px solid var(--rd-line);
}

.rd-hf__save-lab {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 10.5px;
  color: var(--rd-acc);
}

/* Unreachable saving reads as broken, so the label goes quiet with the button
   when there is no time to divide by. */
.rd-hf__save-lab--off { color: var(--rd-fg3); }

/* The drawn count under the label — `tbody 10 / fg3 / marginTop 3`
   (`rd-hitfactor.jsx:191`). The rest of that drawn line ("· tap to see them")
   is a signpost to a pane that is already below; the count is a fact. */
.rd-hf__save-count {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 10px;
  color: var(--rd-fg3);
  margin-top: 3px;
}

.rd-hf__save-field { margin-top: 8px; }
.rd-hf__save-banner { margin-top: var(--rd-banner-gap); }
.rd-hf__save-act { margin-top: 10px; }

/* ── The log ─────────────────────────────────────────────────────────── */

.rd-hf__log { margin-top: 22px; }

.rd-hf__log-empty {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 14px;
}

.rd-hf__log-empty-lead {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  line-height: 1.5;
  color: var(--rd-fg2);
  margin: 0;
}

.rd-hf__log-empty-body {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.5;
  color: var(--rd-fg3);
  text-wrap: pretty;
  margin: 0;
}

.rd-hf__log-list { margin-top: 12px; }

.rd-hf__row {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 54px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-hf__row--last { border-bottom: none; }

.rd-hf__row-copy { flex: 1; min-width: 0; }

.rd-hf__row-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rd-hf__row-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 10px;
  color: var(--rd-fg3);
  margin-top: 3px;
}

/* The value column. Tabular for the same reason the hero is: a list of hit
   factors is read by comparing the columns, and only tabular figures line the
   decimal points up. */
.rd-hf__row-val {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 19px;
  color: var(--rd-fg);
  flex: none;
}

/* `numc`'s one rule, and the corpus states its reason: a placeholder is not a
   number, so it must not be painted like one. */
.rd-hf__row-val--none { color: var(--rd-fg3); }

.rd-hf__row-acts {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
}

.rd-hf__row-act {
  appearance: none;
  background: none;
  border: 0;
  padding: 4px 2px;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9px;
  color: var(--rd-fg3);
}

.rd-hf__row-act:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

.rd-hf__row-act--danger { color: var(--rd-danger); }

.rd-hf__row-edit {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  width: 100%;
  padding: 8px 0;
}

.rd-hf__row-edit-field { flex: 1; min-width: 0; }

/* ---------------------------------------------------------------------------
   §T · THE TABLET RENDERING

   Drawn: **`11 (Tablet) Hit Factor`** — component `ScreenTabHitFactor`; doc rows
   01 "Calculator Left, Log Right" and 02 "Nothing Saved". Rust:
   `pages/hit_factor.rs`, the `DeviceClass::Tablet` arm.

   ⚠️ A SECOND BLOCK IN THIS PART, NOT A SECOND PART FILE, and the choice is
   deliberate. Every earlier split in `order.txt` (s14-add / s14-chal /
   s14-person, s02-boot / s02-auth, s15 / s15-stand) splits one drawing across
   two SCREENS, usually two owners. This is one screen on two devices: same
   route, same page file, same owner, same lane. `.rd-hf-tab__*` is disjoint from
   `.rd-hf__*` above — neither declares nor overrides a selector the other names
   — so the two do not contend, and keeping them together means "where is Hit
   Factor painted" has one answer. A later tablet page SHOULD split instead when
   its tablet rendering has a different owner, or when it has no phone twin.

   ⚠️ THE COLUMN'S WIDTH IS NOT HERE. It is `ListWidth::Wide` (400px) in
   `kit-tablet-panes.css`, because the calculator is `RdPanes`' list pane. The
   drawing's whole argument for this screen depends on that number staying fixed
   — "a numeric pad spread across 600px is further to travel per digit. It keeps
   phone width in a 400px column and the log takes the rest" — so it lives with
   the primitive that fixes it and not in a page rule that could quietly grow.

   ⚠️ TYPE FLOORS. `01 Foundations` §05 rules the tablet scale: 11px mixed-case,
   9.5px tracked uppercase, because iPad pixels run about 16% larger than the
   phone's physical 13px floor. Three drawn values here sit under the tracked
   floor (9, 9, 8.5) and are raised to 9.5 — the same correction
   `kit-tablet-rail.css` records for its 10px sublines. Everything else is the
   drawn number.

   ⚠️ `--rd-ctrl` BOUNDS, `--rd-line` DIVIDES. The shared content file makes that
   a ruling for this page specifically: three drafts of the time box failed
   because `line` was used for a thing you can tap ("that is the DIVIDER token,
   so the mark saying 'tap me' was drawn in the same ink as the passive hairlines
   above it, 1.4:1"). Every cell and every key here is tappable, so every one of
   them is `ctrl`.
   --------------------------------------------------------------------------- */

/* The calculator column, inside `.rd-tpanes__list`. `min-height: 100%` is what
   lets the foot's `margin-top: auto` reach the bottom of the pane rather than
   the bottom of the content. */
.rd-hf-tab__calc {
  box-sizing: border-box;
  min-height: 100%;
  padding: 20px 24px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* ── The figure ──────────────────────────────────────────────────────── */

.rd-hf-tab__fig { flex: none; }

.rd-hf-tab__fig-lab {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* 46px, the drawn tablet size — smaller than the phone's 54 because it shares
   its column with the pad rather than owning the width. The numeric role
   (Archivo 800 / 75 / tabular) and the accent are unchanged: it is still the one
   number that IS the screen. */
.rd-hf-tab__fig-val {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 46px;
  line-height: 1;
  letter-spacing: -.02em;
  margin-top: 6px;
  color: var(--rd-acc);
}

.rd-hf-tab__fig-val--none { color: var(--rd-fg3); }
.rd-hf-tab__fig-val--under { color: var(--rd-danger); }

.rd-hf-tab__fig-work {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 11.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
  margin-top: 6px;
}

/* ── The pad's targets: the zone cells, and the time ─────────────────── */

.rd-hf-tab__cells {
  flex: none;
  display: flex;
  gap: 8px;
}

/* 52px is the drawn cell height and it clears the 44px touch minimum with room
   to spare. ⚠️ It does not shrink because a mouse might be present — "the same
   build serves an iPad and a laptop browser, and there is no way to know which
   is in front of you". */
.rd-hf-tab__cell {
  appearance: none;
  flex: 1;
  min-width: 0;
  min-height: 52px;
  padding: 4px 2px;
  border: 1px solid var(--rd-ctrl);
  background: none;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
}

/* The time target. Full width and its own row: it is not a zone, and putting it
   in the zone row would make the count of cells disagree with the count of
   zones. */
.rd-hf-tab__cell--wide {
  flex: none;
  width: 100%;
}

/* The kit's own selection idiom, borrowed from the rail's lit row: an `acc`
   edge over a `surf2` ground. ⚠️ --rd-acc, never --rd-volt — this is a BORDER,
   and volt yields an unreadable tan rule in light theme. */
.rd-hf-tab__cell--on {
  border-color: var(--rd-acc);
  background: var(--rd-surf2);
}

.rd-hf-tab__cell:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

.rd-hf-tab__cell-n {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 17px;
  line-height: 1.1;
  color: var(--rd-fg);
}

/* Drawn `n ? t.fg : t.fg3`. A zero count is quiet so the eye lands on the zones
   that were actually hit — and the em-dash the time cell shows before anything
   is typed is a placeholder, which `numc`'s one rule says must not be painted
   like a number. */
.rd-hf-tab__cell-n--zero { color: var(--rd-fg3); }

.rd-hf-tab__cell-z {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* ── The keypad ──────────────────────────────────────────────────────── */

/* THREE COLUMNS, INSIDE THE FIXED COLUMN. No width of its own: the pad is as
   wide as the 400px pane leaves it and never wider, which is the drawing's
   stated reason this screen works at all. */
.rd-hf-tab__pad {
  flex: none;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}

.rd-hf-tab__key {
  appearance: none;
  min-height: 52px;
  border: 1px solid var(--rd-ctrl);
  background: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 18px;
  color: var(--rd-fg2);
}

/* Del. A WORD, not a glyph — "the font has no glyph for [⌫], and a
   missing-glyph box on the one key that undoes a mistyped digit is worse than
   any word". So it takes the tracked-uppercase role rather than the numeric
   one, and goes quiet beside eleven figures. */
.rd-hf-tab__key--word {
  font-variant-numeric: normal;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 11px;
  color: var(--rd-fg3);
}

.rd-hf-tab__key:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* ── The foot ────────────────────────────────────────────────────────── */

/* `margin-top: auto`, as drawn: the pad sits under the figure and the save
   pins to the bottom, so the column does not rearrange as the log fills. */
.rd-hf-tab__foot {
  flex: none;
  margin-top: auto;
  padding-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.rd-hf-tab__name { flex: none; }
.rd-hf-tab__save-banner { flex: none; }

/* ── The log ─────────────────────────────────────────────────────────── */

/* Inside `.rd-tpanes__detail`, which owns the scroll and declares no padding of
   its own. The drawn inset is 20/26 — wider than the calculator's 24 because
   this pane has no border on its far side. */
.rd-hf-tab__log {
  box-sizing: border-box;
  padding: 20px 26px;
}

.rd-hf-tab__log-head {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-fg3);
  margin-bottom: 12px;
}

/* Row 02. ONE sentence, and it is allowed to be a paragraph rather than a lead
   and a body: on the phone the same argument has to be made on a screen you
   navigated to, so it is split; here it sits beside the calculator it is about.
   50ch is the drawn measure. */
.rd-hf-tab__log-empty {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  line-height: 1.6;
  color: var(--rd-fg2);
  max-width: 50ch;
  text-wrap: pretty;
  margin: 0;
}

.rd-hf-tab__log-list { margin-top: 0; }
.rd-hf-tab__log-banner { margin-top: var(--rd-banner-gap); }

/* ── The tablet log ROW ───────────────────────────────────────────────────
   The row markup is `SavedRow`, shared with the phone verbatim — a second
   implementation of two RPCs is how one of them quietly stops working. So the
   tablet's geometry is an OVERRIDE on the phone's classes, scoped by the list
   wrapper, and not a second row class: the drawn difference is five numbers and
   a colour, and a parallel class would be five numbers plus a whole markup able
   to drift.

   Drawn (`rd-tablet-solo.jsx:137-142`): `minHeight: 58`, `gap: 18`,
   `padding: '11px 0'`; name `tbody 13` in `t.fg`; sub `tbody 10.5` in `t.fg3`;
   value `mono 19` in `t.fg2`. The phone's row is 54 / 11 / 12.5 / 10 / `fg`.

   ⚠️ TWO DEVIATIONS, BOTH THIS FILE'S OWN RULES:
   - The sub-line is **11px, not the drawn 10.5**. §T's floor above is `11px
     mixed-case`, and 10.5 is under it — the same correction the three tracked
     values in this block already take to 9.5. The phone's 10px is legal on the
     phone scale and illegal here.
   - The sub-line drops the phone's `800 / 75%` condensed face. The drawn tablet
     sub is `tbody`, the same plain body role `.rd-hf-tab__log-empty` takes;
     the phone's condensed face is not in the tablet drawing at all. */
.rd-hf-tab__log-list .rd-hf__row {
  gap: 18px;
  min-height: 58px;
  padding: 11px 0;
}

.rd-hf-tab__log-list .rd-hf__row-name { font-size: 13px; }

.rd-hf-tab__log-list .rd-hf__row-sub {
  font-weight: 400;
  font-stretch: 100%;
  font-size: 11px;
}

.rd-hf-tab__log-list .rd-hf__row-val { color: var(--rd-fg2); }

/* `numc`'s one rule survives the override: a placeholder is not a number. The
   descendant selector above outranks `.rd-hf__row-val--none`, so the quiet
   colour has to be restated at the same specificity or a decoded-non-finite
   value would paint like a real one. */
.rd-hf-tab__log-list .rd-hf__row-val--none { color: var(--rd-fg3); }
/* ==== S12 · 12 Queue — Didn't Send · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   `/training/queue` (`pages/queue.rs`). Drawn: **12 Queue**, `ScreenQueue`,
   its `QRow`, its `QAct` and its `QSync` foot. The dismiss confirmation is
   `ScreenQueueDismiss`, and it is NOT styled here — it composes
   `RdConfirmModal`, whose panel is already the drawn shape.

   The shell, heading, banner and confirm modal are kit. What is page CSS is
   the refused row (nothing in the kit is a card with an eyebrow, a reason and
   an action strip), the three row actions, the sync foot, and the two states
   the kit's empty state cannot carry.

   ⚠️ WHY THE CLEAR STATE IS NOT `RdEmptyState`. The drawn clear state sets
   BOTH its lines in body type — `No pending scores` at 13/`--rd-fg2` over an
   11/`--rd-fg3` sentence. `RdEmptyState`'s title is a display role, and a
   display role is uppercase by construction of its type helper, so composing
   it here would render the drawing's pinned sentence as NO PENDING SCORES.
   The kit is right and so is the drawing; they are answering different
   screens, and this one asks for the quieter of the two.

   ⚠️ THE ROW ACTIONS ARE OUTLINES AND NEVER FILLS, which is the drawing's own
   rule and its own reason: *"three cards with a volt fill each would put nine
   competing loud things on one screen."* Dismiss takes the danger stroke
   because it destroys a run; the other two do not. They are not `RdButton` —
   the kit's button is a 44+ tall page-level commit and these are 29 tall
   inside a card, three to a row. Their construction is still the kit's
   (`RdChamferBox`, hollow fill, 1.5px stroke); only the geometry is local.

   ⚠️ `.rd-q__held*` IS UNDRAWN AND FLAGGED. `ScreenQueue` holds refusals only.
   The section it styles exists because the surface that was supposed to
   replace it — pending runs marking themselves IN PLACE in History — is not
   built (`pages/score_history.rs`'s own module doc says so). Deleting the
   rows before their new home exists would make every unsent run invisible in
   the whole app. Rendered quiet and last, and it goes when History gains them.
   --------------------------------------------------------------------------- */

.rd-q {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  padding-top: 4px;
}

/* The sentence under the heading. Two strings, one per state — see the page. */
.rd-q__sub {
  margin-top: 6px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

/* ── The clear state ─────────────────────────────────────────────────── */

.rd-q__clear {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 10px;
  padding-bottom: 30px;
}

.rd-q__clear-lead {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg2);
  margin: 0;
}

.rd-q__clear-body {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.5;
  color: var(--rd-fg3);
  text-wrap: pretty;
  margin: 0;
}

/* ── The refusals ────────────────────────────────────────────────────── */

/* The scroll lives here so the sync foot stays reachable with any number of
   refusals above it — the same division `.rd-squad__picks` makes. */
.rd-q__list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  margin-top: 14px;
}

.rd-q__rows {
  display: grid;
  gap: 9px;
}

.rd-q__row-inner {
  padding: 11px 13px 12px;
  box-sizing: border-box;
}

.rd-q__row-kind {
  display: flex;
  align-items: center;
  gap: 7px;
}

/* A 5px square, not a red card. The drawing spends a comment on it: the
   shipped failed row painted itself red on red, *"three of those is a wall
   people bounce off, and the reason is the thing that has to be read."* Its
   two rules are gone from the legacy pool with this change; the selectors are
   deliberately not spelled here, because a comment that names a dead selector
   can satisfy somebody else's rule-body assertion — sx-shared.css records
   that happening. */
.rd-q__row-dot {
  width: 5px;
  height: 5px;
  flex: none;
  background: var(--rd-danger);
}

.rd-q__row-kindtext {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .14em;
  font-size: 8.5px;
  color: var(--rd-fg2);
  flex: 1;
  min-width: 0;
}

.rd-q__row-name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .04em;
  font-size: 12.5px;
  color: var(--rd-fg);
  margin-top: 7px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Tabular figures, because this line is where a run's numbers live when the
   name line cannot hold its drill's name. A queue re-orders as rows are
   dismissed and proportional figures shift under the reader. */
.rd-q__row-preview {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 400;
  font-stretch: 100%;
  font-size: 10px;
  color: var(--rd-fg3);
  margin-top: 3px;
}

.rd-q__row-reason {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--rd-danger);
  margin-top: 7px;
  text-wrap: pretty;
}

.rd-q__row-acts {
  display: flex;
  gap: 6px;
  margin-top: 10px;
}

.rd-q__act {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  flex: none;
}

.rd-q__act:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

.rd-q__act-inner {
  height: 29px;
  padding: 0 11px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-fg2);
}

/* `.rd-q__act-inner--accent` (the phone's Fix control) was deleted with the Fix
   act itself — James ruling 2026-08-29. */
.rd-q__act-inner--danger { color: var(--rd-danger); }

/* The finding, on the screen it belongs to (drawn as the last thing in the
   scroll region, not as a banner): a refused SCORE has no edit route in the
   product, so the only thing the app can offer for one is to throw it away. */
.rd-q__note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.45;
  color: var(--rd-fg3);
  text-wrap: pretty;
  padding-bottom: 2px;
  margin: 0;
}

/* ── The sync foot ───────────────────────────────────────────────────── */

.rd-q__sync {
  flex: none;
  padding-top: 11px;
  padding-bottom: 12px;
  border-top: 1px solid var(--rd-line);
}

.rd-q__sync-banner { margin: 0; }

.rd-q__sync-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  line-height: 1.4;
  color: var(--rd-fg3);
  margin-top: 7px;
  text-align: center;
}

/* ── UNDRAWN: the pending rows, held until History carries them ──────── */

.rd-q__held { margin-top: 18px; }

.rd-q__held-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 6px;
}

.rd-q__held-note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.45;
  color: var(--rd-fg3);
  text-wrap: pretty;
  margin: 0 0 8px;
}

.rd-q__held-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 46px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-q__held-row--last { border-bottom: none; }

.rd-q__held-copy { flex: 1; min-width: 0; }

.rd-q__held-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--rd-fg2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rd-q__held-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 10px;
  color: var(--rd-fg3);
  margin-top: 3px;
}

.rd-q__held-state {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8.5px;
  color: var(--rd-fg3);
  flex: none;
}

/* ---------------------------------------------------------------------------
   THE DOOR, AND IT IS ON A DIFFERENT PAGE. Drawn in **12 Queue**, Stage One —
   the strip at the top of History (`ScreenHistory` with `sync: 'fail'`), which
   is this document's own frame even though it renders inside
   `pages/score_history.rs`. It is styled here rather than beside
   `.rd-history__*` for two reasons that point the same way: the drawing puts it
   in 12 Queue, and `.rd-history__*` is another lane's part.

   ⚠️ IT EXISTS FOR REFUSALS ONLY. *"The strip appears only for refusals. A
   queue state that resolves itself does not get a link to a management
   screen."* There is no pending variant of this block and there must not be
   one — the pending badge that used to sit on the drill list's Queue link
   counted the wrong thing, and it left with that link rather than moving here.

   ⚠️ `Review` IS TEXT-RANK, NOT A BUTTON — *"It is a destination, and History
   already ends in a tab bar."* The whole strip is the link; `.rd-q__door-act`
   is a word inside it, with no border, no fill and no tab stop of its own.
   --------------------------------------------------------------------------- */

.rd-q__door {
  display: block;
  text-decoration: none;
  margin-bottom: 10px;
  -webkit-tap-highlight-color: transparent;
}

.rd-q__door:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* `padding: '10px 12px'`, `gap: 9`, `minHeight: 48`. */
.rd-q__door-row {
  display: flex;
  align-items: center;
  gap: 9px;
  min-height: 48px;
  padding: 10px 12px;
  box-sizing: border-box;
}

/* The 5px danger square. The same mark the refused row carries, and the only
   danger ink on the strip — the copy underneath it does the rest, because a
   refusal that shouts twice reads as an error in the app rather than an answer
   from the server. */
.rd-q__door-mark {
  flex: none;
  width: 5px;
  height: 5px;
  background: var(--rd-danger);
}

.rd-q__door-copy {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}

.rd-q__door-head {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 11px;
  color: var(--rd-fg);
}

.rd-q__door-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
  margin-top: 3px;
}

.rd-q__door-act {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-acc);
  flex: none;
}

/* ---------------------------------------------------------------------------
   §T · THE TABLET RENDERING

   Drawn: **`12 (Tablet) Queue`** — component `ScreenTabQueue`
   (`rd-tablet-solo.jsx:157`). Rust: `pages/queue.rs`, the `DeviceClass::Tablet`
   arm.

   ⚠️ A SECOND BLOCK IN THIS PART, `s11.css`'s own reasoning: one screen, two
   devices, one file, one owner. `.rd-q-tab__*` is disjoint from `.rd-q__*`
   above.

   ⚠️ TYPE FLOORS. `01 Foundations` §05: 11px mixed-case, 9.5px tracked
   uppercase. The drawn kicker (9) and meta line (10.5) both sit under a floor
   and are raised to it.

   ⚠️ TWO ACTION COLOURS, AND DISMISS KEEPS DANGER RED. James ruled the Fix
   control out of this screen on 2026-08-29 ("Fix feels like a way to change
   history"), so the three-way map this block used to carry — Fix accent, Retry
   neutral, Dismiss danger — is down to the two controls the drawing itself
   draws, and `.rd-q-tab__act--fix` went with the ruling. What does NOT follow
   the drawing back is Dismiss's colour: the frame paints Retry in `t.acc` and
   Dismiss in `t.fg3`/`t.ctrl`, and Dismiss keeps danger red here deliberately,
   because it deletes a run that never reached the server — the same stakes
   `.rd-q__act-inner--danger` marks on the phone — and the confirm dialog
   behind it is the gate, not the button's hue. Dropping the colour would be
   dropping a real cue for no reason the drawing states.
   --------------------------------------------------------------------------- */

.rd-q-tab {
  flex: 1;
  min-width: 0;
  padding: 20px 28px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  overflow-y: auto;
  overflow-x: hidden;
}

.rd-q-tab__clear { max-width: 54ch; }

.rd-q-tab__clear-lead {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
  margin: 0;
}

.rd-q-tab__clear-body {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg3);
  line-height: 1.6;
  margin: 8px 0 0;
}

.rd-q-tab__rows { display: flex; flex-direction: column; }

.rd-q-tab__row {
  display: flex;
  align-items: flex-start;
  gap: 24px;
  padding: 16px 0;
  border-bottom: 1px solid var(--rd-line);
}

.rd-q-tab__left { flex: none; width: 300px; }

/* Raised from the drawn 9 to the tracked floor of 9.5. */
.rd-q-tab__kicker {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

.rd-q-tab__name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
  margin-top: 5px;
}

/* Raised from the drawn 10.5 to the mixed-case floor of 11. */
.rd-q-tab__meta {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  margin-top: 3px;
}

/* THE ONE LINE, which is the whole tablet change on this page — see the
   page's module doc. Truncated rather than wrapped: the drawing's own claim is
   that the reason "fits on one line", not that it wraps to fewer of them. */
.rd-q-tab__reason {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg2);
  line-height: 1.5;
  padding-top: 14px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.rd-q-tab__acts {
  flex: none;
  display: flex;
  gap: 9px;
  padding-top: 10px;
}

.rd-q-tab__act {
  min-height: 44px;
  display: flex;
  align-items: center;
  padding: 0 15px;
  border: 1px solid var(--rd-ctrl);
  background: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 10px;
  color: var(--rd-fg2);
  cursor: pointer;
}

.rd-q-tab__act:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* `.rd-q-tab__act--fix` deleted with the Fix act — James ruling 2026-08-29. */

/* Named explicitly rather than left as the bare `.rd-q-tab__act` base, even
   though the values match it: the three roles are a set and a reader should
   not have to check that Retry is "the one with no modifier" by elimination. */
.rd-q-tab__act--retry { border-color: var(--rd-ctrl); color: var(--rd-fg2); }

.rd-q-tab__act--dismiss { border-color: var(--rd-danger); color: var(--rd-danger); }

.rd-q-tab__note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  line-height: 1.5;
  margin: 10px 0 0;
  max-width: 68ch;
}
/* ==== S18-FOOT · 18 Profile / 22 Account Deletion — the landing's unmigrated foot · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   `/profile` (`pages/profile.rs`). The drawn landing — identity block, friends
   section, the one continuous list of destinations — already migrated, and its
   `.rd-prof__*` rules live in profile-community.css (owner: lane-2, drawings
   13/14/18/19/20). This part is the half that did not come with it: everything
   BELOW the drawn content, plus the two overlays.

   ⚠️ TWO OWNERS ON ONE BEM BLOCK, said out loud. `order.txt` carries the same
   note. The selectors here are disjoint from every name profile-community.css
   declares, and the prefix is shared because these ARE the same page's
   elements — inventing a second block name to dodge the ownership question
   would make the page's CSS harder to read in exchange for making a ledger
   tidier. This merges into profile-community.css whenever the two lanes are
   one.

   What was here before: a `.profile-item` row (its one caller is gone with
   this change, and its two rules leave the legacy pool), a sign-out on
   `bg-white rounded-lg shadow` with `text-red-600`, a danger zone in
   `text-gray-400` and `border-red-200`, and TWO scrimmed dialogs painted in
   Tailwind — a `bg-white rounded-lg p-6` code modal and a delete confirm on
   the legacy `void-confirm` pair. Four fixed-light surfaces at the bottom of a
   dark page.

   ⚠️ THE DELETE DIALOG IS NOT `RdConfirmModal`, and this is the one place in
   the app where that is the right call. `confirm_modal.rs`'s own survey names
   this file as one of five already-scrimmed sites where adopting it *"changes
   the markup and not the interaction"* — and that survey is right about four
   of them. This one has a TYPED CONFIRMATION inside the panel, and the kit
   modal's body is a `String` with no child slot. It also draws something the
   kit modal cannot: **the confirm control is an outline until the typed name
   matches, and only then takes the destroy fill** — *"so the loudest thing on
   the dialog is never a button you have not earned the right to press."* The
   scrim and panel geometry are copied from `.rd-confirm` deliberately, so the
   two read as one component even though only one of them is.
   --------------------------------------------------------------------------- */

/* ── Below the drawn content ─────────────────────────────────────────── */

/* ⚠️ `.rd-prof__foot`, `.rd-prof__danger` and `.rd-prof__danger-head` were
   HERE and are DELETED. They painted the three undrawn capabilities that sat
   under the landing list; their one caller moved to Edit Account on
   2026-08-17 and now paints through `.rd-eacct__*`, which left these three
   with no caller in the tree at all. `s19-doors.css:38-46` and `profile.rs`'s
   own doc both flagged them as stranded and declined to act because the part
   belonged to another dispatch. Acting on it here.

   Nothing replaces them: the rules are gone, not renamed. Edit Account's
   equivalents already exist and are already cited at their own site. */

/* ── The overlays: one scrim, two panels ─────────────────────────────── */

.rd-prof__over {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(6, 6, 6, .82);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px;
  box-sizing: border-box;
}

.rd-prof__over-panel {
  width: 100%;
  max-width: 272px;
  max-height: 100%;
  overflow-y: auto;
  background: var(--rd-surf);
  border: 1px solid var(--rd-line);
  box-sizing: border-box;
}

/* The code modal is the narrower of the two, as drawn — 250 against 272. It is
   a QR held up at arm's length, so its panel is sized by the code and not by
   the prose. */
.rd-prof__over-panel--code { max-width: 250px; }

/* ⚠️ 22 (Tablet) Account Deletion, v0.2 (design relay 4, 2026-08-18) — the
   DELETION modal widens to 380 at tablet. The 272 above is the phone-drawing
   number, shared by both callers of `.rd-prof__over-panel` (this dialog and
   the MY CODE modal, which sizes off its own `--code` modifier); the tablet
   drawing sets 380 for the deletion modal specifically — the drawn column
   plus the modal's padding — so this is scoped off `--code` with `:not()`
   rather than widening every caller of the base class. The 470 the v0.1 card
   drew is retired with that presentation; 380 is the modal's own tablet
   number, not a stand-in for it.

   Query copied VERBATIM from the repo's device-class twin — `device.rs`'s
   `tablet_query()` and the `@media` in `kit-tablet-shell.css` — a test pins
   the two agree, and no viewport branch was added to `edit_account.rs` to
   reach this; the width lives here, in CSS, alone. */
@media (min-width: 1024px) and (orientation: landscape) {
  .rd-prof__over-panel:not(.rd-prof__over-panel--code) { max-width: 380px; }
}

.rd-prof__over-body { padding: 18px 17px 16px; }

/* ⚠️ THE CLOSE SITS ABOVE `MyFriendCode`, NOT INSIDE ITS HEADER. The drawing
   puts `PClose` on the card's own "My code" row — but that row belongs to
   `MyFriendCode`, which also renders standalone, and a close control there
   would give the standalone surface a button with nothing to close. */
.rd-prof__over-close-row {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 6px;
}

/* ⚠️ THE GLYPH IS THE DRAWN X, NOT THE WORD "CLOSE". `PClose` is a 32×32 hit
   area holding a 12×12 two-stroke X at `--rd-fg3`
   (`components/rd-profile-kit.jsx:145-149`), and the card's own label row is
   where the drawing sits it (`components/rd-profile-code.jsx:15-18`).

   The placement argument above is UNCHANGED and still binding: the close stays
   on a row of this file's own, above `MyFriendCode`, because `MyFriendCode`
   also renders standalone. That argument settles WHERE the control lives and
   says nothing about WHAT it draws — so the glyph comes back to the drawing
   while the row stays ours.

   32×32 is the drawn hit area and it is also the reason the typography is
   gone: there is no text left to set. Ink is `currentColor` so the one `color`
   here reaches the stroke inside the `svg`. */
.rd-prof__over-close {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  width: 32px;
  height: 32px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-right: -8px;
  cursor: pointer;
  color: var(--rd-fg3);
}

.rd-prof__over-close:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* ── 22 Account Deletion — `ScreenDeleteAccount` ─────────────────────── */

.rd-prof__del-head {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 900;
  font-stretch: 62%;
  letter-spacing: .01em;
  font-size: 20px;
  line-height: 1;
  color: var(--rd-fg);
}

/* Named, not "this cannot be undone". *"A list of what leaves is a thing you
   can weigh; a warning about permanence is a thing you skim."* */
.rd-prof__del-list { margin-top: 13px; }

.rd-prof__del-row {
  display: flex;
  gap: 9px;
  padding: 9px 0;
  border-bottom: 1px solid var(--rd-line);
}

.rd-prof__del-row--last { border-bottom: none; }

.rd-prof__del-dot {
  width: 5px;
  height: 5px;
  margin-top: 6px;
  flex: none;
  background: var(--rd-danger);
}

.rd-prof__del-copy { flex: 1; min-width: 0; }

.rd-prof__del-name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .04em;
  font-size: 11.5px;
  color: var(--rd-fg);
}

.rd-prof__del-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  line-height: 1.4;
  color: var(--rd-fg3);
  margin-top: 3px;
  text-wrap: pretty;
}

/* The one fact the shipped prose carries that the drawn list has no row for:
   scores you recorded for OTHER shooters are re-attributed rather than
   destroyed. It is about somebody else's data, so it does not belong in a list
   headed by what YOU lose — but it must not be dropped with the paragraph it
   came from. */
.rd-prof__del-note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  line-height: 1.45;
  color: var(--rd-fg3);
  margin: 10px 0 0;
  text-wrap: pretty;
}

.rd-prof__del-confirm { margin-top: 14px; }

.rd-prof__del-ask {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.45;
  color: var(--rd-fg3);
  text-wrap: pretty;
  margin: 0;
}

.rd-prof__del-word {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  color: var(--rd-fg);
}

.rd-prof__del-field { margin-top: 7px; }

/* The offline state. Drawn, and it is the honest one: deleting an account is a
   server write and the offline queue does not take it — the same rule as
   changing a password. */
.rd-prof__del-off { margin-top: 14px; }

.rd-prof__del-off-head {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 8.5px;
  color: var(--rd-fg2);
}

.rd-prof__del-off-body {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.45;
  color: var(--rd-fg3);
  margin: 4px 0 0;
  text-wrap: pretty;
}

.rd-prof__del-banner { margin-top: var(--rd-banner-gap); }

.rd-prof__del-acts {
  display: grid;
  gap: 8px;
  margin-top: 15px;
}

/* ⚠️ THE ARMED CONTROL IS THE ONLY FILL ON THE DIALOG, AND IT IS EARNED.
   Before the typed name matches it is an outline that does nothing; the
   destroy ground arrives with the arming. Both states are the same box at the
   same height, so nothing moves when it lights — §5.5's invariant, and the
   reason a shooter's thumb cannot be over the fill before it exists. */
.rd-prof__del-go {
  appearance: none;
  border: 0;
  width: 100%;
  min-height: 46px;
  padding: 0 14px;
  box-sizing: border-box;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--rd-dfill);
  color: #fff;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 13px), calc(100% - 13px) 100%, 0 100%);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .14em;
  font-size: 12px;
}

.rd-prof__del-go:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 3px; }

/* UNARMED drops the fill — and so does offline, which is the same "you can't"
   claim about a write the queue does not take. `cursor: default` rather than
   `not-allowed`: this is "not right now", not "never".

   ⚠️ IN FLIGHT NO LONGER DROPS IT. This rule used to be the whole disabled
   story, greying the box while the delete was on the wire; the modifier below
   narrows it. rd-loading.jsx:191, the reading the button primitive took on
   19 Aug: "it keeps its own colour: grey is DISABLED ('you can't'), in-flight is
   'it's going'." The two colour declarations here remain correct for the two
   states that really are "you can't". */
.rd-prof__del-go:disabled {
  background: var(--rd-surf2);
  color: var(--rd-fg3);
  cursor: default;
}

/* ⚠️ THE DESTROY GROUND SURVIVES THE PRESS. round-7-loading-rulings-reply.md
   §1: "on a danger fill the sweep draws in the danger fill's foreground ink,
   trackless, 13px, label kept." That is a statement that the fill is STILL
   THERE while the request is open — a greyed button has no fill and no white
   type, so the ruled mark could not be painted on it. `rd-delete.jsx:81` draws
   the same: label kept, fill kept, sweep beside it.

   `deleting` still sets the native `disabled` attribute — input stops, and a
   destroy already sent must not be sendable twice — so the rule above still
   fires and this one restores the ground over it.

   SPECIFICITY, NOT ORDER: 0,3,0 against the `:disabled` rule's 0,2,0, so moving
   either rule cannot silently swap the paint back. The same pairing
   `.rd-confirm__go--inflight` makes in adm1-5-1.css.

   COLOUR AND THE FLEX GAP ONLY — §5.5's invariant is that construction and
   height never change with state, and the base rule is already a centred flex
   row at 100% width, so the only thing in flight adds is the gap that seats the
   13px mark beside the unchanged label. #fff is the literal the base rule
   spells and the one .rd-sweep--on-danger strokes, so the mark matches the type
   beside it. */
.rd-prof__del-go--inflight { gap: 10px; }

.rd-prof__del-go.rd-prof__del-go--inflight:disabled {
  background: var(--rd-dfill);
  color: #fff;
}

/* ── 22 Account Deletion — `ScreenAccountGone`, the screen after ─────── */

/* `rd-delete.jsx:94`. Component: `pages/account_gone.rs`, route
   `/account-deleted`, declared OUTSIDE `ShellLayout` because the session is
   already gone by the time it renders — see that file's own doc.

   ⚠️ IT IS NOT A `.rd-prof__` ELEMENT AND MUST NOT BECOME ONE. Everything else
   in this part is the profile page's own foot; this is a separate screen at a
   separate route that happens to be reached from it, and the only thing the two
   share is the drawing they come from. A shared block name would say they are
   one page, which is the claim the route layout exists to deny.

   Geometry is the drawn `TShell … pad={20}`: the gutter is the shell's
   (`Gutter::Roomy`), so nothing here sets horizontal padding. */
.rd-gone {
  min-height: 100%;
  display: flex;
  flex-direction: column;
}

/* Vertically centred in the space the shell gives it, with the action pinned
   below — `flex: 1` + `justifyContent: 'center'` at `rd-delete.jsx:97`, the
   same composition `.rd-gate` takes for the same reason.

   Left-aligned, not centred: the drawing sets no `alignItems`, exactly like
   `ScreenGate` and unlike `ScreenVerifying`. That distinction was got wrong once
   already on the boot screens, so it is transcribed here rather than assumed. */
.rd-gone__body {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 13px;
}

/* The third grey, which no kit type role carries — `RdText` is `--rd-fg2` at 18
   of 18 sites by design. The hierarchy the drawing draws is heading → body →
   meta, and this is the meta step: same shape, same reason and the same numbers
   as `.rd-prof__del-note` above. */
.rd-gone__note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.5;
  color: var(--rd-fg3);
  margin: 0;
  text-wrap: pretty;
}

/* `flex: 'none'` + `paddingBottom: 14` (`rd-delete.jsx:109`). The button never
   rides up into the copy when the viewport is short. */
.rd-gone__act {
  flex: none;
  padding-bottom: 14px;
}

/* ── 22 (Tablet) Account Deletion, row 02 — `ScreenTabGone`, the bare farewell ──
   RULED: design-reply-2026-08-18-relay5.md §1. Drawn `rd-tablet-identity.jsx:310`
   ("function ScreenTabGone"). Component: `pages/account_gone.rs`'s tablet arm,
   which renders these three classes directly inside the shared `.rd-tcard`
   ground (`s02-tcard.css`) rather than through `TabletCardGround` — there is no
   card, so there is nothing for `RdCenteredCard` to size. The whole point of
   this trio is that NONE of it paints a background, a border or padding beyond
   what centres and spaces the column: "no card chrome — the ground and the
   words" is the document's own sentence for it, and `.rd-tcard` already paints
   the ground colour these rules must not repeat. */

/* The centring layer. `.rd-ccard`'s own ground (`kit-centered-card.css`) minus
   its `overflow: hidden` and background rules, both already carried by
   `.rd-tcard` above — this div's only job left is to centre a fixed column in
   the space `.rd-tcard` gives it. `rd-tablet-identity.jsx:312`
   ("padding: 40"). */
.rd-gone__tab {
  /* `flex: 1 1 auto; min-height: 0` since 2026-09-01: `.rd-tcard` became a flex
     column when the offline strip moved inside it, and a full-height child
     of a flex column overflows by the strip's height instead of sharing with
     it — the same release `.rd-ccard` got in s02-tcard.css. */
  flex: 1 1 auto;
  min-height: 0;
  height: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px;
}

/* The column itself. `rd-tablet-identity.jsx:313` ("width: 340") sets the
   width and the block's own `gap: 13` in the same declaration
   ("gap: 13") — matching `.rd-gone__body`'s phone gap number by coincidence of
   the drawing, not by sharing a rule with it. `max-width: 100%` for the same
   reason `.rd-ccard__card` carries it: the ground clips
   (`overflow: hidden`, inherited from `.rd-tcard`), so an unclamped 340px
   column would run off a narrower ground with no scroll to reach it. */
.rd-gone__tab-col {
  width: 340px;
  max-width: 100%;
  display: flex;
  flex-direction: column;
  gap: 13px;
}

/* The button's own extra 8px, ON TOP OF the column's 13px gap — 21px total
   ahead of the only control on the screen. `rd-tablet-identity.jsx:317`
   ("marginTop: 8"), stated as its own rule rather than folded into the gap
   because the drawing itself states it as a `marginTop` on the button's
   wrapper, not as a fifth block sharing the column's rhythm. */
.rd-gone__tab-act {
  margin-top: 8px;
}
/* ==== S03-RUN · 03 Training — Run & Score, and the queued-edit fix path · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   `/training/drills/:id` and `/training/runs/queued/:idem_key`
   (`pages/score_entry.rs`). Drawn: **03 Training** — `ScreenDrill`'s pre-run
   facts, `ScreenScoreStep`'s foot, and `ScreenResultCard` for what a saved run
   lands on. The fix path's way out is **12 Queue**.

   ⚠️ ONE ROUTE IS TWO DRAWN SCREENS. The corpus draws Drill Detail and Run &
   Score as separate destinations (`ddet` and `run` on the map); we ship one
   page that is both, and `S3-livefire.md` explicitly leaves everything below
   `/training/drills/` alone. So this part composes the pre-run facts and the
   scoring foot down one column rather than inventing a split the route table
   does not have — and it takes no position on that split, which is an IA
   question for whoever builds `ddet`.

   The zone steppers, the time field, the penalties and the notes are NOT here:
   they are `ScoreInputs` (`.rd-scorein__*`, lane-3's block in
   adm0-picker.css), shared with the hit-factor calculator and the squad row.

   ⚠️ THE RESULT STRIP IS A HIERARCHY DECISION, and it is the same one 11 Hit
   Factor makes one route over. `ScreenScoreStep` puts the run's number at 30px
   in the accent, on the same line as the time, directly above Save Run — the
   figure you are about to commit, at the size of a decision. Ours came from
   `ScoreInputs`' generic preview strip: four equal `Points: / HF: / Time: /`
   items at body size, in source order, with no one of them larger than the
   others. That strip is switched off at this call site and the drawn one
   replaces it; the shared cluster is untouched, because the squad row still
   wants the flat list.
   --------------------------------------------------------------------------- */

.rd-run {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
}

.rd-run__chips {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  margin-bottom: 9px;
}

/* The screen's actions ride the identity strip as accent WORDS, pushed right —
   the drawing's own arrangement for `Edit`, and its reason: *"this column has
   no vertical slack and Edit is not what the screen is for."* Two words here
   rather than one, because both doors were already on this screen as blue
   `hover:underline` text links in a header row. */
.rd-run__doors {
  margin-left: auto;
  display: flex;
  gap: 12px;
  flex: none;
}

.rd-run__door {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-acc);
  text-decoration: none;
}

.rd-run__door:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* `ScreenDrill`'s stat block — everything fixed by the drill definition,
   stated once, up front, in the order a shooter sets up. */
.rd-run__stats { margin-top: 11px; }

.rd-run__stats-inner {
  padding: 10px 14px;
  box-sizing: border-box;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.rd-run__banner { margin-top: var(--rd-banner-gap); }

/* ── The number you are about to commit ──────────────────────────────── */

.rd-run__result {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
  margin-top: 14px;
  padding-top: 11px;
  border-top: 1px solid var(--rd-line);
}

.rd-run__result-copy { flex: 1; min-width: 0; }

.rd-run__result-val {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 30px;
  line-height: 1.1;
  letter-spacing: -.02em;
  color: var(--rd-acc);
  margin-top: 2px;
}

/* Nothing entered yet, or a profile with no numeric metric at all (pass/fail).
   An em-dash in the tertiary grey, never a zero: a run you have not scored has
   no value, and printing one would be a claim. */
.rd-run__result-val--none { color: var(--rd-fg3); }
/* "Pass" only — GREEN, James 30 Aug 2026: `--rd-ok`, the PASS toggle's own tone
   (adm0-picker.css / kit-ok.css). Numeric metrics keep the accent above. */
.rd-run__result-val--pass { color: var(--rd-ok); }

.rd-run__result-unit {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .14em;
  font-size: 10px;
  color: var(--rd-fg2);
  flex: none;
  padding-bottom: 7px;
}

.rd-run__act { margin-top: 12px; padding-bottom: 12px; }

/* ── The Pro-tablet note — a driving drill has no scoring face here ──────
   `pages/score_entry.rs`'s discipline gate (slice 4.4). Deliberately the
   `__saved` card's geometry rather than a second card invented for this
   state: both are "this screen is done asking you for numbers", and the
   note is what a driving drill lands on the way a receipt is what a saved
   run lands on. iOS and Android print the same two sentences. */

.rd-run__note {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 14px;
}

.rd-run__note-inner {
  padding: 20px 18px;
  box-sizing: border-box;
}

.rd-run__note-lab {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .16em;
  font-size: 9.5px;
  color: var(--rd-acc);
  margin-bottom: 10px;
}

.rd-run__note-body {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--rd-fg2);
  text-wrap: pretty;
}

.rd-run__note-acts { display: flex; gap: 8px; }

/* ── `ScreenResultCard` — where a saved run lands ────────────────────── */

.rd-run__saved {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 14px;
}

.rd-run__saved-inner {
  padding: 20px 18px;
  box-sizing: border-box;
}

.rd-run__saved-lab {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .16em;
  font-size: 9.5px;
  color: var(--rd-acc);
  margin-bottom: 10px;
}

.rd-run__saved-row {
  display: flex;
  align-items: flex-end;
  gap: 6px;
}

.rd-run__saved-val {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 40px;
  line-height: .9;
  letter-spacing: -.02em;
  color: var(--rd-fg);
}

.rd-run__saved-unit {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .14em;
  font-size: 10px;
  color: var(--rd-fg2);
  padding-bottom: 4px;
}

.rd-run__saved-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  line-height: 1.45;
  color: var(--rd-fg2);
  margin-top: 8px;
  text-wrap: pretty;
}

.rd-run__saved-acts { display: flex; gap: 8px; }
/* ==== S03-SPLITS · 03 Training — per-step times, collected and read back · owner: lane-reskin ==== */
/*
   ONE DRAWN FEATURE ACROSS TWO SCREENS, so one part file. `03 Training` row 09
   ("Scoring — The Plan Is the Index") draws `ScreenScoreSteps`, where a
   multi-step drill is timed one step at a time and the total is derived; row 13
   ("The Run, Read Back") draws the same run's `Splits` printout inside
   `ScreenRunDetail`. The designer's own note ties them together: *"the form
   collects a time per step and the detail prints them, so the scoring screens
   in 09 need a time per step too, not just a total"*. Splitting these rules
   across two parts would put one feature's paint in two banners.

   ⚠️ TWO OWNERS ON ONE BEM BLOCK, deliberately, and it is the arrangement
   `order.txt` already records for `s18-foot.css`. `.rd-scorein__*` is declared
   in adm0-picker.css (owner: lane-3), which converted the shared input cluster;
   the section below adds only the per-step rows that cluster did not have. The
   names are disjoint — everything here is `.rd-scorein__step*` or
   `.rd-scorein__total*`, none of which that file declares — and this part sits
   after it in `order.txt`, so a tie resolves here. It merges into that block
   whenever the two lanes are one.

   ⚠️ THE DRAWN ACCENT ON THE FIRST STEP ROW IS NOT PAINTED AS DRAWN, and this
   is a reading of the drawing rather than a shortfall. `ScreenScoreSteps`
   strokes row one in the accent at 1.4px and the rest in the line colour. On a
   phone screen that times one step at a time, that is the step being worked. A
   web form has every field live at once, so a permanently accented first row
   would assert a "current step" the page does not have. The accent moves to
   `:focus-within` — the row you are actually in — which is the same claim made
   about a surface that can answer it.
*/

/* ── Entry: `ScreenScoreSteps`, one field per step (score_inputs.rs) ──── */

.rd-scorein__steps { display: grid; gap: 8px; }

.rd-scorein__step {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  padding: 10px 12px;
  border: 1px solid var(--rd-line);
  background: var(--rd-surf);
}

.rd-scorein__step:focus-within { border-color: var(--rd-acc); }

.rd-scorein__step-copy { flex: 1; min-width: 0; }

/* A CONDITIONAL LINE, NEVER A RESERVED EMPTY SLOT — the drawing's own rule for
   the chip strip (*"a prose-only step draws no chips at all and the row keeps
   its shape"*). No min-height, no empty-state margin: when the step has neither
   a distance nor a shot count the caller renders no strip and the instruction
   starts at the top of the row. */
.rd-scorein__step-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-bottom: 5px;
}

.rd-scorein__step-text {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.35;
  color: var(--rd-fg2);
  text-wrap: pretty;
}

.rd-scorein__step-time {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
}

/* The drawn value is a 22px mono figure with no field chrome around it. Here it
   is the input, so it keeps the chrome a control needs to be findable — and the
   specificity defence the sibling `.rd-scorein__control` rule documents: the
   pre-kit backfill styles `input[type="number"]` by ATTRIBUTE, which is
   (0,0,1,1) and outranks a single class. The compound below is (0,0,2,0). */
.rd-scorein__step .rd-scorein__step-input {
  width: 86px;
  box-sizing: border-box;
  padding: 6px 8px;
  text-align: right;
  border: 1px solid var(--rd-ctrl);
  border-radius: 0;
  background: var(--rd-field);
  box-shadow: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 20px;
  line-height: 1.1;
  color: var(--rd-fg);
  appearance: none;
  -webkit-appearance: none;
}

.rd-scorein__step .rd-scorein__step-input:focus {
  outline: none;
  border-color: var(--rd-acc);
  box-shadow: none;
}

.rd-scorein__step-lab {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

/* The derived total: a rule, the label, the sentence that says where the number
   came from, and the number itself in the accent — `ScreenScoreSteps`' foot. */
.rd-scorein__total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-top: 10px;
  padding: 11px 2px 0;
  border-top: 1px solid var(--rd-line);
}

.rd-scorein__total-copy { flex: 1; min-width: 0; }

.rd-scorein__total-lab {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .14em;
  font-size: 9.5px;
  color: var(--rd-fg2);
}

.rd-scorein__total-note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
  margin-top: 3px;
}

.rd-scorein__total-val {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 28px;
  line-height: 1;
  color: var(--rd-acc);
}

/* Nothing typed yet, so there is nothing to add up. An em-dash in the tertiary
   grey, never a 0.00 — the same rule `.rd-run__result-val--none` states one
   screen down: a total you have not earned is not a total of zero. */
.rd-scorein__total-val--none { color: var(--rd-fg3); }

/* ── Read back: `ScreenRunDetail`'s Splits printout (score_detail.rs) ─── */

.rd-splits { display: grid; gap: 6px; }

.rd-splits__row {
  display: flex;
  align-items: flex-start;
  gap: 11px;
}

.rd-splits__idx {
  flex: none;
  width: 10px;
  padding-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 10px;
  color: var(--rd-fg3);
}

.rd-splits__val {
  flex: none;
  margin-left: auto;
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 14px;
  color: var(--rd-fg2);
}

.rd-splits__total {
  display: flex;
  gap: 11px;
  padding-top: 8px;
  border-top: 1px solid var(--rd-line);
}

.rd-splits__total-lab {
  flex: 1;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .14em;
  font-size: 9.5px;
  color: var(--rd-fg2);
}

.rd-splits__total-val {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  font-stretch: 75%;
  font-size: 14px;
  color: var(--rd-fg);
}
/* ==== S15-STAND · 15 Events row 07 — Where You Stand · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   The event standings board — `/events/:event_id/standings`
   (`pages/event_standings.rs` mounting `components/event_standings.rs`).
   Drawn: `ScreenEventStandings` (`components/rd-events.jsx:271-308`), three
   frames — live, empty, light.

   ⚠️ THE OWNER IS `lane-reskin` AND THAT IS NOT A GUESS AT WHO TYPED IT.
   `s15.css` already owns this drawing's list and detail screens; row 07 is the
   same document, and one drawing with two owners is how two parts end up
   disagreeing about the same eyebrow. It merges into `s15.css` the day nothing
   else is landing there. Named separately today only because this section's
   subject is a body with TWO mount points, and a reviewer needs to see that in
   the file list rather than find it inside a screen file.

   ⚠️ ONE SHEET, TWO MOUNTS. The same body renders under the Pro range-day bar
   in Phase B, so nothing here may assume the shooter shell around it: no rule
   reaches outside `.rd-stand`, and the only sizes that are phone-specific are
   the ones the drawing itself makes phone-specific — which is the squad line,
   and it folds rather than shrinking.

   ⚠️ THE LIGHT FRAME IS NOT A SEPARATE SET OF RULES. Row 07's third frame is
   `TT.light`, and the whole of it is carried by the token layer: every colour
   below is `var(--rd-*)`, so the light theme is the same sheet read against the
   other palette. A hex here would paint fixed-dark in a light app — the failure
   `_legacy-*` islands still carry and the kit exists to end. The one thing that
   IS stated per-theme is nothing at all, deliberately: if a rule needs a
   theme-conditional value, it belongs as a token value (architect ruling ②),
   not as a second selector here.

   WHAT IS KIT AND NOT HERE: the shell, the gutter, the back row, the eyebrow's
   label type (`RdLabel`), the heading (`RdHeading`), the empty state
   (`RdEmptyState`), the stage chips (`RdChipSelector`), the offline banner
   (`RdBanner`), the loading line (`RdLoading`), and the row's chamfered
   boundary (`RdChamferBox`, which carries `r=9` and the 1 / 1.5 border widths
   as geometry custom properties). What is page CSS is the head's two derived
   lines, the row's three-cell grid, and the foot.

   ⚠️ THE ROW IS A GRID AND NOT A FLEX ROW, and the reason is the fold. The
   drawing gives the numeral a fixed 20px, the value its natural width, and the
   name column everything left over with the squad UNDER it (`:287-292`). A flex
   row with `flex: 1` on the middle cell does that too — until a long name and a
   four-digit value meet, at which point the value is what shrinks. `1fr` on the
   middle track and `auto` on the outer two makes the name the only thing that
   can lose, which is why the name is the one thing that ellipses.
   --------------------------------------------------------------------------- */

.rd-stand {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
}

/* The eyebrow sits directly above the heading, which owns its own leading. */
.rd-stand__eyebrow {
  margin-bottom: 6px;
}

/* ---- The Pro mount (`StandingsHead::Pro`, under the range-day bar) --------
   The drawn screen is a tablet column (`rd-pro-rangeday.jsx:257-300`), and the
   cap is the body's own since 14 Aug — Phase B capped its container at 760px as
   a stand-in and flagged the cap as this body's to carry, so every Pro mount
   gets the same measure without hand-capping. 760px is that agreed value, not a
   drawn one; the drawn four-column row geometry is a separate open flag. */
.rd-stand--pro {
  max-width: 760px;
}

/* The drawn title row (`:241-244`): title and sub-line share one baseline with
   a 14px gap, 20px above the board; the mount's chrome sits at the far end
   across the flexed gap. Type sizes are the drawing's (24 / 13). */
.rd-stand__prohead {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-bottom: 20px;
}

.rd-stand__prosub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  line-height: 1.4;
  color: var(--rd-fg3);
}

.rd-stand__prohead-gap {
  flex: 1;
}

/* The drawn Pro row (rd-pro-rangeday.jsx:278-285) — four columns on one line,
   no chamfer box, a hairline under each row. Every number is the drawing's:
   gap 20, padding 17px 0; rank 44px at 19px display with the accent on the
   leader only; name takes the remainder at 16px; the squad gets its own 130px
   column at 13px (row 07 folds it under the name because a phone cannot
   afford the column — "Pro has 1194px and gives it a column"); value 90px,
   right-aligned, in the shared numeric role (.rd-num carries the 17px). */
.rd-stand__prorow {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 17px 0;
  border-bottom: 1px solid var(--rd-line);
  /* Skip the layout and paint of off-screen rows: a standings board runs to
     100 rows a page over 10 pages. 58px = 17+17 padding + the 19px rank line
     (~23px at normal leading, the row's tallest item) + the 1px hairline; the
     `auto` keyword lets the browser keep each row's measured height once it
     has rendered, so this figure only shapes the pre-render scrollbar. */
  content-visibility: auto;
  contain-intrinsic-size: auto 58px;
}

.rd-stand__propos {
  width: 44px;
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .02em;
  font-size: 19px;
  color: var(--rd-fg3);
}

.rd-stand__propos--lead {
  color: var(--rd-acc);
}

.rd-stand__proname {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 16px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rd-stand__prosquad {
  width: 130px;
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rd-stand__proval {
  width: 90px;
  flex: none;
  text-align: right;
}

/* ⚠️ THE PARTICIPANT'S QUESTION (`:282`). `--rd-fg2`, a step above the meta
   grey, because it is the one line on the screen written to the reader about
   the reader — the list is about everyone. */
.rd-stand__you {
  margin: 7px 0 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--rd-fg2);
}

/* `Updated just now · Refresh` (`:283`) — one line, the control inline in it. */
.rd-stand__fresh {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  margin-bottom: 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

/* The refresh control. A bare button rather than `RdButton`: the drawing gives
   it no box, no fill and no chamfer — it is a word in the accent, inline in the
   freshness sentence, and a kit button here would be a second primary on a
   screen whose action is scrolling. The reset is explicit because a UA button
   brings its own font, padding and border. */
.rd-stand__refresh {
  appearance: none;
  -webkit-appearance: none;
  background: none;
  border: 0;
  margin: 0;
  padding: 0;
  font: inherit;
  color: var(--rd-acc);
  cursor: pointer;
  /* The row is one line of 10px text; the hit target is the line box plus this,
     which keeps the sentence intact and still clears a fingertip. */
  padding-block: 4px;
}

.rd-stand__refresh:disabled {
  color: var(--rd-fg3);
  cursor: default;
}

.rd-stand__refresh:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* The undrawn stage picker — see the render's own flag. Spaced as a filter row,
   not as part of the head. */
.rd-stand__stages {
  margin-bottom: 10px;
}

/* ⚠️ ABOVE THE BOARD, NEVER INSTEAD OF IT. A failed refresh keeps the rows, so
   this strip has to sit in the flow rather than replace it. */
.rd-stand__error {
  margin-bottom: 8px;
  padding: 8px 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.45;
  color: var(--rd-danger);
  border: 1px solid var(--rd-danger);
  background: var(--rd-danger-wash);
}

.rd-stand__banner {
  margin: 0;
}

/* The truncation note — a partial standing saying so. */
.rd-stand__note {
  margin-bottom: 8px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  line-height: 1.45;
  color: var(--rd-fg3);
}

/* The empty frame is vertically centred in what is left of the screen
   (`:276`), which is why the list and the empty state are siblings under a
   flex column rather than two states of one box. */
.rd-stand__empty {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-bottom: 30px;
}

/* ⚠️ THE NESTED SCROLL STAYS — RE-DECIDED 2026-08-18, and what changed is the
   REASONING, not the rule. It is re-argued in this change rather than in a
   later one because the two-up block at the foot of this file alters this
   element's geometry, and a layout rule and a scroll rule that were decided
   apart is how a board ends up with two scrollbars.

   The repo's standard is `kit/panes.rs`'s recorded argument and its test
   `the_panes_own_the_scroll_because_nothing_above_them_does`: a region owns its
   scroll only when nothing above it does. Read against all three mounts of this
   one body:

   - THE PHONE MOUNT — `pages/event_standings.rs` inside `RdShell`. Nothing
     above this element scrolls usefully: `.rd-shell` is `height: 100dvh;
     overflow: hidden` and `.rd-shell__content` is a definite-height flex column
     (`kit-shell.css` §6, "the REGION scrolls, not the document"), so `.rd-stand`
     resolves to a definite height and this list is the only box that can move.
     That is also exactly what makes `.rd-stand__foot` (`flex: none`) the last
     thing on the screen rather than the first thing the list scrolls past — the
     pinned foot IS this declaration. So the rule is NOT dead weight kept for
     symmetry, which is the claim this note corrects: delete it and the shell's
     own region takes over and scrolls the drawn foot away.
   - THE TABLET MOUNT — `pages/events_tablet.rs`, through `.rd-tev-d__stand`
     (`s15-tablet.css` §5), which gives the body no definite height on purpose.
     `.rd-stand`'s `flex: 1` therefore resolves to content, this element never
     overflows, and `overflow-y: auto` never paints; the detail pane's single
     scroll (`kit-tablet-panes.css`) carries the whole tab. Inert there, not
     fighting: exactly one scroller is live per mount, which is the property
     that had to survive the two-up.
   - THE PRO MOUNT — `StandingsHead::Pro` — reads as the tablet one does.

   The two-up block below therefore adds NO height, deliberately. A height there
   would light this scroller inside a pane that already has one, for a board the
   drawing says fits without scrolling. If either mount ever gives this body a
   definite height, this decision is re-argued rather than inherited — the same
   posture `panes.rs` takes toward the shell above it, and
   `ruling_one_scroller_is_live_per_mount` is what goes red. */
.rd-stand__list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

/* `marginBottom: 6` on the drawn row (`:286`). The gap lives on a wrapper
   rather than on the chamfer box, which owns geometry only. */
.rd-stand__row-wrap {
  margin-bottom: 6px;
  /* Same skip on the phone's variant of the row. 50px = the chamfer box's
     44px `min-height` (`.rd-stand__row`) + this wrapper's 6px gap. The phone
     folds the squad under the name on a narrow name column, so rows are not
     all one height — `auto` is what makes that safe, and no media-query
     variant of this figure is wanted. */
  content-visibility: auto;
  contain-intrinsic-size: auto 50px;
}

.rd-stand__row {
  display: grid;
  grid-template-columns: 20px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 8px 11px;
  min-height: 44px;
}

/* The numeral. `--rd-fg3` at rest; rank 1 takes the accent (`:288`). */
.rd-stand__pos {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .02em;
  font-size: 13px;
  color: var(--rd-fg3);
}

.rd-stand__pos--lead {
  color: var(--rd-acc);
}

/* The name column, and the fold. `min-width: 0` is what lets the name ellipse
   inside a grid track — without it the track takes the text's intrinsic width
   and the value is pushed off the row. */
.rd-stand__who {
  min-width: 0;
}

.rd-stand__name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  line-height: 1.35;
  color: var(--rd-fg);
  /* Names truncate rather than wrap, as row 06 rules for the squad screen: two
     lines break the row rhythm on a screen you scan for a name. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ⚠️ MARK TWO OF TWO. Mark one is the accent BOUNDARY, which the chamfer box
   paints from `Stroke::Accent` — this is the word `You` beside it. The two are
   deliberately different properties: an own row that is also rank 1 would
   otherwise carry one mark twice and read as a single emphasis. */
.rd-stand__name--me {
  color: var(--rd-acc);
  font-weight: 600;
}

/* The squad, folded under the name (`:291`). */
.rd-stand__squad {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.3;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The metric. Tabular figures for `s17.css`'s reason: a board re-sorts under
   the reader, and proportional digits move the column edge on every change. */
.rd-stand__val {
  font-size: 15px;
  color: var(--rd-fg);
}

.rd-stand__val--me {
  color: var(--rd-acc);
}

/* The scoping sentence (`:296`). `flex: none` so it is the last thing on the
   screen rather than the first thing the list scrolls past. */
.rd-stand__foot {
  flex: none;
  padding: 8px 0 12px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  line-height: 1.45;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   THE TABLET TWO-UP BOARD — `15 (Tablet) Events`, the Standings frame.
   --------------------------------------------------------------------------- */
/* Drawn: `ScreenTabEvents`, the Standings arm of the detail pane. The whole of
   it is one declaration — `rd-tablet-social.jsx:257` ("gridTemplateColumns:
   '1fr 1fr'"), with `gap: '0 30px'` on the same line — holding a flat
   `rd-tablet-social.jsx:258` ("ESTAND.map") over the same rows the phone board
   draws. Row 07's board is one column at every width today. #849's body: "Sixteen
   names in one column would waste half the pane; two columns of eight fit
   without scrolling."

   ⚠️ CSS ONLY, AND THE DRAWING IS THE REASON, not convenience. The drawn two-up
   needs no markup this body lacks: one flat list of rows inside one grid
   container is precisely `.rd-stand__list` and its row children as they render
   today. Nothing is sliced into two arrays, nothing is interleaved, and no row
   changes shape — so a markup change would have been a second way to say what
   one property already says. Contrast `s17-tablet.css` §6, where the two-up
   grid is a class the tablet RENDERER puts on, because `leaderboard.rs` has a
   tablet arm to put it on. This body has no such arm and must not grow one: it
   is one renderer with three mounts and only a `StandingsHead` to tell them
   apart, and `head` is `Shooter` on both the phone and the tablet. Teaching it
   the device class would put a viewport branch in Rust to reach a layout — the
   move `s18-foot.css` explicitly declined (#823).

   ⚠️ READING ORDER RUNS ACROSS THEN DOWN. The drawn container sets no auto-flow
   at all, so it takes the default `row`: 1st and 2nd side by side, 3rd and 4th
   beneath them. Stated because "two columns of eight" reads like the other
   flow — 1..8 down the left and 9..16 down the right — and that is not what the
   drawing does. `s17-tablet.css` §6 settled the identical question off the
   identical idiom on the Leaderboard's board, and settles it the same way.

   ⚠️ THE ROW GAP IS ZERO ON PURPOSE, and only the column gap is drawn. The
   drawn row separates itself with its own hairline —
   `rd-tablet-social.jsx:259` ("minHeight: 40") carries `borderBottom` — and
   ours separates itself too, with `.rd-stand__row-wrap`'s 6px margin
   (`rd-events.jsx:293` ("marginBottom: 6")). A row gap here would be a second
   vertical rhythm on rows that already carry one.

   ⚠️ `:not(.rd-stand--pro)` IS LOAD-BEARING, not defensive tidiness. The Pro
   range-day mount runs on a tablet and reaches this query at full width, and
   its rows are `.rd-stand__prorow` — the drawn four-column line
   (`rd-pro-rangeday.jsx:278-285` ("width: 44")) inside the 760px column
   `.rd-stand--pro` caps. Two-up-ing THAT board would put two four-column rows
   in 380px each. The Pro screen is drawn one column and stays one column.

   ⚠️ `align-content: start` is the one declaration here with no twin in the
   drawing, and it protects a drawn number. The drawn container is a content-
   height box; ours is a `flex: 1` child that can be TALLER than its content, and
   a grid's default alignment would then stretch the implicit rows and inflate
   the drawn 40px rhythm. It keeps the rows the height they were drawn.

   ⚠️ WHAT SURVIVES BOTH LAYOUTS, UNTOUCHED. The reader's own row is marked by
   `.rd-stand__name--me`, `.rd-stand__val--me` and the chamfer box's
   `Stroke::Accent` — three properties of the ROW, none of which can know how
   many columns hold it. The dividers are `.rd-stand__row-wrap`'s margin and the
   box's own stroke, per row. The foot is a SIBLING of this list, never a grid
   item. This block therefore names no row class at all, and below the
   breakpoint not one declaration in it applies — which is the safety argument
   for touching a part this lane does not own.

   ⚠️ NO HEIGHT, DELIBERATELY — see the nested-scroll decision at
   `.rd-stand__list` above, re-decided in this same change. The grid halves the
   list's intrinsic height, which is what makes "fits without scrolling" true;
   giving it a height would light a second scrollbar inside a pane that already
   has one.

   THE QUERY IS COPIED VERBATIM from the repo's device-class twin —
   `device.rs`'s `tablet_query()` and the one `@media` in `kit-tablet-shell.css`
   §8 — and `ruling_the_two_up_fires_at_the_devices_own_breakpoint` pins this
   file's copy to `device::TABLET_MIN_WIDTH_PX` and to the orientation clause,
   on the declaration line rather than on this prose. Same shape and same
   argument as `s18-foot.css`'s tablet modal width (#823). */
@media (min-width: 1024px) and (orientation: landscape) {
  .rd-stand:not(.rd-stand--pro) .rd-stand__list {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 30px;
    align-content: start;
  }
}
/* ==== S15-TABLET · 15 (Tablet) Events — the two-pane family · owner: lane-lib ==== */

/* Drawn as `ScreenTabEvents` in `15 (Tablet) Events`, four frames: the populated
   detail (dark), the same with a second event selected (light), the Standings
   tab, and the empty "No events" state. Screen:
   apps/web/src/pages/events_tablet.rs, which carries the entry ruling, the
   RdSectionTabs non-adoption argument and every carried-not-drawn flag. Only the
   values live here.

   ANATOMY — everything below sits INSIDE the substrate, and this part adds no
   frame of its own:

     RdTabletShell [ rail | Events + count | (no section band) ]
       RdPanes width=Medium (360)
         list   -> .rd-tev__list    : the staff door, the groups, the rows
         detail -> .rd-tev-d        : head + Detail/Standings strip + one section

   THE PANES OWN THE SCROLL AND THIS PART DOES NOT ADD A SECOND ONE.
   kit-tablet-panes.css §1 and §3 give each pane `overflow-y: auto`, which is a
   stated deviation from the drawing's `overflow: hidden`. The drawn detail pane
   is a `flex: none` head over a `flex: 1, overflow: hidden` body — a second
   scroll container inside a pane that already scrolls. It is NOT transcribed:
   nesting a scroller inside a scroller is two scrollbars for one gesture, and
   the pane's own scroll is the one the substrate argued for. The consequence is
   stated rather than hidden: the event's name and the Detail/Standings strip
   scroll with the section under them.

   NO MEDIA QUERY IN THIS FILE, DELIBERATELY. The tablet substrate is entered by
   COMPONENT, not by rule: `crate::device::DeviceClass` picks RdTabletShell over
   RdShell and these classes only ever render inside it. kit-tablet-shell.css §8
   is the one place a width query belongs, and its own banner argues there must
   be exactly one — a second breakpoint here would be a second number to keep in
   step with `TABLET_MIN_WIDTH_PX`.

   ⚠️ THE TYPE FLOORS OF `01 Foundations` §05 (18 Aug), APPLIED THREE TIMES.
   "Mixed-case text never goes below 11px on the tablet. Condensed uppercase
   labels with tracking may go to 9.5px … Where an existing tablet drawing shows
   mixed-case below 11, build 11: the scale outranks the drawing." The drawing
   puts three sizes under the floors and all three move here — see §3 (the row's
   who/meta, drawn 10.5 mixed case) and §2/§6 (the two eyebrows, drawn 9 tracked
   uppercase). Every size in this file is swept and floored by
   `ruling_no_mixed_case_type_falls_below_the_tablet_floor` in events_tablet.rs,
   which reads THIS FILE.

   ⚠️ THE DATE BLOCK IS A PHONE COMPONENT REUSED IN A TABLET FRAME, so §05's
   reading rule applies to it and to nothing else here: "A phone component reused
   inside a tablet frame keeps its 312-scale numbers in the drawing and builds at
   ×1.25 — which is how the events card's drawn 9.5px meta is a legal 12 on the
   tablet." The drawing reaches into `rd-events.jsx` for `DateBlock` and
   hand-rolls everything around it, so the block's four numbers are multiplied
   and the rest of the row is taken 1:1. See §3.

   ⚠️ font-stretch 62, NOT the drawn 68. Both display titles here are drawn at
   width 68, which is outside `ALLOWED_STRETCH` (62, 72, 75-78, 100) and
   `rangeday check css` refuses it by design. 62 is the correct member of the set
   for this size band — the designer's own rule, quoted in kit-tablet-shell.css
   §5: "62 is right at 30px and slightly too tight below about 18px." Both of
   these are 24px, which is the 30px end of that band, and `.rd-tshell__title`
   took the same decision at 26px. */

/* ---------------------------------------------------------------------------
   1. The header action, the wait state, and the two centred states.
   --------------------------------------------------------------------------- */
/* ⚠️ CARRIED, NOT DRAWN. The tablet frames put nothing in the shell's action
   slot; the phone list draws `Schedule a range day` under the title because
   "the list scrolls, and an action that scrolls away is one you have to hunt
   for". The tablet header is that position on this device and it does not
   scroll at all, so the door moves into the slot rather than disappearing.

   THE SLOT PAINTS THE BOX; THIS PAINTS NOTHING. kit-tablet-shell.css §5 owns the
   44px height, the 18px inset, the accent border and the type role, and says in
   as many words that it "writes no copy and sets no cursor". So the link inside
   it inherits all of that and declares only the four things an <a> brings with
   it that the slot cannot know about. */
.rd-tev__act {
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
}

/* The centred loading sentence this box existed for is retired
   (43 Loading States.html:40) and both of its sites now render the kit's marks
   in the layout they claim — the list column's stack and the detail pane's body
   — so the box went with the sentence rather than being left behind for the
   dead-class checker. Its replacements are §5a. */

/* THE EMPTY STATE REPLACES THE PANES — it is not an empty list pane beside an
   empty detail pane. The drawn `No events` frame renders no list, no detail and
   no header subtitle: one centred block in a 52ch measure. */
.rd-tev__empty {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px;
}

.rd-tev__empty-inner {
  max-width: 52ch;
  text-align: center;
}

.rd-tev__empty-lead {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 900;
  font-stretch: 62%;
  letter-spacing: .01em;
  font-size: 24px;
  line-height: 1.05;
  color: var(--rd-fg);
}

.rd-tev__empty-body {
  margin-top: 12px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  line-height: 1.6;
  color: var(--rd-fg2);
}

/* THE RULED IN-PANE PRIMARY (C-56.9.3, 26 Aug). `rd-tablet-social.jsx:206-210`
   draws a centring flex row at `marginTop: 20` round a `width: 250` box holding
   a dense primary. The two boxes collapse into this one: an auto inline margin
   centres it inside the 52ch measure, which is what the flex row was for.
   `max-width` keeps it honest if the measure is ever narrower than 250. */
.rd-tev__empty-act {
  margin: 20px auto 0;
  width: 250px;
  max-width: 100%;
}

/* THE NOTHING-SELECTED PANE. Undrawn, and the entry ruling is what creates it:
   every drawn frame of this screen has a selection (`sel = 0` is the drawn
   default) and that default is not available when the URL is the selection. The
   copy takes the form of the corpus's one drawn nothing-selected pane, the stage
   editor's — the state, then what to do about it. See the screen's own note. */
.rd-tev__none {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px;
}

.rd-tev__none-lead {
  max-width: 52ch;
  text-align: center;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  line-height: 1.6;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   2. The list pane — the staff door, and the group headings.
   --------------------------------------------------------------------------- */
/* The pane itself is `.rd-tpanes__list` and already a flex column with its own
   scroll; this is the block inside it. No padding on the block: every row below
   carries its own 16px inset, because a pane-level gutter would inset the row
   borders too and the 1px rules would stop reaching the pane's edges — the same
   argument kit-tablet-shell.css §7 makes about the content region. */
/* ⚠️ `flex: none`, NOT `flex: 1`. This block is a flex ITEM in a scrolling flex
   column (kit-tablet-panes.css §1), and a `flex: 1` item is sized to the
   container: a list taller than the pane would be shrunk to fit and the rows
   past the fold would be clipped with no scrollbar, because the pane's
   scrollable region never grows. `flex: none` keeps the block at its content
   height and lets the pane's own `overflow-y: auto` do the one job the
   substrate gave it. The same declaration and the same reason on
   `.rd-tev-d` in §4. */
.rd-tev__list {
  flex: none;
}

.rd-tev__banner {
  padding: var(--rd-banner-gap) 16px 0;
}

/* ⚠️ CARRIED, NOT DRAWN — the staff-only door to /range-day, which events.rs
   records as the ONLY door to that screen. A door that exists at one device
   class and not the other is the same missing-door defect one width along. It
   is quiet, it sits above the groups, and it is 56px because touch sizes do not
   shrink because a mouse might be present. */
.rd-tev__door {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  min-height: 56px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--rd-line);
  text-decoration: none;
  cursor: pointer;
}

.rd-tev__door-name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 11px;
  color: var(--rd-fg);
}

.rd-tev__door-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

.rd-tev__group {
  display: block;
}

/* ⚠️ 9.5, AND THE DRAWING SAYS 9. The group headings survive the two-pane
   flatten because "This week" and "Later" are the only sense of time the list
   has, and they are drawn as tracked uppercase at 9px. 9.5 is the lowest legal
   size in the tablet scale and it is the exemption this label is claiming, so
   the label is built at the floor rather than under it. */
.rd-tev__group-lab {
  padding: 13px 16px 7px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* The foot of the list, and the only reason it exists: `ListEvents` pages at 20
   and an event on page two is unreachable without it. Undrawn on the tablet
   frame; the phone list carries the same control for the same reason. */
.rd-tev__more {
  /* The in-flight sweep sits BESIDE the label, which no longer changes
     (43 Loading States.html:53) — so this centres its two children instead of
     laying out one block of text. */
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  min-height: 44px;
  padding: 12px 16px;
  background: none;
  border: 0;
  border-top: 1px solid var(--rd-line);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 11px;
  color: var(--rd-fg3);
  cursor: pointer;
}

.rd-tev__more:disabled {
  cursor: default;
}

.rd-tev__more:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* ---------------------------------------------------------------------------
   3. One list row — 78px, a date block beside three lines.
   --------------------------------------------------------------------------- */
/* THE ROW IS THE LINK, not a wrapper around the name: that is what makes the
   target 360 x 78 rather than the width of a word, and it is what makes
   selection an address rather than a state write.

   ⚠️ THE 3px LEFT RULE IS ALWAYS PRESENT AND ONLY EVER RECOLOURED. Drawn as
   `borderLeft: 3px solid (selected ? acc : transparent)`. Declaring it only on
   the selected modifier would shift every unselected row 3px sideways as the
   selection moves down the list. */
.rd-tev-row {
  display: flex;
  gap: 14px;
  min-height: 78px;
  padding: 13px 16px;
  border-bottom: 1px solid var(--rd-line);
  border-left: 3px solid transparent;
  box-sizing: border-box;
  text-decoration: none;
  cursor: pointer;
}

.rd-tev-row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* The drawn selection: the accent rule and the raised ground, both. Never
   colour alone — the sunlight rule `03-tshell.md` records is that volt is the
   first thing a screen loses on a bay at noon, and the ground survives it. */
.rd-tev-row--on {
  border-left-color: var(--rd-acc);
  background: var(--rd-surf2);
}

/* ⚠️ THE DATE BLOCK IS THE PHONE'S `DateBlock`, SO ITS NUMBERS ARE ×1.25.
   The drawn tablet screen reaches into rd-events.jsx for this one component and
   hand-rolls everything around it, which is exactly the case `01 Foundations`
   §05's reading rule names. The block's four drawn numbers and their tablet
   builds:

     width  40   -> 50
     day    21   -> 26
     range  14   -> 17.5   (line box pinned to the single-day height, as below)
     month  8.5  -> 10.5

   The rounding follows §05's own worked example, which reads a drawn 9.5 as "a
   legal 12 on the tablet". */
.rd-tev-row__date {
  width: 50px;
  flex: none;
  text-align: center;
}

.rd-tev-row__day {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 26px;
  line-height: 1;
  color: var(--rd-fg);
}

.rd-tev-row__day--past {
  color: var(--rd-fg3);
}

/* ⚠️ THE LINE-HEIGHT IS THE HALF THAT KEEPS THE PROMISE, exactly as it is on the
   phone card. 26px at line-height 1 and 17.5px at line-height 26px are the same
   line box, so a range row's title sits on the baseline a single-day row's does.
   Changing the size without it re-flows every row in the pane — which is the one
   thing the 18 Aug ruling ("the block's 40px never moves and titles stay
   aligned") exists to prevent. */
.rd-tev-row__day--range {
  font-size: 17.5px;
  line-height: 26px;
}

.rd-tev-row__month {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .14em;
  font-size: 10.5px;
  color: var(--rd-fg3);
}

/* Cross-month only: `Aug–Sep` is nearly twice the glyphs of `Aug` in the same
   block, so the drawing spends the tracking rather than the width. A same-month
   range keeps `.14em` — its month did not get any longer. */
.rd-tev-row__month--range {
  letter-spacing: .02em;
}

.rd-tev-row__copy {
  flex: 1;
  min-width: 0;
}

.rd-tev-row__name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .01em;
  font-size: 13px;
  line-height: 1.15;
  color: var(--rd-fg);
}

/* ⚠️ 11, AND THE DRAWING SAYS 10.5. These two lines are mixed case — an org's
   name, a location, a drill count — so §05's mixed-case floor governs and the
   scale outranks the drawing. The same correction the rail's sublines took when
   the tablet scale landed. */
.rd-tev-row__who {
  margin-top: 4px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* Where on the range, under the line that names it (C-56.9.1). Same 11px as the
   two lines around it — §05's mixed-case floor governs here, the same
   correction `__who` above carries. */
.rd-tev-row__where {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

.rd-tev-row__meta {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

.rd-tev-row__chip {
  margin-top: 5px;
}

/* ---------------------------------------------------------------------------
   4. The detail pane's head — the name, the when-line, the section strip.
   --------------------------------------------------------------------------- */
/* `flex: none` for §2's reason: the pane scrolls, so this block must keep its
   content height rather than be sized to the pane and clipped. A flex COLUMN
   because the head sits above whichever section the route names. */
.rd-tev-d {
  flex: none;
  display: flex;
  flex-direction: column;
}

/* `flex: none` and no scroll of its own — see the banner. The head is fixed in
   the drawing because the drawn body scrolls; here the PANE scrolls, so the head
   moves with it. */
.rd-tev-d__head {
  flex: none;
  padding: 20px 28px 0;
}

.rd-tev-d__name {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 900;
  font-stretch: 62%;
  letter-spacing: .01em;
  font-size: 24px;
  line-height: 1.05;
  color: var(--rd-fg);
}

/* The drawn `{who} · {when} · {where}` line. Its content is `when_line`, which
   carries the 18 Aug ruled date forms and is shared with the phone screen —
   the full ruled form lives on the detail line, and the list's numeral block is
   "a glance, not the record". */
.rd-tev-d__when {
  margin-top: 7px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg3);
}

/* Line 2 — Where on the range, under the line that names it (C-56.9.1). The
   pane's own geometry for the split the phone detail draws. */
.rd-tev-d__where {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg3);
}

/* THE DETAIL / STANDINGS STRIP. Screen work rather than `RdSectionTabs`, for the
   two reasons in the screen's module doc: the kit's row reads a nav table in
   which Events is a ratified leaf, and it renders in the SHELL's band rather
   than inside a pane.

   Selection is the underline plus full-strength text, never a filled pill —
   the same rule the kit's row states, and for the same reason: a pill row under
   a page header competes with the header for the top of the screen. */
.rd-tev-d__tabs {
  display: flex;
  gap: 8px;
  margin-top: 15px;
  border-bottom: 1px solid var(--rd-line);
}

/* ⚠️ 44px, AND THE DRAWING DRAWS ~21. The drawn tab is `padding: 0 3px 10px`
   around a line of 10.5px type, which is a 21px target. rd-tablet-kit's own
   rule: "TOUCH SIZES DO NOT SHRINK … The same build serves an iPad and a laptop
   browser, and there is no way to know which is in front of you." So the box is
   44 and the ink is unmoved: `align-items: flex-end` with the drawn 10px of
   bottom padding puts the underline exactly where the drawing puts it, and the
   extra height is added above the word where there is nothing to disturb. */
.rd-tev-d__tab {
  display: inline-flex;
  align-items: flex-end;
  min-height: 44px;
  margin-right: 18px;
  padding: 0 3px 10px;
  border-bottom: 2px solid transparent;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 10.5px;
  color: var(--rd-fg3);
  text-decoration: none;
  cursor: pointer;
}

.rd-tev-d__tab:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-tev-d__tab--on {
  border-bottom-color: var(--rd-acc);
  color: var(--rd-fg);
}

/* ---------------------------------------------------------------------------
   5. The detail pane's body, and the standings mount.
   --------------------------------------------------------------------------- */
.rd-tev-d__body {
  padding: 18px 28px 28px;
}

/* ---------------------------------------------------------------------------
   5b. Your own RSVP, boxed, at phone width.
   --------------------------------------------------------------------------- */
/* DRAWN: `41 (Tablet) Event Logistics - Participant and Guest`,
   `rd-tablet-logistics.jsx:37`. The block itself is the PHONE's — one component,
   mounted at both widths, ruled that way by the drawing:

     "THE RSVP BLOCK DOES NOT CHANGE. It is the phone's block, at the phone's
      width, in the left pane — because answering yes is one decision and 25
      dollars is one number, and neither gets easier to read spread across
      700px."  (`rd-tablet-logistics.jsx:14-16`)

   So this section declares the box, the cap and three overrides, and nothing
   about the block's interior. `.rd-evd__rsvp*`'s own values live in `s15.css`
   and are not restated here — restating them is how two widths drift.

   ⚠️ THE CAP IS THE RULING, NOT A TASTE. The detail pane at this width is far
   wider than 400px, and a block that filled it would spread one question and one
   number across the whole pane, which is the sentence above in as many words.
   352 = the drawn column's 400 less its own `padding: '20px 24px'`
   (`rd-tablet-logistics.jsx:27`) — the width the box actually occupies there,
   carried rather than re-derived. */
.rd-tev-d__rsvp {
  max-width: 352px;
  /* `border: 1px solid t.line, padding: 15` — the tablet's one added geometry
     (`rd-tablet-logistics.jsx:37`). The phone draws no box at all. */
  border: 1px solid var(--rd-line);
  padding: 15px;
  /* The drawn column's own `gap: 16` between the box and "The plan" under it
     (`rd-tablet-logistics.jsx:27`). */
  margin-bottom: 16px;
}

/* ⚠️ THE BOX REPLACES THE PHONE'S RULE — it does not sit on top of it. `s15.css`
   gives the block `margin-top: 14; padding-bottom: 14; border-bottom` because on
   the phone a hairline UNDER the block is what separates it from the head
   (`rd-logistics-guest.jsx:71`). Inside a border there is nothing left to
   separate, and keeping both would draw a second line one pixel inside the
   first. */
.rd-tev-d__rsvp .rd-evd__rsvp {
  margin-top: 0;
  padding-bottom: 0;
  border-bottom: 0;
}

/* ⚠️ `01 Foundations` §05, THE FOURTH APPLICATION IN THIS FILE. "Mixed-case text
   never goes below 11px on the tablet." The phone's note is 10 and mixed case
   (`rd-logistics-guest.jsx:78`), so it moves to 11 here — the same lift §2, §3
   and §6 already make, and for the same reason: the scale outranks the drawing.
   The tablet's own drawing does not draw this note at all, so there is no tablet
   value to contradict. */
.rd-tev-d__rsvp .rd-evd__rsvp-note {
  font-size: 11px;
}

/* THE TABLET'S COMPLETED STATE, and it is genuinely its own sentence rather than
   a transcription slip: `This event is over. Responses are closed.` with no
   label and no read-back above it (`rd-tablet-logistics.jsx:39`), where the
   phone draws all three (`rd-logistics-guest.jsx:76-78`). Each width ships as
   drawn and the divergence is a designer question.

   Drawn values, unchanged: `fontSize: 12.5, color: t.fg3, lineHeight: 1.55`. It
   is 12.5 mixed case, which clears the §05 floor on its own. */
.rd-tev-d__rsvp .rd-evd__rsvp-over {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   5a. The pane entering — the head is REAL, the body is the claim.
   --------------------------------------------------------------------------- */
/* loading-and-rulings-reply.md:34-37: "Chrome is code and renders real — tab
   strip, pane head, list column — only the body the fetch owns skeletons. …
   carried-forward facts (the tapped row's name, date) render as text from the
   first frame."

   Two rules and no geometry of their own beyond rhythm: the kit primitives carry
   their own sizes (kit-loading.css), and what a screen owns is the space between
   them. */
.rd-tev-d__skel {
  display: grid;
  gap: 12px;
}

/* The name line's stand-in, for a DEEP LINK only — an event opened from the list
   pane arrives with its name and renders it as text. The height is the drawn
   name's line box, so the tab strip below does not move when the fetch lands. */
.rd-tev-d__skel-name {
  min-height: 25px;
  padding-top: 6px;
}

/* The list column entering. The column itself is chrome and keeps its own
   padding; this is the stack's inset inside it. */
.rd-tev__skel {
  padding: 12px 14px;
}

/* ⚠️ A MOUNT SITE AND NOTHING ELSE. `EventStandingsBody`'s interior is another
   lane's custody, so this rule gives the shared board the drawn pane's inset and
   declares not one thing about what is inside it. The drawn tablet standings are
   two-up in a 1fr/1fr grid; that is a change INSIDE the body and it is routed to
   its owner rather than reached into from here.

   ⚠️ AND IT IS NOT GIVEN A DEFINITE HEIGHT, DELIBERATELY. `.rd-stand__list`
   carries `flex: 1; min-height: 0; overflow-y: auto` — a scroller sized against
   whatever bounds it. Bounding it here would put a second scrollbar inside a
   pane that already has one, for a board the drawing says fits without
   scrolling ("Sixteen names ... fit two-up without scrolling"). With this
   block at its content height the body's `flex: 1` resolves to content and its
   `overflow-y: auto` never fires, so the pane's single scroll carries the whole
   tab. Nothing inside the body is touched to achieve it. */
.rd-tev-d__stand {
  padding: 18px 28px 28px;
}

/* ⚠️ 9.5 AGAIN, AND THE DRAWING SAYS 9 — the same floor the group headings take
   in §2. This is the drawn "What you are shooting" eyebrow and the two carried
   section headings under it. It is NOT `RdLabel`: that role is 8.5px, which is
   below every legal tablet size, so the kit's eyebrow cannot be reused inside a
   tablet frame without an override that would reach into another part. */
.rd-tev-d__lab {
  margin-bottom: 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* An empty section gets copy instead of vanishing — the phone screen's headline
   decision for this event, and it carries unchanged. */
.rd-tev-d__sec-empty {
  margin: 0;
  max-width: 58ch;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--rd-fg3);
}

.rd-tev-d__stages {
  display: block;
}

/* One stage: the drawn `01 · name · meta` row at 52px, with the phone screen's
   two doors kept on it. The drawn row is inert; an inert row here would mean a
   shooter holding an iPad on a bay cannot score, and `15 Events` row 03 rules
   that scoring lives on the drill row. */
.rd-tev-d__stage {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 52px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-tev-d__stage-open {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 16px;
  min-height: 52px;
  text-decoration: none;
  cursor: pointer;
}

.rd-tev-d__stage-open:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* `rd-num` beside this class carries the tabular figures; the width is what
   keeps `01` and `10` in one column. */
.rd-tev-d__stage-n {
  width: 22px;
  flex: none;
  font-size: 11px;
  color: var(--rd-fg3);
}

.rd-tev-d__stage-name {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg);
}

.rd-tev-d__stage-meta {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* SCORE is an accent instruction and SHOT is a spent one — the phone row's own
   split, in the slot the drawn hit factor would head. ⚠️ The hit factor itself is
   still absent on both devices: `GetEvent` returns no score rows, and a mono
   figure in that slot reads as a hit factor. */
.rd-tev-d__stage-act {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 11px;
  color: var(--rd-acc);
}

.rd-tev-d__stage-act--shot {
  color: var(--rd-fg3);
}

.rd-tev-d__stage-squad {
  flex: none;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 4px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 11px;
  color: var(--rd-fg3);
  text-decoration: none;
  cursor: pointer;
}

.rd-tev-d__stage-squad:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* ⚠️ THE DRAWN SENTENCE, and the document says why it is here: "The stage list
   keeps its numbered order and its sentence … That is the fact a shooter most
   often tests on this screen." 58ch is the drawn measure. */
.rd-tev-d__note {
  margin-top: 16px;
  max-width: 58ch;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   6. Carried, not drawn — the chips, the roster and the foot.
   --------------------------------------------------------------------------- */
/* Each of these is the only path to its data at this width. The drawn tablet
   pane is a reading surface and the shipped phone screen is a working one;
   `event_detail.rs` settled the rule in the same drawing's roster note —
   "Build-what-is-drawn governs how a thing looks; it has never authorised
   removing the only path to data." They sit BELOW the drawn material so the
   drawn reading order is unchanged. */
.rd-tev-d__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 20px;
}

.rd-tev-d__sec {
  margin-top: 24px;
}

.rd-tev-d__roster {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
}

.rd-tev-d__person {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 32px;
}

.rd-tev-d__person-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg);
}

.rd-tev-d__person-mark {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* Capped rather than full-bleed: the detail pane is 436px at the breakpoint and
   grows to 1000-odd on a laptop, and a Save-width button stretched across a
   desktop pane is the thing the desktop-growth ruling is about. */
.rd-tev-d__foot {
  display: grid;
  gap: 8px;
  margin-top: 24px;
  max-width: 320px;
}

.rd-tev-d__foot-note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   7. The undrawn state the pane can still reach.
   --------------------------------------------------------------------------- */
/* `GetEvent` can answer Ok with no event — a deleted event, or a link for an org
   you are not in. Kept for the phone screen's reason: "not in the drawing" never
   deletes a state the page can reach. */
.rd-tev-d__gone-lead {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .04em;
  font-size: 16px;
  color: var(--rd-fg);
}

.rd-tev-d__gone-body {
  margin-top: 8px;
  max-width: 58ch;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  line-height: 1.55;
  color: var(--rd-fg3);
}
/* ==== S18-TABLET · 18/19 (Tablet) Profile — the identity hub · owner: lane-3 ==== */

/* ⚠️ ADDED 2026-08-18. Drawn as `ScreenTabProfile` in `18 (Tablet) Profile`
   (four frames: Achievements in the pane, Settings in the pane, Edit Profile in
   the pane on light, and a new account) and as the two panes of `19 (Tablet)
   Profile Pages`. Screen: apps/web/src/pages/profile_tablet.rs, which carries
   the hub ruling, the deep-link answer, and every carried / skipped /
   wire-blocked flag. Only the values live here.

   ANATOMY — everything below sits INSIDE the substrate, and this part adds no
   frame of its own:

     RdTabletShell [ rail | Profile | (no section band) | modal slot ]
       RdHubRail width=W340   -> the column, the pinned head, the rows
         head  -> .rd-tprof__ident + .rd-tprof__code   (this file)
       .rd-tprof__pane        -> whichever pane the route names
         Achievements -> .rd-tprof__ach-*              (this file)
         Edit Profile -> .rd-tprof__eyebrow + .rd-eprof (s19-doors.css)
         Settings     -> .rd-tprof__form--rows + .rd-nprefs (s19-prefs.css)
         Edit Account -> .rd-tprof__form--rows + .rd-eacct (s19-doors.css)

   THE COLUMN ITSELF IS NOT HERE. `.rd-thub*` is kit-tablet-hub-rail.css — the
   width, the pinned head's geometry, the row densities, the selection idiom and
   the scroll are all the primitive's, and this file neither declares nor
   overrides one of them. What is here is what goes INSIDE the head, and the pane
   beside it.

   ⚠️ THIS PART DECLARES THREE SELECTORS IN ANOTHER FILE'S BLOCK, and that is
   deliberate rather than an accident. `.rd-eprof__field--beside`,
   `.rd-eprof__hint` and `.rd-eprof__discard` extend `.rd-eprof*`, which is
   declared in s19-doors.css (owner: lane-lib). The markup they paint is in
   `pages/edit_profile.rs`, whose form both device classes mount — the tablet
   arm asks for the drawn `330px | 1fr` measure and s19-doors.css has no reason
   to carry a tablet-only modifier. The names are disjoint from every selector
   that file declares, and this is the same two-owners-on-one-BEM-block
   arrangement s18-foot.css records against profile-community.css. It merges into
   s19-doors.css whenever those two lanes are one.

   NO MEDIA QUERY IN THIS FILE, DELIBERATELY. The tablet substrate is entered by
   COMPONENT, not by rule: `crate::device::DeviceClass` picks RdTabletShell over
   RdShell and these classes only ever render inside it. kit-tablet-shell.css §8
   is the one place a width query belongs, and its own banner argues there must
   be exactly one — a second breakpoint here would be a second number to keep in
   step with `TABLET_MIN_WIDTH_PX`. Pinned by
   `ruling_the_screen_part_carries_no_breakpoint_of_its_own` in
   profile_tablet.rs, which reads THIS FILE.

   ⚠️ THE TYPE FLOORS OF `01 Foundations` §05 (18 Aug), APPLIED TWICE.
   "Mixed-case text never goes below 11px on the tablet. Condensed uppercase
   labels with tracking may go to 9.5px … Where an existing tablet drawing shows
   mixed-case below 11, build 11: the scale outranks the drawing." The drawing
   puts two sizes under the floors and both move here — §2 (the join line, drawn
   10 mixed case) and §3 (the MY CODE caption, drawn 8.5 tracked uppercase).
   Every size in this file is swept by `ruling_no_type_falls_below_the_tablet_floor`
   in profile_tablet.rs, which also reads THIS FILE.

   ⚠️ THE OVERLAY IS BORROWED AND NOT REDECLARED. The MY CODE modal uses
   `.rd-prof__over*` (s18-foot.css) unchanged — the same scrim, panel, close row
   and 250px code width the phone hub opens. It is only ever mounted in the
   shell's `modal` slot, which is a sibling of the frame, so its `position:
   fixed; inset: 0` covers the rail and the hub as well as the pane. */

/* ---------------------------------------------------------------------------
   1. The pane — the fluid half of the content row.
   --------------------------------------------------------------------------- */
/* rd-tablet-identity.jsx:54 ("flex: 1, minWidth: 0, padding: '20px 26px'").

   ⚠️ `overflow-y: auto` WHERE THE DRAWING WRITES `overflow: hidden`, the same
   stated deviation kit-tablet-panes.css and kit-tablet-hub-rail.css both make:
   nothing above this scrolls (`.rd-tshell` is `overflow-y: hidden` and
   `.rd-tshell__content` is `overflow: hidden`), so a roster of forty rows or a
   settings page of two groups would run off the bottom with no scrollbar
   anywhere on screen to say so. The hub column beside it owns its own scroll and
   the two are independent, which is the point of the two-column reading. */
.rd-tprof__pane {
  flex: 1;
  min-width: 0;
  padding: 20px 26px;
  overflow-y: auto;
  overflow-x: hidden;
  box-sizing: border-box;
  font-family: 'Archivo', system-ui, sans-serif;
}

/* ---------------------------------------------------------------------------
   2. The pinned identity block — what goes INSIDE the hub's head.
   --------------------------------------------------------------------------- */
/* rd-tablet-identity.jsx:33 — the head is `flex, alignItems center, gap 13,
   padding '18px 18px 16px'`, and every one of those numbers belongs to
   `.rd-thub__head` in the primitive. The three children are the caller's: an
   avatar, this text column, and the code button.

   ⚠️ NEVER SELECTABLE, AND THE PRIMITIVE IS WHAT ENFORCES IT. There is no
   `--on` state anywhere in this section and there must not be one:
   rd-tablet-identity.jsx:30 ("The person, not a row") — "The person, not a row.
   It never drives the pane and never scrolls away." */
.rd-tprof__ident {
  flex: 1;
  min-width: 0;
}

/* rd-tablet-identity.jsx:36 ("fontSize: 18, lineHeight: 1") — `tdisp(900, 62,
   '.01em'), fontSize 18, lineHeight 1, color t.fg`. The display voice, at the
   one size on this screen that is a name rather than a label. */
/* ⚠️ `line-height` 1.25, NOT the drawn 1 — the name line WRAPS now. C-44.1
   (2026-08-27): *"long names take two lines, never an ellipsis."* The call site
   carries `rd-clamp-2` (it carried `rd-clamp-1`, which is the same cut by
   another route), and at 1 the second line's caps touch the first line's
   descenders at 900/62%. 1.25 is not invented: it is the door's own name-line
   value for the same two-line case (`rd-doors.jsx:48` — `lineHeight: 1.25`).
   The phone twin took the identical change in `profile-community.css`. */
.rd-tprof__name {
  text-transform: uppercase;
  font-weight: 900;
  font-stretch: 62%;
  letter-spacing: .01em;
  font-size: 18px;
  line-height: 1.25;
  color: var(--rd-fg);
  overflow-wrap: anywhere;
}

/* rd-tablet-identity.jsx:37 ("fontSize: 11, color: t.fg3") — the handle, or the
   drawn invitation when there is no handle set. Both arms come from
   `profile.rs`'s `tag_line`, so the two device classes cannot word it
   differently. */
.rd-tprof__tag {
  margin-top: 4px;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* rd-tablet-identity.jsx:38 ("fontSize: 10, color: t.fg3").

   ⚠️ 11px, NOT THE DRAWN 10. `Since Mar 2023` is a mixed-case sentence and
   `01 Foundations` §05 puts the tablet's mixed-case floor at 11; the 9.5
   exemption is for condensed uppercase with tracking, which this is not. The
   same correction kit-tablet-hub-rail.css records for the hub's own sub-line,
   and the same one kit-tablet-rail.css records for the rail's subtitle. */
.rd-tprof__since {
  margin-top: 2px;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   3. The MY CODE button — a labelled icon, never a pane.
   --------------------------------------------------------------------------- */
/* `PIconBtn` (rd-profile.jsx:51): a 44x44 tap target holding a chamfered 34x34
   outline, with a tracked caption under it. The document's own rule is why the
   caption exists at all: "a gear is guessable, a QR square is not."

   `18 (Tablet)`, Decided — This Device: "My Code stays an icon button in the
   header, not a pane. It is an overlay on the phone and an overlay here — a QR
   code shown to somebody standing next to you does not want to be half a screen
   wide."

   The chamfered box is `RdChamferBox`, which paints its own edge and fill; this
   sets only the geometry around it and the two things a <button> brings with it
   that the kit cannot know about. */
.rd-tprof__code {
  flex: none;
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  color: var(--rd-fg2);
}

.rd-tprof__code-inner {
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* rd-tablet-identity.jsx:40 via PIconBtn's `cap` (rd-profile.jsx:59,
   "fontSize: 8.5").

   ⚠️ 9.5px, NOT THE DRAWN 8.5. Tracked uppercase may go to 9.5 on the tablet
   and no further (`01 Foundations` §05). This is the exempt role and it is
   still floored — the exemption has a number, not an absence of one. */
.rd-tprof__code-cap {
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9.5px;
  line-height: 1;
  color: var(--rd-fg3);
}

.rd-tprof__code:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* ---------------------------------------------------------------------------
   3b. Sign Out — the hub column's foot, and UNDRAWN.
   --------------------------------------------------------------------------- */
/* Ruled onto the hub by James (batch 27); `18 (Tablet)` draws nothing under the
   rows, and `19` draws this control one level in, on Edit Account, where it also
   stays. It rides `RdHubRail`'s `foot` slot rather than the row list because
   `HubRow.to` is required and this control leads nowhere — it acts.

   The geometry is the tall density's, so it lines up with the six rows above it
   rather than announcing itself as a different kind of object: 62px, the rows'
   own `0 18px` gutter plus their 3px transparent left rule, so the label sits on
   the same vertical as every label above it. It is a stack rather than a row
   because the sub-line is the whole of its right-hand side — there is no count
   and no chevron, since a chevron would promise a screen this control never
   opens.

   No red. `19` spends danger ink on this control where it sits beside Delete
   Account and has to be told apart from it; nothing red is adjacent here, and
   the ruling is a plain link row. The two sizes are the rows' own — 13 for the
   label and the 11px mixed-case floor (`01 Foundations` §05) for the sub. */
.rd-tprof__signout {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  width: 100%;
  min-height: 62px;
  padding: 0 18px;
  border: 0;
  border-left: 3px solid transparent;
  background: none;
  text-align: left;
  font-family: 'Archivo', system-ui, sans-serif;
  cursor: pointer;
  box-sizing: border-box;
}

.rd-tprof__signout:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-tprof__signout-name {
  font-size: 13px;
  color: var(--rd-fg2);
}

.rd-tprof__signout-sub {
  font-size: 11px;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   4. The pane's own eyebrow — where a pane has a title and the shell does not.
   --------------------------------------------------------------------------- */
/* rd-tablet-identity.jsx:140 ("Edit profile") — `tdisp(700, 78, '.12em'),
   fontSize 9, color t.fg3, marginBottom 10`.

   ⚠️ NOT AN `h1`, AND THAT IS THE POINT. `RdTabletShell` already renders the
   only top-level heading on this screen (the drawn TabShell title, `Profile`),
   so the pane names itself with a tracked label instead. `RdLabel` carries the
   type role; this carries the space under it.

   ⚠️ THE SETTINGS PANE HAS NONE, and that is drawn rather than forgotten:
   `SettingsPane` opens straight on its first group label, and the lit hub row is
   what says which pane you are in. */
.rd-tprof__eyebrow {
  margin-bottom: 10px;
}

/* ---------------------------------------------------------------------------
   5. The two mounted forms, and the measures `19 (Tablet)` rules for them.
   --------------------------------------------------------------------------- */
/* Neither form is re-implemented here — `pages/edit_profile.rs` and
   `pages/notification_prefs.rs` each render one tree that both device classes
   mount. What the tablet supplies is the measure, and the drawing states both:

     row 01 — "Fields stay 330px. Width goes to the explanation, not the input.
       Nothing about a wider text box makes a display name easier to type."
     row 02 — "Settings keeps its groups and its toggle treatment. Notifications
       above, Display below, 620px maximum — a toggle row stretched to 1000px
       puts the switch a long way from the thing it switches." */
.rd-tprof__form {
  max-width: 980px;
}

/* ⚠️ NAMED FOR THE SHAPE, NOT FOR THE SCREEN — it was `--settings` until
   2026-08-19, when it got a second caller.

   `AccountPane` (rd-tablet-identity.jsx:182, row 03, drawn 18 Aug) takes the
   same 620, and it takes it for the same reason rather than by coincidence:
   both panes are a stack of full-width ROWS whose control sits at the right-hand
   end, so the cap is what keeps the control near the label it belongs to. Row 01
   (Edit Profile) is the other shape — a 330px field with the width spent on the
   explanation beside it — and it keeps 980 above.

   A modifier that named `settings` would have made the second caller either
   claim to be Settings or restate 620 under a second name, and two statements of
   one number drift. Same rule the kit's `CardWidth` states about naming variants
   after their callers. */
.rd-tprof__form--rows {
  max-width: 620px;
}

/* rd-tablet-identity.jsx:130 — the drawn field row: `display flex, gap 26,
   padding '13px 0', borderBottom 1px solid t.line`, a 330px left column and a
   `flex: 1` hint beside it.

   BUILT AS A GRID RATHER THAN AS THE DRAWN FLEX ROW, for one reason: the Tag
   field's three children (label, value, hint) have to split across the two
   columns, and a flex row would need a wrapper element around the first two —
   an element that would then exist on the phone as well, where the drawing has
   no wrapper and the shipped markup has none. A grid places existing children;
   flex would require new ones. */
.rd-eprof__field--beside {
  display: grid;
  grid-template-columns: 330px 1fr;
  column-gap: 26px;
  align-items: start;
  padding: 13px 0;
  border-bottom: 1px solid var(--rd-line);
}

/* Everything the form already renders stays in the left column; only the hint
   crosses. `grid-column: 1` on the children rather than `auto-flow` on the
   parent, so a field that grows a fourth element lands under the value rather
   than silently in the hint's column. */
.rd-eprof__field--beside > .rd-field {
  grid-column: 1;
}

/* rd-tablet-identity.jsx:135 ("fontSize: 11.5, color: t.fg3") — `flex 1,
   minWidth 0, tbody, fontSize 11.5, color t.fg3, lineHeight 1.5, paddingTop 24`.
   The top padding is what puts the first line of the explanation level with the
   field it explains rather than with the label above it.

   ONE CLASS NOW. This used to name `.rd-eprof__note` too — the phone's own note
   under the read-only tag VALUE, which was already a sibling and so crossed on a
   placement alone. #1151 made the tag a field, so its hint travels the way the
   display name's always has: `RdTextField`'s own status line on a phone, this
   sibling beside it on a tablet. The drawing's rule is unchanged and is the same
   sentence for both: "Every hint is the phone's hint, moved rather than
   rewritten." */
.rd-eprof__field--beside > .rd-eprof__hint {
  grid-column: 2;
  grid-row: 1 / -1;
  min-width: 0;
  margin: 0;
  padding-top: 24px;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

/* ⚠️ **`.rd-eprof__discard` LEFT THIS FILE 2026-08-28.** It was declared here
   as a tablet-only extension of `.rd-eprof*` — `19 (Tablet)` row 01 draws a
   quiet Discard beside a 240px Save and `19 (Mobile)` row 03 draws no Discard
   at all. #1354's foot-act ruling gives the phone the same inline pair, so the
   control is on both device classes and its rule moved to the part that owns
   the block, `s19-doors.css`. Nothing tablet-only is left to say about it.

   (Its history, kept because it is the same defect twice: the rule used to be
   the ONLY half that was built, so the pane still mounted the phone's PINNED
   bar — `flex: none`, a hairline, a full-bleed negative gutter. That was
   corrected on 2026-08-26 by the override below, and the phone has now been
   corrected the same way at the source.) */

/* The pair. `19 (Tablet) Profile Pages` row 01: *"Save stays a 240px button
   with Discard as quiet text. The phone's save bar becomes an inline pair,
   because there is no scroll to pin it to."* Drawn at
   `components/rd-tablet-identity.jsx:144-147` — a flex row, `gap: 14`,
   `marginTop: 18`, a 240px Save and the Discard beside it. No rule above it and
   no full bleed, because neither is drawn and neither has a job: a hairline
   separating a bar from the page is what a PINNED bar needs to sit on, and this
   one scrolls with the form.

   ⚠️ SCOPED BY MARKUP, NOT BY A BREAKPOINT. `.rd-tprof__form` is the pane
   wrapper `profile_tablet.rs` puts the tablet form in, and it renders only
   inside `RdTabletShell` — so this reads as "the bar, when the tablet pane is
   the host", which is the substrate rule this file's banner states. A media
   query here would be the second breakpoint the banner forbids.

   It also drops `margin: 10px calc(-1 * var(--rd-shell-gutter)) 0`, which was a
   second defect at the same site: `--rd-shell-gutter` is `RdShell`'s variable
   and the tablet pane never sets it, so the phone rule bled the bar against a
   value that is not there. */
.rd-tprof__form .rd-eprof__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  margin: 18px 0 0;
  padding: 0;
  border-top: 0;
  background: none;
}

/* 240px, the drawn number — not a percentage of a pane whose width is the
   window's. `RdButton` is full-width by default, which is the phone's bar and
   not this one. */
.rd-tprof__form .rd-eprof__bar > .rd-btn { width: 240px; }

/* The two full-width members of the row. Neither is drawn — the error banner is
   the page's own and the offline note is ours (`SAVE_OFFLINE_NOTE`) — but both
   are prose about the whole act, so they take their own line rather than
   sharing one with the pair. */
.rd-tprof__form .rd-eprof__bar > .rd-eprof__banner,
.rd-tprof__form .rd-eprof__bar > .rd-eprof__bar-msg {
  flex-basis: 100%;
  margin: 0;
}

/* ---------------------------------------------------------------------------
   6. The roster — `AchGrid`, in BOTH of its drawn forms.
   ---------------------------------------------------------------------------
   ⚠️ THIS BLOCK SERVES TWO DOCUMENTS AND ONE COMPONENT. Terse and three columns
   is the hub pane (`18 (Tablet) Profile`, `ScreenTabProfile`); full and four
   columns is the standalone page (`20 (Tablet) Achievements` row 01,
   `ScreenTabAchievements`, screen `apps/web/src/pages/achievements_tablet.rs`).
   The drawing shares one `AchGrid` between them and so does the code — one
   `AchievementsPane` with a `terse` flag — so the rules live together and the
   two forms are modifiers rather than a second block. Splitting them would put
   the shared 90% in whichever file was written second.
   --------------------------------------------------------------------------- */
/* rd-tablet-identity.jsx:83 — the head is `display flex, alignItems baseline,
   gap 14, marginBottom 13`, an eyebrow beside an accent count.

   ⚠️ THE COUNT IS DERIVED AND NEVER TYPED, which is the screen's business; what
   is here is that it wears the kit's NUMERIC role rather than a system mono.
   Substrate §P2: "Numbers are the loudest thing on a scoring surface, so they
   speak in the display voice." `tabular-nums` is what stops `9` jittering into
   `10`.

   ⚠️ IT WAS A FRACTION UNTIL 2026-08-29 — James ruled that only earned
   achievements are listed or returned, so there is no denominator to state. */
.rd-tprof__ach-head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-bottom: 13px;
}

.rd-tprof__ach-count {
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--rd-acc);
}

/* rd-tablet-identity.jsx:87 ("gap: '0 22px', alignContent: 'start'") — three columns
   in the pane and four on the standalone page, and the drawing says why the
   number is a fit rather than a taste (rd-tablet-identity.jsx:72, "COLS IS A
   FIT, NOT A STYLE CHOICE"): "Forty rows plus eight category heads is taller
   than 737px of pane at any comfortable row height, so the standalone runs four
   columns and the profile pane runs three."

   ⚠️ THE DRAWN CATEGORY GROUPING LANDED 2026-08-18, and this note is corrected
   rather than deleted. It read "THE DRAWN CATEGORY GROUPING IS ABSENT AND THE
   SCREEN'S DOC SAYS WHY: the record on the wire carries no category" — true when
   written, and #894 put `category` on the record. So the grid's children are now
   the eight groups (`.rd-tprof__ach-group`), each a category head over its own
   rows, and the three columns flow those blocks rather than bare rows.

   `grid-auto-flow: column` is STILL not used, and the reason has changed with
   the markup: each group is one cell that grows to its own height, so the
   default row-major flow lays the eight blocks across three columns exactly as
   the drawing does. Column-major would balance the columns by height instead,
   which is a different picture and not the drawn one. */
.rd-tprof__ach-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0 22px;
  align-content: start;
}

/* THE FULL SET'S COLUMNS — `/profile/achievements`, ruled by James 2026-08-28
   (relay Q247): the hub column stays and the pane swaps from the preview to the
   set, with "as many columns as the pane fits".

   ⚠️ `auto-fill`, AND THE FLOOR IS READ OFF THE DRAWING RATHER THAN CHOSEN.
   rd-tablet-identity.jsx:72 ("COLS IS A FIT, NOT A STYLE CHOICE") rules that the
   count is whatever fits, and the drawn `cols={4}` is that rule applied to the
   standalone page's width. The ruling puts the same rows in a narrower pane, so
   the COUNT cannot carry over — but the drawn column WIDTH can, and it is what
   the drawing actually priced a full, two-line row at:

     1194 (TAB_W)  -  208 (RAIL_W)                       =  986   the page
      986          -   48 (`padding: '18px 24px'`)       =  938   its content
      938          -   66 (three `gap: '0 22px'`)        =  872   its four columns
      872 / 4                                            =  218   one full row

   (rd-tablet-kit.jsx:19 for the two frame constants, rd-tablet-identity.jsx:114
   for the padding and the count, :87 for the gap.)

   So 218px is the width a full row was drawn to occupy, and `auto-fill` spends
   the pane in units of it. Beside the 340 hub column that yields two columns
   (218*2 + 22 = 458, inside the pane's 594) and refuses a third (218*3 + 44 =
   698, which would clip) — which is the rule's own conclusion, reached by the
   stylesheet instead of by a second hand-counted number.

   ⚠️ A MODIFIER AND NOT A SECOND BLOCK, because it is the SAME GRID: one
   `AchievementsPane` serves both forms, exactly as one `AchGrid` does in the
   drawing. What changes is the column rule and the row height, and the drawing
   ties those two together — so they are one flag on the component and two
   modifiers here, never four independent knobs.

   ⚠️ `.rd-tprof__ach-grid--4` WAS HERE AND IS DELETED, not left "in case".
   It served `ScreenTabAchievements`, the standalone four-column page, and the
   ruling does not use that frame. A modifier whose only caller is a screen we
   decided not to build is a hole nobody decided to leave — `hub_rail.rs` refuses
   its 300px variant in the same words. */
.rd-tprof__ach-grid--auto {
  grid-template-columns: repeat(auto-fill, minmax(218px, 1fr));
}

/* ⚠️ THE SET'S FILTER STRIP RULE WAS HERE AND IS DELETED (2026-08-29). Q247
   gave the full-set pane "all sections/filters", so the pane grew an
   All/Earned/Locked chip strip and this rule spaced it off the grid. James then
   ruled that only earned achievements are listed or returned: two of the three
   chips named sets that cannot exist and the third named the whole list, so the
   strip went and this rule went with it. */

/* One category block. It carries no margin of its own — the head's own top
   padding is the drawing's separation (`padding: '9px 0 5px'`), and a margin
   here would double it above the first block in each column. */
.rd-tprof__ach-group { min-width: 0; }

/* rd-tablet-identity.jsx:90 — the category head: 8.5px display voice, fg3,
   `padding: 9px 0 5px` over a hairline.

   ⚠️ NOT `RdLabel`. The kit's micro-label is 8.5px at .2em tracking and this is
   the drawing's .12em at its own size; composing the primitive and then
   overriding both would be worse than declaring the one rule the drawing states.

   ⚠️ 9.5px, NOT THE DRAWN 8.5 — the third application of `01 Foundations` §05 in
   this file, on `.rd-tprof__code-cap`'s reasoning: tracked condensed uppercase
   may go to 9.5 on the tablet and no further, and the scale outranks the
   drawing. Caught by `ruling_no_type_falls_below_the_tablet_floor`, which is
   exactly the invisible failure that guard exists for — 8.5px renders fine and
   is simply illegible on the glass it was drawn for. */
.rd-tprof__ach-cat {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-fg3);
  padding: 9px 0 5px;
  border-bottom: 1px solid var(--rd-line);
}

/* rd-tablet-identity.jsx:92 ("minHeight: terse ? 29 : 38") — the pane's terse
   row, which is what makes the roster fit beside a 340 column at all. */
.rd-tprof__ach-row {
  min-height: 29px;
  display: flex;
  align-items: center;
  gap: 9px;
  border-bottom: 1px solid var(--rd-line);
}

/* The same declaration's other half — `minHeight: terse ? 29 : 38`. 38 is what
   the standalone page's second line costs, and the drawing's own note says the
   number is load-bearing: "the row is 38px rather than 46, which is what makes
   either of them fit." */
.rd-tprof__ach-row--full {
  min-height: 38px;
}

/* rd-tablet-identity.jsx:94 ("flex: 1, minWidth: 0") — the text column inside
   the row, present in BOTH forms; the terse one simply holds one child.

   ⚠️ THE `flex: 1; min-width: 0` MOVED HERE FROM `.rd-tprof__ach-name` in the
   same change, and that is a no-op for the terse row rather than a restyle: the
   name was the row's only growing child and now the body is. `min-width: 0` is
   what lets `rd-clamp-1` ellipsis at all — without it the flex child refuses to
   shrink below its text, which is the failure `.rd-thub__body` records one file
   over. */
.rd-tprof__ach-body {
  flex: 1;
  min-width: 0;
}

/* rd-tablet-identity.jsx:96 — the description the PANE drops and the PAGE keeps:
   "the pane is a preview of the set, the page is the set."

   ⚠️ 9.5px, NOT THE DRAWN 9 — `01 Foundations` §05's tablet floor, the same
   correction `.rd-tprof__ach-cat` carries three rules up, and pinned by the same
   `ruling_no_type_falls_below_the_tablet_floor` sweep over this file. 9px renders
   fine and is simply illegible on the glass it was drawn for. */
.rd-tprof__ach-desc {
  display: block;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* ⚠️ THE MASKED TIER'S ITALIC RULE WAS HERE AND IS DELETED (2026-08-29). It set
   the *hidden on purpose* sentence in the description's own slot, carried from
   the phone screen. Its whole population was the UNEARNED Keyword tier, and
   James ruled that only earned achievements are listed or returned — so the
   phone's mask block went, this slot's markup went with it, and the rule has no
   caller left. */

/* rd-tablet-identity.jsx:93 — `width 15, height 15, border 1.5px solid (e ?
   t.acc : t.ctrl), background e ? t.acc : transparent`. The drawing has two
   states and the pane now has one: rd-tablet-identity.jsx:64 ("THE ROSTER STOPS
   BEING A SCROLL") argues that "the unearned ones are the reason to look", and
   James ruled on 2026-08-29 that only earned achievements are listed or
   returned. So the earned paint is folded into the rule and the hairline state
   is gone with its modifier. */
.rd-tprof__ach-mark {
  width: 15px;
  height: 15px;
  flex: none;
  border: 1.5px solid var(--rd-acc);
  background: var(--rd-acc);
  box-sizing: border-box;
}

/* rd-tablet-identity.jsx:95 ("fontSize: 11, color: e ? t.fg : t.fg3") — full
   strength, which after the 2026-08-29 ruling is the only state a listed row
   has. The quiet half went with its modifier. */
.rd-tprof__ach-name {
  display: block;
  font-size: 11px;
  color: var(--rd-fg);
}

/* The two states the drawing has no frame for and the screen can still reach: a
   read that has not answered, and a definition list that came back empty. The
   screen's own note argues the difference — "a read that has not answered is not
   a roster with nothing in it." */
.rd-tprof__ach-msg {
  margin: 0;
  font-size: 12px;
  line-height: 1.6;
  color: var(--rd-fg3);
}
/* ==== S03-LF · 03 Training — the Live Fire menu · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   `/training` (`pages/live_fire.rs`). Drawn: **03 Training**, Stage Three,
   row 19 — `ScreenLiveFire`, in its quiet variant.

   Spec: `docs/design/kit/S3-livefire.md`.

   THE FOURTH PART OFF DRAWING 03, AND ALL FOUR ARE NAME-DISJOINT: `.rd-run*`
   (s03-run, Run & Score), `.rd-scorein__` per-step rows (s03-splits),
   `.rd-tdl*` (s03-drills, the drill index) and `.rd-lf*` here. None declares
   or overrides a selector another one names, so their relative order is not
   load-bearing.

   ⚠️ ALMOST NOTHING IS HERE, AND THAT IS THE POINT. The shell, the heading and
   the row container are kit — `RdShell`, `RdHeading`, `RdChamferBox`. What is
   left is four rows of layout and two type blocks the kit has no role for.
   §4 of the spec says the same thing about the tokens: no new ones, and the
   radius and the row height are page geometry rather than token-layer values
   because this screen is their only consumer.

   ⚠️ THE ROW NAME IS NOT A HEADING ROLE, and the divergence is the drawing's.
   The Home tile next door draws its name at 900/62%/14px, which is exactly
   `.rd-h1-alt`, so `home.rs` uses `RdHeading` and declares no type. This row
   draws at 800/78%/12.5px with .06em tracking — nearest neighbour is
   `.rd-btn-label` (800/78%/12.5px) and even that differs in tracking (.14em).
   One face at one site with no role to claim, so it is declared here rather
   than bent onto a role it is not.
   --------------------------------------------------------------------------- */

/* The page column. `paddingTop: 6` under the shell's own gutter. */
.rd-lf {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  padding-top: 6px;
}

/* `gap: 8, marginTop: 14`. A grid rather than a flex column: four rows of one
   shape, and the grid is what keeps them one shape when a name wraps. */
.rd-lf__rows {
  display: grid;
  gap: 8px;
  margin-top: 14px;
}

/* The whole row is the target — *"the name is the target"*, the same argument
   the Home tile makes for being full width. The link carries no decoration of
   its own; everything visible is the chamfer box inside it. */
.rd-lf__link {
  display: block;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}

.rd-lf__link:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* `padding: '13px 14px'`, `gap: 10`, `minHeight: 58`. */
.rd-lf__row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 58px;
  padding: 13px 14px;
  box-sizing: border-box;
}

.rd-lf__copy {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}

/* The section's word. See the header note on why this is declared and not a
   role. */
.rd-lf__name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 12.5px;
  line-height: 1;
  color: var(--rd-fg);
}

/* The quiet line under it — `marginTop: 3`, and the tertiary grey rather than
   the secondary one. It is the sentence a shooter reads once and then never
   again, so it sits a step below the word it explains. */
.rd-lf__sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.35;
  color: var(--rd-fg3);
  margin-top: 3px;
  text-wrap: pretty;
}

/* 7x12, --rd-fg3, stroke-width 1.6, square cap — the same mark the Home tiles
   and the queue rows carry. Sized here because the component sets no width or
   height attributes; `currentColor` takes the value. */
.rd-lf__chev {
  flex: none;
  width: 7px;
  height: 12px;
  color: var(--rd-fg3);
}
/* ==== S03-DRILLS · 03 Training — the drill index, and Assessments & Quals · owner: lane-reskin ==== */
/*
   `/training/drills` (`pages/training.rs`) and `/training/assessment-quals`
   (`pages/assessment_quals.rs`). Drawn: **03 Training**, `ScreenDrillList`,
   and **08 Assessments**' `AQRow` for the sibling page's version of the row.

   ⚠️ RENAMED FROM `train-drills.css`, AND THE INTERIM IT DECLARED IS OVER.
   The header this replaces called itself an HONEST INTERIM and said the part
   *"leaves with the page"* when the Live Fire hub landed. The hub has landed
   (`docs/design/kit/S3-livefire.md`, `styles/s03-lf.css`) and the page did NOT
   leave with it: the list moved down a level to `/training/drills`, which is
   where its whole route family already lived. What left is the IA the note was
   actually objecting to — the strip of seven sibling links that was this
   section's only structure. The rows, their geometry and the two chip colour
   roles are unchanged, so they stay, under a name and an owner that say what
   they are rather than how long they were expected to last.

   ⚠️ ONE RULE LEFT WITH THE STRIP: `.rd-tdl-queue-count`, the pending badge on
   the Queue link. Its count was the wrong count anyway — the queue's drawn door
   is a strip that appears for REFUSALS only (`.rd-q__door` in `s12.css`), on the
   grounds that *"a queue state that resolves itself does not get a link to a
   management screen."*

   THE FOURTH PART OFF DRAWING 03, AND ALL FOUR ARE NAME-DISJOINT: `.rd-run*`
   (s03-run), the `.rd-scorein__` per-step rows (s03-splits), `.rd-lf*`
   (s03-lf, the menu above this page) and `.rd-tdl*` here.

   Drawn sources, and they agree with each other:
     · `ScreenDrillList` — the corpus's own drill list, built deliberately on
       REAL names at REAL widths (a 103-character name and a 1-character name
       in the same list). It is where the row geometry, the 2-line name clamp
       and the 1-line subline come from.
     · `AQRow` — the sibling page's version of the same row, with the attribute
       chip in the right-hand column.

   Prefix `rd-tdl-` (train drill list). Kit primitives carry everything they can:
   `RdChamferBox` is the card, `RdChipStatus` is the attribute chip,
   `RdChipSelector` is the filter row, `RdHeading` is the title. What is left
   here is layout, two chip colour roles, and the quiet type the kit has no
   component for.
*/

/* ---------------------------------------------------------------------------
   color-scheme — APP-GLOBAL, and deliberately parked in a page part.

   Nothing in `apps/web` declared `color-scheme`, so the browser painted every
   scrollbar, every form widget and every overscroll gutter from the LIGHT UA
   palette regardless of the theme: white scrollbars down the side of a #0B0B0B
   app. This is the declaration that fixes it, and it fixes it everywhere, not
   only on these two pages.

   It belongs with the tokens it mirrors — but `_legacy-tokens.css` is UNOWNED
   and DELETE-ONLY, so no lane may add a rule to it, ever. A correct rule in a
   frozen file is not an option, so it lands in the newest owned part instead
   and says why. Move it to the token layer's successor when one exists.

   The keying matches `theme.rs` exactly and for the same reasons:
     · `:root` covers the DEFAULT — `theme::DEFAULT` is `Theme::Dark`, and
       `apply()` is best-effort, so a document with no attribute (tests, SSR, a
       failed stamp) must resolve dark, the same fallback the tokens take.
     · The attribute selectors are BARE, not `:root[data-theme=…]`. `/kit`
       re-scopes `data-theme` onto a subtree to render one theme inside the
       other, and a root-anchored selector would miss it.
     · No `@media (prefers-color-scheme)`. The theme is a stored preference —
       "a phone that flips to dark at sunset while someone is still on the line
       is worse than one that stays where they put it."

   ⚠️ `:root` and `[data-theme="light"]` have EQUAL specificity (0,1,0), so on a
   light document both match and SOURCE ORDER decides. Light must therefore stay
   below. Do not reorder these three rules.
*/
:root,
[data-theme="dark"] { color-scheme: dark; }

[data-theme="light"] { color-scheme: light; }

/* ---------------------------------------------------------------------------
   The page column.
*/
.rd-tdl {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* ---------------------------------------------------------------------------
   The header. THIS WAS THE HORIZONTAL-SCROLLBAR FIX, and the wrapping stays
   even though the overflow that forced it is gone.

   `kit-shell.css:299-317` carries the measurement: the header used to be an
   `h1` and a SEVEN-child link group in one nowrap flex row — 470px intrinsic
   inside a 416px box (the 448px cap less `p-4`'s 32px) — and
   `.rd-shell__content` declares `overflow-x: auto`, so the browser rendered a
   horizontal scrollbar and 54px of the strip was reachable only by dragging
   sideways.

   S3 took six of those seven links away, so the row no longer overflows at any
   width we ship. The wrapping is kept anyway: it costs nothing, and the reason
   the shell's `auto` is deliberate has not changed — it makes an overflowing
   screen VISIBLE instead of silently clipping it. A header that cannot overflow
   is better than one that reports it, and this is how it cannot.
*/
.rd-tdl-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px 12px;
}

.rd-tdl-head .rd-heading {
  flex: 0 1 auto;
  min-width: 0;
}

.rd-tdl-links {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 7px 13px;
}

/* A quiet destination word, not a blue underline. The label type role
   (`700 / 78 / .1em`) at the size the corpus gives a row's secondary display
   text (`ScreenDrillList`, 8.5px) — one step under the chips so the
   strip reads as chrome and the cards read as content. */
.rd-tdl-link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8.5px;
  color: var(--rd-fg3);
  text-decoration: none;
  white-space: nowrap;
}

.rd-tdl-link:hover { color: var(--rd-acc); }

.rd-tdl-link:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* `+ New Drill` — the one entry the drawn list keeps beside its heading, and
   the drawing gives it a louder treatment than the quiet word above: accent
   rather than fg3, 9.5px rather than 8.5, 800 rather than 700. It is the only
   thing on this screen a shooter can DO from the header, so it is the only
   thing in the header that carries the accent. */
.rd-tdl-new {
  font-weight: 800;
  font-size: 9.5px;
  color: var(--rd-acc);
}

/* The two stacked chip rows on Assessments & Quals — category, then scope. Closer
   together than the page column's own rhythm, because they are one control
   surface read top to bottom rather than two unrelated blocks. */
.rd-tdl-axes {
  display: grid;
  gap: 8px;
}

/* ---------------------------------------------------------------------------
   States: loading, the fetch error, the empty list.
*/
.rd-tdl-state {
  font-size: 13px;
  color: var(--rd-fg3);
  padding: 18px 0;
  text-align: center;
}

/* A banner, never a replacement for the list — queued drills stay visible
   behind it. Danger is the TEXT and hairline role; the ground is the danger
   wash, which is dark in dark and pale in light, so neither theme gets a white
   card. */
.rd-tdl-error {
  border: 1px solid var(--rd-danger);
  background: var(--rd-danger-wash);
  color: var(--rd-danger);
  padding: 11px 13px;
  font-size: 11.5px;
  line-height: 1.4;
}

/* ⚠️ NOT `RdEmptyState`, and the reason is copy rather than looks. That
   component takes a title AND a body, and every empty state on these two pages
   is a SINGLE sentence that already names the next move ("No favorite drills yet
   — tap the star on any drill to keep it here."). Splitting one sentence into
   two would be writing new copy, which this change is not allowed to do, and
   padding it with a second line that says nothing is what `RdEmptyState`'s own
   docs argue against. So: the same quiet centred treatment `train.css` gives its
   own states, and the action stays a real link. */
.rd-tdl-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 13px;
  padding: 26px 0 40px;
  text-align: center;
}

.rd-tdl-empty-lead {
  font-size: 13px;
  color: var(--rd-fg3);
  line-height: 1.5;
  max-width: 34ch;
}

/* ⚠️ THE SECOND LINE IS DRAWN, AND ONLY ONE PAGE HAS ONE. The note above
   argues that these states are a single sentence and that a second line saying
   nothing is padding — which is still true of every state `training.rs`
   renders. `08 (Mobile) Assessments` row 01 draws a sub-line under BOTH pinned
   empty strings (`rd-assess.jsx:111,116` — "Curated standards from T.REX ARMS
   land here." / "Quals you author, and ones the community shares."), so
   `assessment_quals.rs` mounts these two rules and nothing else does. The lead
   above is unchanged for every caller.

   `.rd-tdl-empty` is a 13px-gap column, which is the gap between the sentence
   and `.rd-tdl-cta`. The drawn gap between the two COPY lines is 4, so they sit
   inside their own box and the outer gap stays what it was. */
.rd-tdl-empty-copy {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}

/* 11px, NOT THE DRAWN 10 — the mixed-case type floor (`01 Foundations` §05),
   applied not transcribed, as `adm-loc.css:640` records for its own drawn
   10.5. */
.rd-tdl-empty-sub {
  font-size: 11px;
  color: var(--rd-fg3);
  line-height: 1.45;
  max-width: 34ch;
}

/* The empty state's one action. An accent word in the control voice — the
   `.rd-cruns__more-btn` treatment the arc already uses for a non-terminal
   action, rather than a full-width `RdButton`, which is the kit's shape for a
   screen's single terminal action and would outweigh the sentence above it. */
.rd-tdl-cta {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-acc);
  text-decoration: none;
}

.rd-tdl-cta:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* ---------------------------------------------------------------------------
   The list and its rows. Drawn on `ScreenDrillList`: `display: grid`,
   `gap: 6`, `alignContent: 'start'`.
*/
.rd-tdl-list {
  display: grid;
  gap: 6px;
  align-content: start;
}

/* The row IS the control, so it is a `<button>` and the reset undoes the UA
   chrome. The painted box is the `RdChamferBox` inside it; this element only
   carries the hit area and the focus ring. */
.rd-tdl-row {
  display: block;
  width: 100%;
  padding: 0;
  background: none;
  border: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.rd-tdl-row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* The card's inner face. Drawn on `ScreenDrillList` — `padding: '10px 12px'`, `gap: 10`,
   `alignItems: 'flex-start'`, `minHeight: 44`. Top-aligned, not centred: a
   103-character name wraps to two lines and the actions must stay beside the
   name rather than drift to the middle of the card.

   The stroke role is `--rd-line`, from the drawing, and it is NOT the
   `--rd-ctrl` a control boundary usually takes. `--rd-ctrl` exists for a
   TAPPABLE THING WITH NO FILL, where the stroke is the only thing saying the
   control is there; this card is filled with `--rd-surf`, and both drawn
   witnesses for this exact row (`ScreenDrillList`, `AQRow`)
   stroke it with `t.line`. Set at the call site, so it is visible in the rsx. */
.rd-tdl-card {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  min-height: 44px;
  padding: 10px 12px;
  box-sizing: border-box;
}

.rd-tdl-card__copy {
  flex: 1;
  min-width: 0;
}

/* The drill name. BODY face, not display: a drill name is user-authored text and
   the display role would uppercase "7 yard draw par" into something the shooter
   never typed. Two lines then clamp (as drawn), because the corpus's real names run
   from `1` to 103 characters and a row built for one end looks broken holding
   the other.

   ⚠️ THE CLAMP ITSELF IS `.rd-clamp-2`, APPLIED AT THE CALL SITE in
   `pages/training.rs`. It left this rule with the three other copies of the same
   four declarations, on the tablet kit's ruling that a clamp defined beside one
   screen works only for the screens that happen to load it —
   rd-tablet-kit.jsx:21 ("Line clamp. Lives in the KIT, not in a page file").
   What stays here is what belongs to a drill name: its face, size and colour. */
.rd-tdl-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  line-height: 1.3;
  color: var(--rd-fg);
}

/* The attribute strip: the Dry Fire mark (DRY rows only, ROW ANATOMY, ruled
   13 Aug — a LIVE FIRE mark on every row would spend the loudest slot on the
   default case) and a queued-edit state. Credit and Shared moved to the
   subline below, `.rd-tdl-meta`, as printed facts rather than chips. Wraps
   rather than scrolls — a chip pushed off the edge of a card has no
   affordance to say it is there. */
.rd-tdl-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 5px;
  margin-top: 5px;
}

/* The two chip roles the kit has no component for. `RdChipStatus` covers the
   neutral attribute chip; these are the same two-layer construction with the
   state ink, composed at the call site from `RdChamferBox` + the kit's own
   attribute face. Only the colour is ours — the geometry stays the kit's, so a
   change to the chip scale reaches these too.

   Both win on source order, not specificity: `.rd-chip-attr-face` sets
   `color: var(--rd-fg)` at equal weight in an earlier part. */
.rd-tdl-chip--warn   { color: var(--rd-warn); }
.rd-tdl-chip--danger { color: var(--rd-danger); }

/* The quiet subline — `by Lowrey · Pistol · 3.0 par`, a curated drill's
   `by T.REX ARMS · Pistol · 3.0 par`, or an own shared drill's
   `Pistol · 3.0 par · Shared`. Drawn on `ScreenDrillList`: body face, 10px,
   `--rd-fg3`, 3px under the name, clamped to one line. Everything the old
   skin scattered across four grey spans and two chips, in the one line the
   drawing gives it — credit (`by …`, the AQRow form; curated drills credit
   `T.REX ARMS`, not the admin account that published them) and Shared
   (subline text, again as on 08) included, ruled 13 Aug.

   ⚠️ THE ONE-LINE CLAMP IS `.rd-clamp-1`, APPLIED AT THE CALL SITE in
   `pages/training.rs` — the only `-webkit-line-clamp: 1` in the app, and the
   reason the kit utility ships two depths rather than one. */
.rd-tdl-meta {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  line-height: 1.35;
  color: var(--rd-fg3);
  margin-top: 3px;
}

/* A queued create's rejection reason. Not clamped: it is the thing the shooter
   has to read to fix the row. */
.rd-tdl-reason {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  line-height: 1.4;
  color: var(--rd-danger);
  margin-top: 4px;
}

/* The right-hand RAIL, and it is a column now (`rd-train-stress.jsx:112-115`,
   audit r7 doc 03 D13): the marks along the top, one quiet word along the
   bottom. `align-self: stretch` + `space-between` is the drawn arrangement —
   the word sits at the foot of the row's height, not 4px under the star — and
   `min-height` is the drawn 38px so a one-line row still separates them.
   `padding-top` lines the marks up with the first line of the name rather than
   with the top of the card's padding box. */
.rd-tdl-acts {
  flex: none;
  align-self: stretch;
  min-height: 38px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: space-between;
  gap: 4px;
  padding-top: 1px;
}

/* The marks themselves, in the row they were always in. */
.rd-tdl-acts__row {
  display: flex;
  align-items: center;
  gap: 6px;
}

/* The rail's word — display type, quiet, never a chip. `--rd-fg2` for the dry
   mark and `--rd-fg3` for a category, as drawn: the mark is the louder of the
   two because it is the one that changes where the tap goes. */
.rd-tdl-rail {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .1em;
  font-size: 8.5px;
  line-height: 1.2;
  color: var(--rd-fg3);
  white-space: nowrap;
}

.rd-tdl-rail--dry { color: var(--rd-fg2); }

/* The drill's own description, one clamped line between the name and the meta
   (`rd-train-stress.jsx:108`). Absent entirely when the drill has none — the
   same no-reserved-slot rule the meta line follows. The clamp is
   `.rd-clamp-1`, applied at the call site in `pages/training.rs`. */
.rd-tdl-desc {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  line-height: 1.35;
  color: var(--rd-fg3);
  margin-top: 3px;
}

/* Edit — an accent WORD in the control voice, not a blue underline. Visible only
   on the shooter's own drills, exactly as before; the server enforces ownership
   regardless. */
.rd-tdl-edit {
  background: none;
  border: 0;
  padding: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9px;
  color: var(--rd-acc);
  cursor: pointer;
}

.rd-tdl-edit:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* The favourite toggle. Same glyphs, same optimistic behaviour; what changes is
   that an un-starred row is quiet (`--rd-fg3`) and a starred one is the accent,
   instead of both being browser-default ink. */
.rd-tdl-star {
  background: none;
  border: 0;
  padding: 2px 3px;
  line-height: 1;
  font-size: 15px;
  color: var(--rd-fg3);
  cursor: pointer;
}

.rd-tdl-star--on { color: var(--rd-acc); }

.rd-tdl-star:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* The 7x12 chevron the kit uses wherever a row opens something
   (`.rd-home__chevron`). Sized here because the mark sets no width or height of
   its own and takes `currentColor`. */
.rd-tdl-chev {
  flex: none;
  width: 7px;
  height: 12px;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   Pagination. An accent word, matching `.rd-cruns__more-btn` in `train.css` —
   the arc's existing treatment for "there is more, fetch it", which is not a
   terminal action and must not wear the terminal action's shape.
*/
.rd-tdl-more {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 42px;
}

/* The in-flight row: the sweep joins the label at 10px of gap and the label
   does not move (`kit-loading.css` §8, rd-loading.jsx:197). Declared here
   rather than by borrowing `.rd-btn__face--inflight`, because that modifier is
   the kit button's face and this control is a text link. */
.rd-tdl-more-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  background: none;
  border: 0;
  padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-acc);
  cursor: pointer;
}

.rd-tdl-more-btn:disabled {
  color: var(--rd-fg3);
  cursor: default;
}

.rd-tdl-more-btn:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}
/* ==== S02-AUTH · 02 Auth — the five routed auth screens · owner: lane-reskin ==== */

/* ---------------------------------------------------------------------------
   The last pre-kit island in the shooter app: `/login`, `/register`,
   `/reset-password`, `/verify` and `/invite/:token`. Five pages, ~815 lines,
   zero kit components, raw fixed-light Tailwind (`bg-white`, `text-gray-600`,
   `bg-blue-600`) — the FIRST screens every user and every sales audience sees,
   and the only ones still painting a white card in a dark app.

   Drawn: **02 Auth**, Stage Two (`ScreenLogin`, `ScreenSignup`,
   `ScreenVerifyWeb`, `ScreenReset`) and Stage Seven — The Invitation
   (`ScreenInviteNew`, `ScreenInviteExisting`, `ScreenInviteCrossed`,
   `ScreenInviteStale`, `ScreenInviteFail`). Sources: `rd-auth-screens.jsx` and
   `rd-auth-invite.jsx`.

   ⚠️ WHY THIS IS A SIBLING OF `s02-boot.css` AND NOT AN EXTENSION OF IT.
   Same drawing, deliberately not the same part. `s02-boot.css`'s own banner
   scopes itself to *"two surfaces that render OUTSIDE the router"* — the
   session-restore beat and the update notice, neither of which is a `Route`.
   Everything here IS routed, and every one of these five pages is reachable by
   a URL a stranger was emailed. Folding the two would put a part's contents
   outside its own stated scope on the day it merged, which is how a scope note
   stops being read. The names are disjoint — `.rd-update*` there, `.rd-auth*`
   here — so the two do not contend and their relative order is not
   load-bearing.

   ⚠️ THE CENTRING IS THE SCREEN'S, NOT THE SHELL'S. AU1 §0.3, and
   `AU1-gate-screens` in `_legacy-kit-components-and-screens.css` already made
   the same call for `.rd-gate`: `RdShell` gives a gutter and a content region,
   and a shell that centred its children would make every ordinary page fight
   it. `.rd-auth` below is this family's copy of that decision — not a borrow of
   `.rd-gate`, because these screens are NOT centred horizontally (the drawn
   auth bodies run from the left edge of the gutter, exactly as `ScreenGate`
   does).

   ⚠️ THE FOOT IS NO LONGER THIS FAMILY'S ALONE. `ScreenGate` grew one when the
   17 Aug reply §2a delivered its copy, and it declares its own
   (`.rd-gate__foot` + `.rd-gate--footed`, gate block) rather than composing
   `.rd-auth__foot` below. Same values, deliberately unshared: the wordmark is
   shared because `/login` renders immediately after `ScreenVerifying` and two
   spellings of it flicker, and the blocked gate has no such neighbour — it is
   terminal, and this part's own scope note says everything in it is ROUTED,
   which the three pre-router boot screens are not.

   ⚠️ TWO BORROWED CLASSES, NEITHER DECLARED NOR OVERRIDDEN HERE:

   — `.rd-gate__mark` (AU1-gate-screens block, above). `/login`'s head is the
     wordmark, and the drawing puts a 190px brand IMAGE there. It IS an image
     now — AU1 §6.12 closed 2026-08-25 with `uploads/RD_Assets` — and the class
     is still borrowed rather than redeclared, for the reason it always was:
     `/login` is the screen the shooter sees IMMEDIATELY after
     `ScreenVerifying`, and two declarations of one mark are two places for that
     transition to start flickering.

     ⚠️ SAME ARTWORK, ONE SIZE **DOWN** — and the size is the only thing this
     screen changes. The drawing gives every auth screen the same `RDLogo`
     (`rd-auth-screens.jsx:85`) over the same `RD.logo` (`:9`, the square
     powered-by-T.REX lockup) and varies only its width: `w={182}` on the boot
     pair (`:141`), and here a three-way ternary (`:299`).

     ⚠️ **THAT TERNARY'S 196 IS THE ARM `/login` NEVER TAKES, and reading it as
     the value was this file's mistake for two batches.** The two lines are:

         const dense = banners.length > 0 || migrated || error || pwError
                       || instance !== 'quiet';                      // :298
         const logoW = banners.length > 1 ? 142 : dense ? 158 : 196; // :299

     `dense` is not a banner condition. Its last disjunct is `instance !==
     'quiet'`, `ScreenLogin`'s `instance` DEFAULTS to `'row'` (`:283`), and
     every web frame in the document passes `instance: 'none'` (rows 08/25/28/
     31/39). Both are `!== 'quiet'`, so **`dense` is true on every drawn login
     frame but one**. Counted in the document: `instance: 'quiet'` appears at
     exactly one call site, `02 (Mobile) Auth.html:239`, captioned *"Was —
     buried in the footer"* — the RETIRED composition row 21 draws in order to
     argue against it. 196 is that frame's width and nothing else's.

     So the drawn width is the `dense` arm, 158, through `RDLogo`'s own
     `Math.round(w * 0.85)` → **134px**, against the boot pair's 155. The head
     steps DOWN from `ScreenVerifying`, not up: the boot pair is a mark alone on
     a ground, and this screen has a form under it.

     SO THIS FILE ADDS A MODIFIER, NOT A DECLARATION. `.rd-gate__mark--auth`
     below overrides one property of the borrowed rule and inherits the rest.
     That is not the thing the paragraph above refuses: two DECLARATIONS of one
     mark are two places for the display, the box model and the fallback to
     drift apart independently, which is how a transition starts flickering.
     One declaration plus one width is one place, and the 21px step is drawn
     rather than a fit — it is the same step the drawing makes.

     The ladder's REMAINING arm (`banners.length > 1 ? 142`, → 121px) stays
     untranscribed for the reason `.rd-auth__brand` records: two stacked banners
     is a phone-frame composition, and we have one banner slot — the app's
     global offline strip — so nothing here can reach `length > 1`.
   — `.rd-box__inner` (kit chamfer substrate). `.rd-auth__mark` below sizes the
     status mark and nothing else; the chamfer is `RdChamferBox`'s prop and the
     colour is a `Fill` role, so neither is expressible here by accident. This
     is `.rd-gate__fault-inner`'s arrangement, one drawing over.

   ⚠️ ONE PLACE THIS FILE PAINTS A COLOUR, AND IT IS A RECORDED KIT GAP —
   THE ONE RULE HERE THAT OVERRIDES A PRIMITIVE. `.rd-auth__mark--wait` sets
   `background: var(--rd-fg3)` on top of a `Fill::Hollow` box's own ground.
   `ScreenVerifyWeb`'s `working` frame draws the same 46×46 mark as its three
   siblings in the quiet grey, and `Fill` has `Volt`, `Danger` and `Warn` but no
   neutral member — so the fourth drawn mark has no role to ask for, and
   `components/kit/` is another lane's to change. The want is recorded for its
   owner (a `Fill::Muted`); until it exists this is the page-local fallback.
   It is a TOKEN ROLE rather than a hex, so it themes and `check design-leaks`
   can see it, and it is one class against one class — it wins only because this
   part sits after the kit parts in `order.txt`, which that file records.
   ⚠️ DELETE THIS RULE, AND ITS `Fill::Hollow` CALL SITE, THE DAY THE ENUM GROWS.

   ⚠️ THE GUTTER IS THE KIT'S 20, NOT THE DRAWING'S 26. `RDShell`'s `pad`
   defaults to 26 on every frame in both source files. The kit's gutter is a
   four-step scale — 0 / 16 / 18 / 20 — modelled as four classes *"so a fifth
   value is a deliberate act rather than a typo"* (`kit-shell.css`), and these
   screens take `Gutter::Roomy` because that is what the boot screens beside
   them already take. Transcribed as a divergence rather than smuggled in as a
   fifth step.
   --------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
   1. The column. One per screen, inside the shell's content region.
   --------------------------------------------------------------------------- */

/* `flex: 1` + `justify-content: center` is `RDShell`'s own content box, and
   every drawn auth screen ALSO wraps its body in a second `flex: 1` +
   `justify-content: center` div — the drawing expresses the centring twice and
   AU1 §0.3 rules it belongs to the screen. `min-height: 0` because the parent
   is a flex column and a child that refuses to shrink below its content pushes
   the foot off the bottom on the shortest phone. */
.rd-auth {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* The foot: a sibling of the column, never inside it. `RDFoot` sits OUTSIDE the
   centred box in every drawn screen that has one, which is what pins it to the
   bottom of the viewport instead of letting it float under the last control. */
.rd-auth__foot {
  flex: none;
  text-align: center;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  line-height: 1.5;
  color: var(--rd-fg3);
  padding: 20px 0 26px;
}

/* ---------------------------------------------------------------------------
   2. Heads — the wordmark, the eyebrow, the status mark.
   --------------------------------------------------------------------------- */

/* `/login`'s brand mark: the boot pair's rule at the width THIS screen is
   drawn at. `rd-auth-screens.jsx:299`'s `dense` arm (`logoW = ... : 158 :`)
   against the boot pair's 182 (`:141`), both through `RDLogo`'s
   `Math.round(w * 0.85)` — 134 against 155. Everything else (display,
   max-width, height, centring) comes from `.rd-gate__mark` in the
   AU1-gate-screens block and is deliberately not restated; see the
   borrowed-classes note at the head of this file, which is also where the
   evidence that `/login` is always `dense` lives.

   Pinned by `the_head_is_the_drawn_lockup_at_its_dense_width` in login.rs,
   which fails if the markup drops the modifier — a head that quietly renders at
   155 on the screen the shooter looks at longest is not a difference anyone
   would catch. */
.rd-gate__mark--auth { width: 134px; }

/* 18px under the wordmark on `/login` — `rd-auth-screens.jsx:304`,
   `marginBottom: dense ? 18 : 34`.

   ⚠️ **THE DENSE ARM, FOR THE REASON THE MARK'S NOTE SETS OUT AT LENGTH.** What
   this rule carried was 34, the non-dense arm, justified here as *"the drawing's
   dense variants exist to absorb stacked banners … we have one banner slot, so
   the dense ladder has no trigger here."* That reading of `dense` was wrong on
   its own terms: `dense` is `banners.length > 0 || migrated || error || pwError
   || instance !== 'quiet'` (`:298`), and its LAST disjunct is true on every web
   frame the document draws — no banner required. One misread ternary set three
   numbers in this file; this is the second of them, and `.rd-auth__forgot` is
   the third. */
.rd-auth__brand { margin-bottom: 18px; }

/* The invite family's eyebrow — `You've been invited`, and on a dead invitation
   `Invitation expired` / `Invitation withdrawn`. Shell-title type role. */
.rd-auth__eyebrow {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  font-size: 9.5px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--rd-fg3);
  margin-bottom: 12px;
}

/* Amber, never red, on both stale states: *"recoverable, and nothing was
   lost"*. The Auth document's own ranking puts amber between the transient grey
   and the error red, so this is the system's ladder rather than a one-off. */
.rd-auth__eyebrow--warn { color: var(--rd-warn); }

/* The 46×46 status mark, replacing the head on the screens that report an
   outcome rather than ask for one — `ScreenVerifyWeb` in all four states and
   `ScreenReset`'s `sent`. Size only; see the borrow note above. */
.rd-auth__mark-row { margin-bottom: 24px; }
.rd-auth__mark { width: 46px; height: 46px; }
.rd-auth__mark--wait { background: var(--rd-fg3); }

/* 24px under a display heading that is followed by fields rather than by a
   body paragraph — `ScreenSignup` and `ScreenReset`'s request state. The type
   roles carry no spacing by design (`.rd-h1` has none, `.rd-p` is `margin: 0`),
   so every gap on these screens is the screen's to supply. */
.rd-auth__head { margin-bottom: 24px; }

/* ---------------------------------------------------------------------------
   3. Body copy.
   --------------------------------------------------------------------------- */

/* `RDBody`'s own `margin: '14px 0 28px'`. The 28 below it is the gap to the
   control the drawing puts underneath, so it belongs to this block and not to
   the button. */
.rd-auth__body { margin: 14px 0 28px; }

/* The quiet line under a control: `Invitations expire seven days after they are
   sent.` and `Not you? Sign out to accept with a different account.` Two sizes
   because the drawing uses two — 11.5 on the new-user form, 12.5 on the crossed
   account — and normalising them would be deciding a question nobody asked. */
.rd-auth__note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--rd-fg3);
  text-align: center;
  margin-top: 16px;
}

.rd-auth__note--sm { font-size: 11.5px; }

/* The instance name, quiet, under the sign-in CTA. `RDInstance`.

   ⚠️ THE DRAWN WEB FRAMES SHOW NO INSTANCE NAME AT ALL (`instance: 'none'`),
   and this renders one anyway. The drawing's removal is about the SERVER ROW —
   *"web genuinely cannot switch … drawing a Change affordance there would be a
   lie"* — and `RDInstance` is the one instance treatment that carries no
   Change control, which is exactly what web can show honestly. The name is real
   data off the licence handshake and the shipped page already put it in its
   `<h1>`; dropping it would remove information under cover of a skin. Flagged
   for the relay, drawn treatment, no invented one. */
.rd-auth__instance {
  text-align: center;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  font-size: 9.5px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--rd-fg3);
  margin-top: 14px;
}

/* ---------------------------------------------------------------------------
   4. Controls and links.
   --------------------------------------------------------------------------- */

/* ⚠️ `display: contents`, AND IT IS LOAD-BEARING RATHER THAN A NO-OP CLASS.
   `RdTextField` has no `on_keydown` prop, and Enter-to-submit is behaviour the
   shipped login and register pages already have — it cannot be dropped in a
   skin. The want is recorded for the kit's owner; the page-local fallback is to
   hang the handler on a wrapper and let the input's `keydown` bubble to it.
   `display: contents` is what keeps that wrapper out of the layout entirely, so
   the field's own `margin-bottom` still resolves against the form column and
   the wrapper cannot introduce a box nobody drew.

   It wraps exactly the fields that carry the handler TODAY — never a whole form
   — so the set of keys that submit is unchanged page for page. */
.rd-auth__key { display: contents; }

/* `Forgot password?` — right-aligned against the field it relates to, *"not
   beside the CTA"*. 16px to the button under it: `rd-auth-screens.jsx:312`,
   `marginBottom: dense ? 16 : 22`, the same `dense` the mark and the brand
   margin take — see the borrowed-classes note at the head of this file. */
.rd-auth__forgot {
  text-align: right;
  margin-bottom: 16px;
}

/* 14px between the primary and the outlined action under it (`Create Account`),
   and 20px above a bare text CTA (`Continue to sign in`). */
.rd-auth__second { margin-top: 14px; }
.rd-auth__cta { margin-top: 20px; }

/* `RDLink` — the accent as text.

   ⚠️ `--rd-acc`, NEVER `--rd-volt`. Substrate §1.1: volt fills, `acc` strokes
   and marks text; `--rd-volt` on a light-theme ground is an unreadable tan.
   The drawing writes `RD.volt` here because it is drawn dark-only. */
.rd-auth__link {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  font-size: 11.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--rd-acc);
  text-decoration: none;
}

.rd-auth__link:hover { color: var(--rd-fg); }

/* ---------------------------------------------------------------------------
   5. The invitation's read-only address.
   --------------------------------------------------------------------------- */

/* ⚠️ A LABELLED VALUE THAT IS NOT A FIELD, and the drawing argues the case
   itself: *"the server grants membership to that address, so offering to edit
   it would offer something the wire refuses."* No fill, no stroke, no box —
   text under a label. `RdTextField` has no read-only variant and passing it an
   unsupported kind would silently draw the editable box this exists to avoid,
   which is the mistake the drawing's own comment records making. */
.rd-auth__fixed { margin-bottom: 16px; }

.rd-auth__fixed-label {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  font-size: 8.5px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--rd-fg3);
  margin-bottom: 6px;
}

.rd-auth__fixed-value {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 15px;
  line-height: 1.4;
  color: var(--rd-fg2);
  overflow-wrap: anywhere;
}

/* 22px between the invitation's head and the first thing it asks for. */
.rd-auth__fields { margin-top: 22px; }

/* 8px above the invite form's submit: the field above it already carries its
   own 16px of status-line room, so the drawing spends less here than the
   28px a body paragraph would have left. */
.rd-auth__submit { margin-top: 8px; }
/* ==== s02-tcard · 02 Auth + 22 Account Deletion — the tablet card's ground · owner: lane-3 ==== */

/* FIVE SCREENS STAND ON THIS: /login, the three gate states, and /verify —
   every one of them an auth screen, which is what this ground is now for.

   ⚠️ BOTH SCREENS OF `22 Account Deletion` HAVE LEFT IT, each by its own ruling,
   and neither absence is an unfinished migration:

     the CONFIRM — James ruled it a modal on every viewport (2026-08-18), so it
       keeps `.rd-prof__over*` and never reached this ground at all. See
       apps/web/src/pages/edit_account.rs.
     the FAREWELL (/account-deleted) — ruled BARE at tablet (design relay 5
       item 1, 2026-08-19): the phone column centred on the ground with no card
       chrome, because "a hairline box dresses a goodbye up as a dialog with
       something behind it, and nothing is behind it." It brought this file's job
       with it rather than leaving it behind — `.rd-gone__ground` in s18-foot.css
       is this rule and `.rd-ccard`'s centring in one node, offline-banner twin
       included, because with no card there is no reason for two layers. See
       apps/web/src/pages/account_gone.rs.

   The document name stays in this part's title as the record of what it used to
   serve. */

/* THE GROUND THE KIT DELIBERATELY DID NOT DECLARE.
   `RdCenteredCard` (kit-centered-card.css) is two rules: `.rd-ccard`, which
   centres, and `.rd-ccard__card`, which sizes. Both are correct and neither is
   sufficient on its own here, for two reasons its own banner names:

     1. `.rd-ccard` is `min-height: 100%`, lifted from `.rd-gate` — "fill the
        SHELL's content region". A percentage min-height resolves against an
        ancestor with a definite height, and these five screens have none: the
        tablet ruling for this family is that the card BYPASSES the shell
        (02 (Tablet) Auth: "The phone card centres on the tablet ground
        unchanged"; rd-tablet-identity.jsx:281 ("w={420}") calls CenteredCard
        with no TabShell around it). With no shell there is no height, so
        `min-height: 100%` computes to auto and the card sits at the TOP of the
        viewport instead of centred on it.

     2. `.rd-ccard` paints no colour, and says so: "A background added here
        would paint over whatever theme surface the shell put down." True while
        a shell is behind it — and no shell is behind it here. `--rd-app-bg` is
        painted by `.rd-shell` (kit-shell.css §1) and by `.rd-tshell`
        (kit-tablet-shell.css §1), which are exactly the two things this family
        does not render. So the drawing's own `background: t.bg` on the ground
        (rd-tablet-identity.jsx:242 ("padding: 40")) becomes correct again the
        moment the card is the whole screen, and the page supplies it.

   Hence this part: the VIEWPORT layer for a screen whose only content is a
   centred card. It is `.rd-shell`'s first rule with the capped column removed,
   and every declaration below is copied from there for the reason stated there.
   Nothing here duplicates a `.rd-ccard` declaration — this element is the
   PARENT of that one, never a second copy of it.

   ⚠️ **AND THE ONE DECLARATION THAT IS NO LONGER COPIED IS THE GROUND.** Since
   2026-08-27 `.rd-shell` paints `--rd-app-ground` — the concrete photograph
   under a two-stop Ink scrim (`kit-shell.css` §0b) — and this rule deliberately
   does **not** follow it. It stays the flat `--rd-app-bg`. Recorded here rather
   than left to be re-derived, because "copied from `.rd-shell`" is this part's
   whole premise and a reader who checks that premise will find it broken in
   exactly one place.

   The reason is that these are AUTH screens, and the corpus splits one
   photograph into two treatments. `01 Foundations` §02 Logo & Texture: *"It
   should sit at full strength behind auth and splash only; inside the app it
   drops to ~30% over Ink so it reads as material, not wallpaper."*
   `--rd-app-ground` IS the in-app treatment — the ~30% half — so painting it
   here would put the in-app scrim behind auth, the one place the drawing says
   the photograph is not scrimmed. The auth half is already built and it is a
   different picture: the five-layer PSD rebuild in `s02-splash.css`.

   ⚠️ IT WOULD ALSO APPLY INCONSISTENTLY ACROSS ONE FAMILY, which is the part
   that settles it. `s02-splash.css` §5 is `.rd-shell:has(.rd-splash),
   .rd-tcard:has(.rd-splash) { background: none }` — it drops the whole layer
   stack on the screens that carry the splash. So a ground token here would
   reach the auth screens WITHOUT a splash and be dropped on the ones with it,
   splitting one family down the middle at one viewport: some tablet auth
   screens on scrimmed concrete, their siblings on the PSD rebuild. No drawing
   asks for that.

   Ten consumers and every one is on the auth side of that split: the nine of
   `tablet_card.rs`'s `TabletCardGround` family (`/login`, the three gate
   states, `/verify`, `/register`, both reset forms, `/invite/:token` —
   `02 (Tablet) Auth.html:16` rules them one treatment) plus `/account-deleted`
   at tablet, which reuses this ground directly (`account_gone.rs`) for a
   farewell drawn with no card at all. A goodbye is not an in-app page either.

   If the texture is ever wanted behind auth it arrives at FULL strength as its
   own layer — a different value from this token, and a designer question rather
   than a `var()` swap on this line. Pinned as an absence by
   `brand.rs::the_auth_ground_is_the_splash_rebuild_not_the_concrete`. */

.rd-tcard {
  /* 100dvh, NEVER 100vh — kit-shell.css §1's argument, unchanged: on mobile
     browsers 100vh is the URL-bar-collapsed height, so a 100vh ground is taller
     than the glass. `height`, not `min-height`: `.rd-ccard`'s own
     `min-height: 100%` needs a DEFINITE height above it, which is the whole
     reason this rule exists. */
  height: 100dvh;
  width: 100%;
  box-sizing: border-box;

  /* The ground is painted on the viewport layer, not on the card — the same
     split kit-shell.css §1 states ("Painting it on the column letterboxes the
     page in flat colour"). */
  background: var(--rd-app-bg);

  /* Body type role, as `.rd-shell` sets it: family only, so descendants inherit
     the CSS defaults. */
  font-family: 'Archivo', sans-serif;

  /* The card clips rather than scrolls, which is `.rd-ccard`'s own decision
     (`overflow: hidden`) and the drawing's. Stated here too so the DOCUMENT
     never scrolls behind a ground that is exactly one viewport tall. */
  overflow: hidden;

  /* ⚠️ A COLUMN SINCE 2026-09-01 (James, option 2), AND ONLY BECAUSE OF THE
     OFFLINE STRIP. The ground holds two children now — `OfflineBanner` first,
     then the card — where it used to hold one, and a column is what lets the
     second take the height the first leaves. In block flow the card's own
     `min-height: 100%` would start below the strip and still ask for a full
     viewport, so the bottom 63px of it would be clipped by the `overflow`
     above: the same below-the-fold defect the deleted `calc` rule was written
     for, moved inside the element. */
  display: flex;
  flex-direction: column;
}

/* ⚠️ THE CARD TAKES WHAT THE STRIP LEAVES — the other half of the ruling, and
   the ONE place this part redeclares a `.rd-ccard` declaration rather than
   borrowing the primitive by descent (`order.txt` carries the dated correction
   to its own "never redeclaring" note).

   It has to. `.rd-ccard` is `min-height: 100%` so that it fills a ground it
   cannot measure, and a percentage minimum resolves against this ground's full
   `100dvh` however tall the strip above it is — a floor no flex shrink can get
   under. `min-height: 0` releases it and `flex: 1 1 auto` puts it back on the
   only rule that knows about the strip: normal flow. Online the strip renders
   nothing, the card is the only child, and it is exactly `100dvh` again.

   Scoped to the direct child so it reaches this ground and no other consumer of
   the primitive; specificity (0,2,0) beats the kit part's (0,1,0), and
   `order.txt` places this part after `kit-centered-card.css` in any case. */
.rd-tcard > .rd-ccard {
  flex: 1 1 auto;
  min-height: 0;
}

/* ⚠️ `.offline-banner ~ .rd-tcard { height: calc(100dvh - …) }` STOOD HERE AND
   IS DELETED, 2026-09-01. The banner was a SIBLING above this ground, out of
   `ConnectivityShell`, and 100dvh under a 63px strip put the bottom of the card
   below the fold. It is a CHILD of the ground now, mounted by
   `TabletCardGround` and hand-rolled by `pages/account_gone.rs` for the bare
   farewell, so the ground keeps its clean `100dvh` and the pair of rules above
   is the whole mechanism. */

/* ⚠️ OURS, AND UNDRAWN. No tablet frame in `02 (Tablet) Auth` carries a banner
   — the drawn `ScreenTabAuth` is a card with a wordmark, two fields and a
   button. `/login` has two real ones (the offline-grace countdown and the
   sign-in failure) and they cannot be dropped on a tablet: the shell's banner
   slot is what satisfied #465's invariant — "a banner that governs a control
   renders ABOVE that control" — and bypassing the shell takes the slot with it.
   So the stack renders as the first thing INSIDE the card, above the wordmark,
   which keeps it above every control on the screen.

   The 11px gap is the drawn card's own internal rhythm
   (rd-tablet-identity.jsx:288 ("gap: 11")), and the 18px below it is the step
   the drawn card puts between blocks (:244 ("marginTop: 18")). Both are read
   from the drawing rather than invented, even though what they space is not. */
.rd-tcard__banner {
  display: grid;
  gap: 0;
  margin-bottom: 0;
}
/* ==== S02-SPLASH · 02 Auth — the splash ground, rebuilt from the PSD layers · owner: 3-track5 ==== */

/* ---------------------------------------------------------------------------
   THE GROUND EVERY AUTH SURFACE STANDS ON, AND IT IS NO LONGER ONE FLATTENED
   WEBP. `apps/playground/splash-reference/splash-reference.html` (the
   designer's rebuild, tuned 2026-08-25) takes `Splash.psd` apart and ships its
   five layers separately: a base colour, a ten-stop gradient, the greyscale
   shooter, and the two range diagrams. Nothing here is a flattened export —
   `splash-reference.html:147`: *"the background is a CSS gradient, the logo is
   the embedded SVG, and the photo and range diagrams are the PSD's own pixel
   layers (transparency included), downscaled. Total assets ≈ 450 KB and they
   serve every screen size; the mobile-only webp was 496 KB on its own."*

   Consumers: `ScreenVerifying`, `ScreenRestoring`, `ScreenGate` (all three
   through `gate_screens.rs::boot_chrome`), `/login`, `/register`,
   `/reset-password`. The markup is one component — `brand.rs::RdSplashGround` —
   so the stack is spelled once and every surface gets the same five layers in
   the same order.

   ⚠️ TWO TUNINGS DISAGREE AND THE NEWER ONE WINS ON GEOMETRY ONLY.
   `02 (Mobile) Auth` / `rd-auth-screens.jsx:129` grounds these screens on
   *"the official splash art (RD_Assets), dimmed just enough for the wordmark
   and loading line to read"* — i.e. `Splash_bg_mobile.webp` under a
   `[.5, .72]` dim (`rd-auth-screens.jsx:135`, `:151`). The reference is dated
   2026-08-25 and is the LATER of the two, and it supersedes the *art*: the
   webp it replaces was one flattened phone-shaped crop, and the whole point of
   the rebuild is that these layers *"serve every screen size"*.

   It does NOT supersede the DIM, because the reference never takes a position
   on one: its only foreground is a centred logo (`splash-reference.html:117`),
   and a logo is legible on the raw tan ground where a 12.5px body paragraph
   and a text field are not. So the two documents are read at the altitude each
   one speaks at — GEOMETRY from the reference, LEGIBILITY from 02 Auth — and
   §4 below carries 02 Auth's dims forward, over the rebuilt stack instead of
   over the webp.

   ⚠️ **DIMS, PLURAL — 02 AUTH DRAWS TWO AND THIS PART USED TO SHIP ONE.** The
   paragraph above is right that legibility comes from 02 Auth, and the version
   of it that stood here then took 02 Auth's SPLASH dim onto 02 Auth's FORM
   screens, which is the one number that document separates. `RDShell` declares
   `dim = [.72, .88]` for every screen at large (`rd-auth-screens.jsx:50`), and
   exactly two screens override it to `[.5, .72]` — `ScreenSplash` (`:135`) and
   `ScreenVerifying` (`:151`), the two with nothing on them but a mark and one
   caption. Nothing else in either document ever asks for `.5`.

   So the argument this part makes about logos and body text is the argument for
   the SPLIT, not for the lighter pair everywhere: it is precisely because a
   logo is legible where a 12.5px `--rd-fg2` paragraph and a `#1B1B1B` field are
   not that the two families take different numbers. §4 now draws both.

   ⚠️ THE LOGO IS NOT IN THIS PART. The reference centres one at
   `min(67%, 68cqh)`; these screens already draw their own mark at the width
   `02 Auth` gives them (`.rd-gate__mark` 155px, `.rd-gate__mark--auth` 134px,
   from `rd-auth-screens.jsx:141` and `:299`'s `dense` arm, both through
   `RDLogo`'s `Math.round(w * 0.85)`), and those two widths are pinned by tests in
   `login.rs` and `brand.rs`. A ground that drew a second logo would put two
   marks on every one of these screens. This part paints the ground; the screen
   keeps its head.

   ⚠️ WHY THE GROUND IS A LAYER STACK AND NOT A `background-image` LIST.
   One element can carry all five images and even blend them per layer
   (`background-blend-mode` takes a list) — but it CANNOT give them per-layer
   opacity, and three of the five carry one (.85 / .78 / .51 / .59). Painting
   them at full strength would not be a subtle miss; soft-light at 1.0 is a
   different picture. So each layer is its own element, exactly as the
   reference draws it.

   ⚠️ HOW IT GETS BEHIND THE SCREEN, and why it is not a `z-index: 1` on
   everything else. The stack is `position: fixed` (it must span the viewport,
   not the shell's 448px column) with `z-index: -1`, inside the element that
   already owns the ground — `.rd-shell` on the phone (`kit-shell.css` §1) or
   `.rd-tcard` on the tablet (`s02-tcard.css`). A negative-z child paints above
   its stacking context's own background and below every one of its contents,
   which is precisely the slot a ground wants: the shell's banner slot, the
   back row and the screen column all keep painting in their normal order and
   none of them needs a rule here. The alternative — a `z-index: 0` overlay
   plus a lift on every sibling — puts the count of things that must be lifted
   into this file, where it would go stale the day a fourth slot is added to
   the shell.

   ⚠️ SO THE GROUND ELEMENT MUST GO TRANSPARENT AND MUST ISOLATE, and that is
   what §5 does. `background: none` because the flat `--rd-app-bg` is opaque and
   would hide a negative-z child completely; `isolation: isolate` because
   without a stacking context on `.rd-shell` the negative-z layer resolves
   against the ROOT and disappears behind that same background anyway.

   ⚠️ `:has()`, WHICH THIS STYLESHEET HAS RETIRED TWICE — and the objection
   that retired it does not reach this rule. `adm0.css`'s scrollbar block was
   scoped `html:has(.rd-pro)` and the designer's ruling was that *"on a console
   page the root exists before .rd-pro mounts, so the thumb paints UA-default
   and then flips"* — a query about something that MOUNTS LATER. `.rd-splash`
   does not mount later: it is a static child of the same `rsx!` tree as the
   shell that contains it, so the shell's first paint and the splash's are the
   same paint and there is no frame in which one is true and the other is not.
   `kit-shell.css`'s second refusal cites this one and adds that it would be
   *"speculative plumbing for a composition no screen performs"* — six screens
   perform this one.

   What was considered instead and NOT taken:
     - A `ground` prop or a modifier class on `RdShell`. It is the kit's, every
       screen in the app consumes it, and this is one family's ground.
     - Painting the stack ON `.rd-shell` itself. Ruled out by the per-layer
       opacity above, and `kit-shell.css` §1 forbids a `url()` there by name.
     - The old `Splash_bg_mobile.webp` kept as the phone ground with the rebuild
       used only when wider. Two grounds for one screen family, one of them the
       thing the other was built to replace.
   --------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
   1. The stack.
   --------------------------------------------------------------------------- */

/* ⚠️ `container-type: size` IS THE MECHANISM, NOT A DECORATION. Every number in
   §3 is a `cqh`/`cqw` or an aspect-ratio query, and both resolve against the
   nearest size container — this element, which is the viewport. The reference
   puts the same declaration on its own preview frame
   (`splash-reference.html:27`), and its `@container` blocks are written against
   it; transcribing the blocks without the container would make every one of
   them match nothing and the whole wide layout would silently fall back to the
   phone geometry.

   `overflow: hidden` because the photo is 286% of the frame wide by
   construction; `isolation: isolate` so the three soft-light layers blend
   against each other and the base, and never against the page behind.

   THE BASE COLOUR IS ASSET DATA, NOT A THEME TOKEN. `Layer 1`'s colour overlay
   in the PSD (`splash-reference.html:153`, `#a89c75`) — it is a pixel value out
   of the artwork, in the same class as the ten gradient stops below it, and
   there is no token it could be spelled as. It is NOT `--rd-volt`/`--rd-acc`
   (`#A89968`), which is a near neighbour and a different colour with a
   different job; naming the token here would be an accent leaking into a
   photograph. */
.rd-splash {
  position: fixed;
  inset: 0;
  z-index: -1;
  container-type: size;
  overflow: hidden;
  isolation: isolate;
  background: #A89C75;
  pointer-events: none;
}

/* Layer 2 — `Layer 2 copy`, 85% opacity, ten stops sampled top→bottom
   (`splash-reference.html:33`, `:154`). Transcribed stop for stop rather than
   reduced to two ends: the PSD's ramp is not linear in either lightness or
   chroma, and a two-stop approximation of it is visibly greener in the middle
   third. */
.rd-splash__grad {
  position: absolute;
  inset: 0;
  opacity: .85;
  background: linear-gradient(180deg,
    #A39873 0%, #9D926F 11%, #968C6C 22%, #8E8568 33%, #867E63 44%,
    #7E765F 56%, #766F5B 67%, #6F6957 78%, #696454 89%, #646051 100%);
}

/* Layer 3 — `3M2A4735 copy`, the greyscale shooter with the PSD's own alpha
   fade, Soft Light at 78% (`splash-reference.html:34`, `:155`). */
.rd-splash__photo {
  position: absolute;
  inset: 0;
  mix-blend-mode: soft-light;
  opacity: .78;
}

/* Layers 4 and 5 — the two range diagrams, Soft Light, each at its own
   strength. `pointer-events` is already off on the stack; the widths and
   opacities are per-diagram and sit with them in §2. */
.rd-splash__dg {
  position: absolute;
  mix-blend-mode: soft-light;
}

.rd-splash__photo img,
.rd-splash__dg img {
  display: block;
  height: auto;
  /* ⚠️ `max-width: none` IS LOAD-BEARING (`splash-reference.html:36`): every
     reset in this app caps images at 100% of their box, and the photo is drawn
     at 286% of it. Without this the layer is silently scaled to a third of its
     size and the shooter sits in the wrong place with nothing to indicate the
     rule that did it. */
  max-width: none;
}

.rd-splash__dg img { width: 100%; }

/* ---------------------------------------------------------------------------
   2. The phone geometry — PSD-exact, and the reference does not tune it.
   --------------------------------------------------------------------------- */

/* ⚠️ NARROWER THAN 3:4 IS FIXED AND HAS NO OVERRIDES. The reference's hand-off
   opens with exactly that sentence — *"Narrower than 3:4 (phone 9:16): no
   overrides — PSD geometry as in the base CSS"* — and its tuning panel disables
   every slider on the phone preset (`splash-reference.html:124`: *"Phone
   (narrower than 3:4) is fixed to the PSD."*). So these five numbers are the
   PSD's placement, not a fit: the layer is 286% of canvas width at
   (-3180, -3863)px on a 4320px canvas (`splash-reference.html:35`).

   `top: 0` with a NEGATIVE `margin-top` rather than a negative `top`, because
   the wide blocks in §3 re-express the vertical offset in `cqh` and a rule that
   changed which property carried it would leave the base value stranded. */
.rd-splash__photo img {
  position: absolute;
  left: -73.6%;
  top: 0;
  margin-top: -89.4%;
  width: 286.1%;
}

.rd-splash__dg--200m { left: -20%; top: 45%; width: 129%; opacity: .51; }
.rd-splash__dg--cqb  { left: 1.5%; top: 65%; width: 100.5%; opacity: .59; }

/* ---------------------------------------------------------------------------
   3. Wider than 3:4 — the aspect blocks, tuned 2026-08-25.
   --------------------------------------------------------------------------- */

/* ⚠️ THIS IS THE ONE LAYOUT DECISION THE PSD DID NOT MAKE, which is why it is
   the only part of the splash that has tuned values at all.
   `splash-reference.html:162`: *"On anything wider than 3:4, the photo keeps
   the phone view's proportions but is scaled by height and centred on the
   shooter, so the subject stays behind the logo; the two diagrams sit in the
   lower band. That is the set of `@container` aspect-ratio blocks in the
   hand-off — the only layout decision the PSD didn't already make. The baked-in
   values are the set tuned on 2026-08-25."*

   The values below are that set, read out of the page's own `DEFAULTS`
   (`splash-reference.html:190`) and its per-breakpoint CSS
   (`splash-reference.html:59-61`), not off a screenshot:

     breakpoint          cover  px   py   ps   fade  d1x  d1y  d2x  d2y
     tablet portrait      0     50  -50  107   25   -26   78   48   61
     tablet landscape     0     32  -63  130   25   -18   73   52   48
     desktop              1     36  -71  134   25   -22   68   52   48

   and they reach CSS through the same three expressions the hand-off box emits
   (`splash-reference.html:194`): `height: <ps>cqh`, `left: <px>%`,
   `margin-top: <py>cqh`, and a bottom mask at `100 - <fade>`%.

   ⚠️ `translateX(-43.2%)` IS THE SHOOTER, and it is the same on all three.
   43.2% across the photo is the subject's focal point (`splash-reference.html:48`),
   so shifting the layer by it is what makes `left: <px>%` mean *"put the
   SHOOTER here"* rather than *"put the left edge of a 286%-wide image here"*.
   That is the whole of the wide-layout rule quoted above; a block that dropped
   it would place the numbers correctly against the wrong thing.

   ⚠️ THE BOTTOM FADE IS OURS TO PAINT AND THE PSD'S TO EXPLAIN
   (`splash-reference.html:49`): *"the PSD layer was only soft-erased where the
   phone crop needed it; fade the whole bottom uniformly."* At phone proportions
   the alpha baked into the file is enough; scaled by height on a wide frame it
   is not, and the un-erased part of the layer reaches the bottom edge as a hard
   cut. 25% on all three breakpoints — the tuning never moved it.

   ⚠️ AND THE DESKTOP BLOCK IS `cover`, WHICH THE OTHER TWO ARE NOT. Past 16:10
   a height-scaled photo no longer spans the frame, so the layer switches to
   being width-driven: `max()` of the height it would have had and the two
   widths that keep the focal point inside the frame from either side
   (`splash-reference.html:52`). The three terms are the reference's own
   arithmetic on the desktop row — 134 × 1.5 = 201cqh, 36 ÷ 0.432 = 83.3cqw,
   (100 − 36) ÷ 0.568 = 112.7cqw — and `height: auto` is what hands control to
   the width. */

/* Everything wider than 3:4 shares the diagram widths and the photo's frame of
   reference; only the four positions and the photo's size differ per band. */
@container (min-aspect-ratio: 3/4) {
  .rd-splash__photo img {
    width: auto;
    transform: translateX(-43.2%);
    -webkit-mask-image: linear-gradient(to bottom, #000 75%, transparent);
    mask-image: linear-gradient(to bottom, #000 75%, transparent);
  }
  .rd-splash__dg--200m { width: 64%; }
  .rd-splash__dg--cqb  { width: 56%; }
}

/* Tablet portrait — 3:4 to 1:1. */
@container (min-aspect-ratio: 3/4) and (max-aspect-ratio: 1/1) {
  .rd-splash__photo img { height: 107cqh; left: 50%; margin-top: -50cqh; }
  .rd-splash__dg--200m { left: -26%; top: 78%; }
  .rd-splash__dg--cqb  { left: 48%; top: 61%; }
}

/* Tablet landscape — 1:1 to 16:10. */
@container (min-aspect-ratio: 1/1) and (max-aspect-ratio: 16/10) {
  .rd-splash__photo img { height: 130cqh; left: 32%; margin-top: -63cqh; }
  .rd-splash__dg--200m { left: -18%; top: 73%; }
  .rd-splash__dg--cqb  { left: 52%; top: 48%; }
}

/* Desktop — 16:10 and wider. The one `cover` band. */
@container (min-aspect-ratio: 16/10) {
  .rd-splash__photo img {
    height: auto;
    width: max(201cqh, 83.3cqw, 112.7cqw);
    left: 36%;
    margin-top: -71cqh;
  }
  .rd-splash__dg--200m { left: -22%; top: 68%; }
  .rd-splash__dg--cqb  { left: 52%; top: 48%; }
}

/* ---------------------------------------------------------------------------
   4. The dims — `02 Auth`'s two, carried forward.
   --------------------------------------------------------------------------- */

/* THE BASE IS `RDShell`'s OWN DEFAULT, `[.72, .88]` (`rd-auth-screens.jsx:50`).
   It is what `ScreenLogin`, `ScreenSignup`, `ScreenReset` and `ScreenGate` all
   take, because none of them passes a `dim` — and those are exactly the four
   grounded surfaces here that carry body text, form fields or a fault block.

   It is the app's ink that is being dimmed TO, so the colour is `--rd-app-bg`
   through `color-mix` rather than a second spelling of `#0B0B0B`: a token that
   themes, and a value `check design-leaks` can still read. (The pre-boot copy
   cannot do this — it must survive a stylesheet that never loaded — which is
   why that one file is allowed its annotated duplicate.) */
.rd-splash__dim {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    color-mix(in srgb, var(--rd-app-bg) 72%, transparent),
    color-mix(in srgb, var(--rd-app-bg) 88%, transparent));
}

/* ⚠️ `[.5, .72]` IS THE LOGO-ONLY PAIR AND IT REACHES TWO SCREENS, NOT SIX.
   `ScreenSplash` (`rd-auth-screens.jsx:135`) and `ScreenVerifying` (`:151`) are
   the only two overrides of the default above anywhere in the document, and
   both are a mark plus one 10px caption over the art. `.rd-gate--brand` is
   precisely that pair in this app — `ScreenVerifying` and `ScreenRestoring`
   (`gate_screens.rs`, `CLASS_VERIFYING` / `CLASS_RESTORING`) — and it is the
   class the two of them already share for the same reason: AU1 §2.1 requires
   them to be indistinguishable from each other and from the pre-boot frame,
   which carries this pair inline in `index.html`.

   ⚠️ **`ScreenGate` IS NOT IN THIS RULE, DELIBERATELY.** It is grounded and it
   is a boot screen, but it is not logo-only — it carries a danger block, a body
   and a footer, and row 03 (`02 (Mobile) Auth.html:103`) says why it has no
   mark at all: *"Danger block replaces the wordmark: this is a fault, not a
   brand moment."* It takes the default above, which is also what the drawing
   gives it. `.rd-gate--footed` is how it differs in the markup.

   `:has()` for the same reason §5 argues it: `.rd-gate--brand` is a static
   child of the same `rsx!` tree as the shell, so there is no frame in which the
   shell has painted and the query has not. */
.rd-shell:has(.rd-gate--brand) .rd-splash__dim,
.rd-tcard:has(.rd-gate--brand) .rd-splash__dim {
  background: linear-gradient(
    color-mix(in srgb, var(--rd-app-bg) 50%, transparent),
    color-mix(in srgb, var(--rd-app-bg) 72%, transparent));
}

/* ---------------------------------------------------------------------------
   5. The two grounds that hand over to it.
   --------------------------------------------------------------------------- */

/* See the banner: transparent so a negative-z child is visible at all,
   isolated so it resolves here rather than against the root. Nothing else
   about either ground changes — the height, the centring and the gutter are
   still `kit-shell.css` §1's and `s02-tcard.css`'s.

   ⚠️ THIS IS ALSO WHAT KEEPS THE IN-APP CONCRETE TEXTURE OFF THE AUTH SCREENS,
   and that became load-bearing the day the texture shipped. `01 Foundations`
   §02 Logo & Texture splits the two uses of one photograph: *"It should sit at
   full strength behind auth and splash only; inside the app it drops to ~30%
   over Ink."* The in-app half is `--rd-app-ground` on `.rd-shell`
   (`kit-shell.css` §1); the auth half is the five-layer PSD rebuild in this
   file, which is a DIFFERENT picture and must not have the app's scrimmed
   concrete behind it. `background: none` was already the correct declaration —
   it drops the whole layer stack, not merely a colour — so nothing here had to
   change. What changed is the consequence of removing it: it used to mean a
   flat `#0B0B0B` leaking over the splash, and it now means the concrete
   vignette leaking over it, which is both more visible and more wrong. Pinned
   by `brand.rs::the_auth_ground_is_the_splash_rebuild_not_the_concrete`.
   `.rd-tcard` (`s02-tcard.css`) never took the ground token and still paints
   the flat `--rd-app-bg`, so the tablet auth card is unaffected either way. */
.rd-shell:has(.rd-splash),
.rd-tcard:has(.rd-splash) {
  background: none;
  isolation: isolate;
}

/* ⚠️ THE SPLASH COVERS THE OFFLINE STRIP, AND THAT IS THE DECISION — 2026-09-01
   (James, option 2, and the consequence ruled here).

   A `.offline-banner ~ .rd-shell .rd-splash` pair stood here and pushed the
   splash down by `--rd-offline-banner-h`, because the strip rendered ABOVE the
   shells and a `position: fixed` layer measured from the viewport would have
   painted over it. Both halves of that selector are gone: the strip is INSIDE
   the frame now, so there is no sibling to select on, and the token it read is
   deleted.

   Nothing replaces it, deliberately — and the ORDER is the opposite of what
   this comment first claimed: `.rd-splash` is a `z-index: -1` ground layer, so
   the opaque strip paints OVER the splash's top 63px, not under it. Accepted
   for the boot seconds: there is nothing to act on, the strip is the one true
   fact on screen, and it hands over the moment the app has a frame. The
   alternative, a splash inset by a strip it does not otherwise know about,
   reintroduces exactly the cross-part arithmetic the ruling deleted. Flagged
   to James with the PR rather than silently chosen. */
/* ==== TRAIN · drawings 03-08 · owner: spine — splits when a second owner takes any part of the arc ==== */
/*
   The shooter Training arc. One part file for the whole arc rather than one per
   page: seven pages would be seven order.txt reviews, and the arc has one owner.
   The banner names the split trigger so the condition is recorded where the next
   author reads it, not only in the clearance that granted it.

   Position: LAST in order.txt. The arc must win specificity ties against the
   legacy pool, its selectors are disjoint from every admin section, and
   appending re-orders no existing pair.
*/

/* ---------------------------------------------------------------------------
   06 Courses — the picker. Drawn `rd-courses.jsx:98-139` (`ScreenCourses`),
   row at `:80-95` (`CListRow`).

   The shell supplies the gutter, the scroll and the clip; everything here is
   inside the content region.
*/
.rd-courses {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  padding-top: 4px;
}

/* Title and the New Course control share a baseline, not a centre line: the
   drawn row is `alignItems: 'baseline'` (`:103`), which keeps the 9.5px label
   sitting on the same line as the 25px display type instead of floating in the
   middle of it. */
.rd-courses__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}

/* `+ New Course` (`:105`). Display type at 9.5px in the accent.
   ⚠️ IT IS A BUTTON NOW, on this block's own invitation — the rule used to read
   "NOT a button: there is no route behind it yet", and `/training/courses/new`
   (07 Course Builder) is that route. The resets are what a `<button>` needs to
   stop being a UA control; the type is unchanged, so the drawn face is
   unchanged. */
.rd-courses__new {
  flex: none;
  padding: 0;
  background: none;
  border: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-acc);
  cursor: pointer;
}

.rd-courses__new:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* The line under the title: what a course IS, in one clause (`:121`). */
.rd-courses__sub {
  font-size: 10.5px;
  color: var(--rd-fg3);
  margin-top: 6px;
}

/* The loading sentence that used to share this rule is gone — the list's cold
   state is the kit stack (`43 Loading States.html:40`, the honest-sentence
   retirement). A rewrite owns the CSS it strands, so the `__state` selector went
   with it rather than being left for the dead-class checker. */
.rd-courses__error {
  font-size: 13px;
  padding: 14px 0;
  color: var(--rd-danger);
}

.rd-courses__list {
  flex: 1;
  min-height: 0;
  overflow: hidden;
  margin-top: 12px;
}

/* One course. A `<button>` rather than a div with a click handler — the row IS
   the control, so it gets the element that is focusable and announced without
   any ARIA to prop it up. The reset undoes the UA button chrome; the layout is
   the drawn row (`:82`): 60px minimum height, 11px gap, a hairline under every
   row but the last. */
.rd-courses__row {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 60px;
  width: 100%;
  padding: 0;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.rd-courses__row--last { border-bottom: none; }

.rd-courses__row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-courses__row-text {
  flex: 1;
  min-width: 0;
}

/* The course name (`:84`). Truncates rather than wraps: a wrapped name pushes
   the row past its drawn height and the list stops being scannable. */
.rd-courses__name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .04em;
  font-size: 12.5px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The favourite toggle. UNDRAWN — no course-favourite control exists in the
   export, and this follows the drill list by the 17 Aug ruling: same star, same
   two states, and the control may move when the designer draws one.

   Its own pair rather than a reach for `.rd-tdl-star`: that rule lives in
   `s03-drills.css`, which owns the drill list, and a page borrowing another
   part's selector makes the two impossible to change apart. Same declarations
   today; the duplication is what lets them diverge when the drawing arrives.

   `flex: none` is the difference from the drill row's, and it is required —
   `.rd-courses__row-text` takes `flex: 1`, so an un-fixed sibling here is a
   shrink target and the star squashes on a long course name. */
.rd-courses__star {
  flex: none;
  background: none;
  border: 0;
  padding: 2px 3px;
  line-height: 1;
  font-size: 15px;
  color: var(--rd-fg3);
  cursor: pointer;
}

.rd-courses__star--on { color: var(--rd-acc); }

.rd-courses__star:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

.rd-courses__chev {
  flex: none;
  stroke: var(--rd-fg3);
}

/* The empty state's two actions, stacked (`:114`). */
.rd-courses__empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 12px;
  padding-bottom: 40px;
}

.rd-courses__empty-acts {
  display: grid;
  gap: 8px;
  margin-top: 4px;
}

/* The other door — past runs (`:127-133`). Pinned to the bottom of the column,
   separated by a rule, because it is a sibling route and not a list item: it
   must not read as a sixth course. */
.rd-courses__door {
  flex: none;
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  border: 0;
  border-top: 1px solid var(--rd-line);
  padding: 11px 0 12px;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.rd-courses__door:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-courses__door-text {
  flex: 1;
  min-width: 0;
}

.rd-courses__door-title {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 11px;
  color: var(--rd-fg);
}

/* ---------------------------------------------------------------------------
   §T-courses · THE TABLET RENDERING — the Courses picker

   Drawn: **`06 (Tablet) Courses`**, row 02 ("No Courses") for the empty
   state. The populated picker has no still frame — blessed by design-reply
   relay-4 §4 as a catalogue card grid, the pattern `03 (Tablet) Training`
   row "Where A Grid Is Right" draws for Assessments. Rust:
   `pages/courses.rs`, the `DeviceClass::Tablet` arm.

   ⚠️ A SECOND BLOCK IN THIS PART, kept beside the phone rules immediately
   above it rather than at the file's end — same screen, two devices, one
   file, one owner, `s11.css`'s own reasoning. `.rd-courses-tab__*` is
   disjoint from `.rd-courses__*` above.

   ⚠️ THE CARD CARRIES A NAME ONLY. See the page's own module doc: the
   blessing's assumed "stage count" and "banked progress" facts are not on
   `CourseDto` or `list_courses`, and the phone row this pattern is read from
   does not render them either. No layout here reserves room for a value
   column or a meta line the data cannot fill — that is `RdStat`'s and
   `RdChipStatus`'s trap, not repeated here.

   ⚠️ NO THUMBNAIL. `03 (Tablet) Training`'s own argument for a card over a
   row is "the plan itself as a thumbnail" — one stage's plan. A course has
   many stages and no one plan to draw, so the card is text, not `StageArt`.

   ⚠️ TYPE FLOORS. `01 Foundations` §05: 11px mixed-case, 9.5px tracked
   uppercase. The phone's 10.5px sub-line and 12.5px name are raised where
   they fall under the mixed-case floor; the tracked door title (11px on
   phone) already clears it.
   --------------------------------------------------------------------------- */

.rd-courses-tab__act {
  display: inline-flex;
  align-items: center;
  height: 100%;
  padding: 0 4px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 11px;
  color: var(--rd-acc);
  text-decoration: none;
}

.rd-courses-tab__body {
  box-sizing: border-box;
  flex: 1;
  min-height: 0;
  padding: 20px 26px;
  overflow-y: auto;
  overflow-x: hidden;
}

/* The `__state` half of this rule went with the loading sentence — see the
   phone rule above. */
.rd-courses-tab__error {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  padding: 14px 0;
  color: var(--rd-danger);
}

.rd-courses-tab__empty {
  max-width: 640px;
  margin: 40px auto 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
  text-align: center;
}

.rd-courses-tab__empty-acts {
  display: grid;
  gap: 10px;
  max-width: 320px;
  margin: 4px auto 0;
}

/* Raised from the drawn/phone 10.5 to the §05 mixed-case floor of 11. */
.rd-courses-tab__sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  margin-bottom: 14px;
}

.rd-courses-tab__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}

/* One card. A `<button>`, the row's own reasoning: the card IS the control. */
.rd-courses-tab__card {
  appearance: none;
  width: 100%;
  min-height: 64px;
  padding: 14px 16px;
  display: flex;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--rd-line);
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.rd-courses-tab__card:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* The card's text column — name over standing line. The `flex: 1; min-width: 0`
   moved here from `-name` when the second line landed: the growth belongs to
   the column, and a column child that also grew would stretch the name box. */
.rd-courses-tab__card-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.rd-courses-tab__card-name {
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .02em;
  font-size: 12.5px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The blessed standing line (relay-4 §4) — `{n} stages · {banked}s so far`.
   The section subline's own type (11px, §05's mixed-case floor, `--rd-fg3`):
   it is the same class of fact about the thing above it, and a second weight
   here would make the card argue with its own grid. */
.rd-courses-tab__card-line {
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The favourite star — the phone row's own pair, unchanged: `flex: none` so
   a long course name cannot squash it. */
.rd-courses-tab__star {
  flex: none;
  background: none;
  border: 0;
  padding: 2px 3px;
  line-height: 1;
  font-size: 15px;
  color: var(--rd-fg3);
  cursor: pointer;
}

.rd-courses-tab__star--on { color: var(--rd-acc); }

.rd-courses-tab__star:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

.rd-courses-tab__door {
  margin-top: 18px;
  width: 100%;
  min-height: 44px;
  padding: 12px 0;
  border: 0;
  border-top: 1px solid var(--rd-line);
  background: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 11px;
  color: var(--rd-fg);
  text-align: left;
  cursor: pointer;
}

.rd-courses-tab__door:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* ---------------------------------------------------------------------------
   06 Course Runs — the run index. Drawn `rd-courses.jsx:143-182`
   (`ScreenCourseRuns`).
*/
.rd-cruns,
.rd-run {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  padding-top: 4px;
}

.rd-cruns__state,
.rd-run__state {
  font-size: 13px;
  color: var(--rd-fg3);
  padding: 14px 0;
}

.rd-cruns__error,
.rd-run__error {
  font-size: 13px;
  color: var(--rd-danger);
  padding: 14px 0;
}

/* Two lines, not one sentence (`:150-153`) — kept apart in the markup for the
   same reason the drawing keeps them apart in the source. */
.rd-cruns__empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 10px;
  padding-bottom: 40px;
}

.rd-cruns__empty-lead { font-size: 13px; color: var(--rd-fg2); }
.rd-cruns__empty-sub  { font-size: 11px; color: var(--rd-fg3); }

.rd-cruns__list {
  flex: 1;
  min-height: 0;
  overflow: hidden;
  margin-top: 12px;
}

/* One run. A <button> because the row IS the control (`:161`): 58px minimum,
   11px gap, a hairline under each. */
.rd-cruns__row {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 58px;
  width: 100%;
  padding: 0;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.rd-cruns__row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-cruns__row-text { flex: 1; min-width: 0; }

.rd-cruns__name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .04em;
  font-size: 12.5px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rd-cruns__meta {
  font-size: 10px;
  color: var(--rd-fg3);
  margin-top: 3px;
}

.rd-cruns__row-num { text-align: right; flex: none; }

/* The run's total. `.rd-num` carries the role (Archivo 800/75/tabular); the
   size is a call-site override, which is how the kit applies size to the
   numeric role — and NO letter-spacing rides here, because the shared rule
   carries none (`_legacy-tokens.css:228`) and the -.01em belongs at the call
   site that wants it (`rd-train-kit.jsx:240`). */
.rd-cruns__total { font-size: 15px; }

.rd-cruns__unit {
  font-size: 9.5px;
  color: var(--rd-fg3);
  margin-top: 2px;
}

/* Pinned lowercase — "Load more", not "Load More" (`:172-175`). */
.rd-cruns__more {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 42px;
}

/* In flight, the sweep joins the label at 10px of gap (kit-loading.css §8). */
.rd-cruns__more-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  background: none;
  border: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-acc);
  cursor: pointer;
  padding: 0;
}

.rd-cruns__more-btn:disabled { color: var(--rd-fg3); cursor: default; }
.rd-cruns__more-btn:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* ---------------------------------------------------------------------------
   06 Run Detail — one run. Drawn `rd-courses.jsx:186-240` (`ScreenRunDetail`).
*/
.rd-run__chips { display: flex; gap: 6px; margin-bottom: 8px; }

/* Total and standing side by side, baselined on the bottom (`:206`) so the
   34px number and the 20px standing sit on one line rather than centring
   against each other. */
.rd-run__totals {
  display: flex;
  align-items: flex-end;
  gap: 18px;
  margin-top: 12px;
  padding-bottom: 13px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-run__total {
  font-size: 34px;
  line-height: 1;
  margin-top: 3px;
}

.rd-run__total-unit { font-size: 15px; color: var(--rd-fg3); margin-left: 2px; }

.rd-run__standing-cell { padding-bottom: 3px; }

.rd-run__standing {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 900;
  font-stretch: 62%;
  letter-spacing: .01em;
  font-size: 20px;
  color: var(--rd-acc);
  margin-top: 4px;
}

.rd-run__stages { flex: 1; min-height: 0; overflow: hidden; margin-top: 12px; }
.rd-run__stage-list { margin-top: 5px; }

/* One stage: index, name, hit factor, seconds (`:220-225`). */
.rd-run__stage {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-run__stage:last-child { border-bottom: none; }

.rd-run__stage-n     { font-size: 10.5px; color: var(--rd-fg3); width: 12px; flex: none; }
.rd-run__stage-name  { font-size: 12px; color: var(--rd-fg); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rd-run__stage-hf    { font-size: 12.5px; color: var(--rd-fg); flex: none; }
.rd-run__stage-secs  { font-size: 11px; color: var(--rd-fg3); width: 38px; flex: none; text-align: right; }

/* An ABSENT hit factor, dimmed. The drawing colours this cell through `numc`
   (`rd-courses.jsx:223`), which is the numeric role's placeholder rule: an
   em-dash drops to `t.fg3` so a value that never arrived cannot read as loud as
   one that did. `hit_factor` is an `Option` on this surface and the page really
   does print `—` (`course_runs_page.rs`, the `unwrap_or_else`), so the modifier
   is applied there rather than left to the reader. The seconds cell has no
   sibling modifier on purpose — it is `t.fg3` in the drawing whatever it holds. */
.rd-run__stage-hf--none { color: var(--rd-fg3); }

/* The no-stages case is an ERROR, not an empty state (`:193-195`). */
.rd-run__nostages {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 11px;
  padding-bottom: 40px;
}

/* The inner surface of an `RdChamferBox` — `chamfer: 10`, `Stroke::Danger`,
   `Fill::DangerWash`, exactly the drawn `TOut` (`rd-courses.jsx:197`). It
   therefore declares NO border and NO background: both are the box's two layers,
   and a border authored here would paint on the rectangular border-box and lose
   its diagonal to the cut. Padding and type only. */
.rd-run__nostages-box {
  padding: 11px 13px;
  font-size: 11.5px;
  color: var(--rd-danger);
  line-height: 1.4;
}

.rd-run__nostages-sub { font-size: 10.5px; color: var(--rd-fg3); line-height: 1.5; }

.rd-run__foot {
  flex: none;
  border-top: 1px solid var(--rd-line);
  padding-top: 10px;
  padding-bottom: 12px;
}

/* Resume is an RdButton (Outline), not a hand-rolled control.
   ⚠️ It was briefly filled, `color: var(--rd-on-acc, #0b0b0b)` — and
   `--rd-on-acc` IS NOT DEFINED. Only `--rd-on-volt` exists
   (`_legacy-tokens.css:73`, `:162`). The fallback would therefore have been the
   only thing that ever applied: a hardcoded hex wearing a token's clothes, and
   invisible to any check that greps for bare hex values.
   The corpus settles the treatment anyway — where Resume is drawn it is
   `<TBtn kind="o">` (`rd-courses.jsx:91`), outline, which needs no on-ground
   colour at all. */

/* ---------------------------------------------------------------------------
   03 Course Run — the live-run hub. Drawn `rd-train-course.jsx:48-112`
   (`ScreenCourseRun`), its row at `:32-46` (`CourseRow`).

   ⚠️ The stage-SCORING surface is NOT drawn here. `rd-train-run.jsx` /
   `rd-train-stage.jsx` sketch three competing options for it (tap-the-target,
   steppers, keypad) and none is ruled, so `.rd-crun__form` wraps the existing
   ScoreInputs flow rather than a drawn scoring UI — this section styles the
   hub's chrome around it: progress, the row list, the running total, the
   gated finish. `RdConfirmModal` supplies the finish-confirm sheet's own
   scrim and panel; `.rd-crun` only needs `position: relative` so that scrim
   (`position: absolute; inset: 0`) covers the content region and not the
   viewport (`confirm_modal.rs` §"the scrim's scope is drawn").
*/
.rd-crun {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  padding-top: 4px;
  position: relative;
}

.rd-crun__state,
.rd-crun__error {
  font-size: 13px;
  padding: 14px 0;
}
/* The no-claim fallback stands on empty ground, centred: real chrome, one
   26px mark in the middle (43 Loading States.html:52). The colour below is
   inert now that the sentence is gone and is kept because the error rule
   above shares the box. */
.rd-crun__state {
  display: flex;
  justify-content: center;
  padding: 40px 0;
  color: var(--rd-fg3);
}
.rd-crun__error { color: var(--rd-danger); }

/* Progress bar + `{done} of {n}` (`:55-60`). */
.rd-crun__progress {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: 9px 0 13px;
}

.rd-crun__progress-track {
  flex: 1;
  height: 3px;
  background: var(--rd-line);
  position: relative;
}

.rd-crun__progress-fill {
  position: absolute;
  inset: 0;
  background: var(--rd-acc);
}

.rd-crun__progress-count {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9px;
  color: var(--rd-fg2);
  flex: none;
}

.rd-crun__rows { display: grid; gap: 7px; }

/* One stage row (`:32-46`). Colour alone cannot carry "done" — the drawing's
   own reasoning (`:12-16`): "the sunlight photographs put volt last for
   legibility. So a done row flips fill AND gains a check AND states its
   score. Three signals, one of which survives noon." */
.rd-crun__row {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 50px;
  padding: 11px 13px;
  border: 1px solid var(--rd-line);
}

.rd-crun__row--done {
  border: 1.4px solid var(--rd-acc);
  background: var(--rd-acc-wash);
}

.rd-crun__row--next { border: 1.4px solid var(--rd-fg3); }

.rd-crun__row-mark {
  width: 17px;
  height: 17px;
  border-radius: 9px;
  flex: none;
  border: 1.4px solid var(--rd-fg3);
  display: flex;
  align-items: center;
  justify-content: center;
}

.rd-crun__row--done .rd-crun__row-mark {
  background: var(--rd-acc);
  border: none;
}

/* `--rd-on-volt`, same token `02-tbtn.md` mints for "ink on a volt fill" —
   this mark sits on the same ground. */
.rd-crun__row-check { stroke: var(--rd-on-volt); }

.rd-crun__row-text { flex: 1; min-width: 0; }

.rd-crun__row-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
  line-height: 1.2;
}

.rd-crun__row-next {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 8px;
  color: var(--rd-fg2);
  margin-top: 3px;
}

/* Not accent — the drawn row colours a done score with the numeric role's
   ordinary ink (`numc`), reserved for negative values this screen never
   shows. Undone reads `--rd-fg3`, matching the drawn `'Not shot'`. */
.rd-crun__row-score {
  font-family: ui-monospace, monospace;
  font-size: 12.5px;
  color: var(--rd-fg3);
  flex: none;
}

.rd-crun__row-score--done { color: var(--rd-fg); }

/* The active stage's scoring form — no drawn precedent (see the section
   banner), styled as a plain card so it reads as one unit against the rows
   above it rather than floating loose in the column. */
.rd-crun__form {
  border: 1px solid var(--rd-line);
  padding: 13px;
  margin-top: 11px;
}

.rd-crun__form-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 600;
  font-size: 13px;
  color: var(--rd-fg);
  margin-bottom: 8px;
}

/* In flight, the sweep joins the label at 10px of gap and the label does not
   move (kit-loading.css §8, rd-loading.jsx:197). */
.rd-crun__save {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  margin-top: 12px;
  min-height: 48px;
  background: var(--rd-volt);
  color: var(--rd-on-volt);
  border: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 12px;
  cursor: pointer;
}

.rd-crun__save:disabled { background: var(--rd-surf2); color: var(--rd-fg3); cursor: default; }
.rd-crun__save:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

.rd-crun__complete {
  margin-top: 11px;
  padding: 12px 13px;
  border: 1px solid var(--rd-acc);
  background: var(--rd-acc-wash);
  color: var(--rd-fg);
  font-size: 12.5px;
}

/* Running total, which the legacy hub never showed (`:64-65`). */
.rd-crun__total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 12px 2px 0;
  margin-top: 11px;
  border-top: 1px solid var(--rd-line);
}

.rd-crun__total-label {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .14em;
  font-size: 9.5px;
  color: var(--rd-fg2);
}

.rd-crun__total-value {
  font-family: ui-monospace, monospace;
  font-size: 22px;
  color: var(--rd-fg);
}

.rd-crun__total-unit { font-size: 14px; color: var(--rd-fg3); margin-left: 1px; }

.rd-crun__foot {
  margin-top: auto;
  padding-bottom: 12px;
  display: grid;
  gap: 9px;
}

.rd-crun__gate-note {
  font-size: 10.5px;
  color: var(--rd-fg3);
  text-align: center;
}

/* `Abandon this run` (`rd-train-course.jsx:88-92`). A centred TEXT action, not
   a button face: it is the foot's secondary and the primary above it is the one
   thing this screen is for. Danger ink because the act is one-way, at the
   9px/.12em display size the drawing gives it — the same demotion the 12 Aug
   correction made when it took the weight off the legacy `DELETE COURSE`. */
.rd-crun__abandon-row { text-align: center; }

.rd-crun__abandon {
  background: none;
  border: 0;
  padding: 4px 8px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: .12em;
  font-size: 9px;
  color: var(--rd-danger);
  cursor: pointer;
}

.rd-crun__abandon:focus-visible {
  outline: 2px solid var(--rd-danger);
  outline-offset: 2px;
}

/* ---------------------------------------------------------------------------
   §T · THE TABLET RENDERING — CourseRun

   Drawn: **`06 (Tablet) Courses`** — component `ScreenTabCourses`, row 01
   "The Course Does Not Leave The Screen". Rust: `pages/course_run_page.rs`,
   the `DeviceClass::Tablet` arm.

   ⚠️ A SECOND BLOCK IN THIS PART, NOT A SECOND PART FILE — `s11.css`'s own
   reasoning: one screen on two devices, same route, same page file, same
   owner. `.rd-crun-tab__*` is disjoint from `.rd-crun__*` above; neither
   declares nor overrides a selector the other names.

   ⚠️ THE LIST'S WIDTH IS NOT HERE. It is `ListWidth::Narrow` (340px) in
   `kit-tablet-panes.css` — this exact list is that scale's own citation for
   the variant (`rd-tablet-livefire.jsx:135`).

   ⚠️ TYPE FLOORS. `01 Foundations` §05: 11px mixed-case, 9.5px tracked
   uppercase. The drawn row sub-line (10.5) and gate note (10.5, see
   `.rd-crun__gate-note` above for the PHONE figure this does not touch) are
   raised to 11 here; the tracked labels sit at the drawn 9.5 already.
   --------------------------------------------------------------------------- */

/* ── The list pane: rows + the banked footer ─────────────────────────── */

.rd-crun-tab__list {
  box-sizing: border-box;
  min-height: 100%;
  display: flex;
  flex-direction: column;
}

.rd-crun-tab__rows { flex: none; }

/* THE ROW IS A BUTTON, reset to the list-row shape `rd-tev-row` already
   established: a 3px left rule always present and only ever recoloured (never
   declared solely on the selected modifier, which would shift every other row
   sideways as selection moves), a bottom hairline, 58px min-height — the
   drawn `minHeight: 58` and comfortably past the 44px touch minimum. */
.rd-crun-tab__row {
  appearance: none;
  width: 100%;
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 58px;
  padding: 11px 16px;
  border: none;
  border-bottom: 1px solid var(--rd-line);
  border-left: 3px solid transparent;
  background: none;
  box-sizing: border-box;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: inherit;
}

.rd-crun-tab__row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* Selection outranks the done ground — drawn
   `i === sel ? t.surf2 : s[2] ? t.surf : 'transparent'`. */
.rd-crun-tab__row--sel {
  border-left-color: var(--rd-acc);
  background: var(--rd-surf2);
}

.rd-crun-tab__row--done { background: var(--rd-surf); }

/* ⚠️ THE NUMERIC ROLE, NOT A SYSTEM MONO (audit r7, doc 06 D3). The three
   §T course-run figures below — this stage mark, the banked total and the
   already-scored readout — all draw with the kit's `mono`
   (`rd-tablet-livefire.jsx:138,148`), which is `rd-train-kit.jsx:72`:
   *"Archivo, sans-serif / 800 / stretch 75% / tabular-nums"*. That is the
   shipped `.rd-num` (`_legacy-tokens.css`). `ui-monospace` was adjudicated
   2026-08-04 as *"Never for numbers … numbers are the loudest thing on a
   scoring surface, so they speak in the display voice"* — it survives only for
   verbatim technical strings. The declarations are spelled out here rather than
   swapping the markup to `.rd-num`, because `.rd-num` also carries a 17px size
   and each of these three sites has its own drawn one. */
.rd-crun-tab__row-mark {
  width: 18px;
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--rd-fg3);
}

.rd-crun-tab__row-mark--done { color: var(--rd-acc); }

.rd-crun-tab__row-text { flex: 1; min-width: 0; }

.rd-crun-tab__row-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .01em;
  font-size: 12.5px;
  color: var(--rd-fg);
}

.rd-crun-tab__row-name--done { color: var(--rd-fg2); }

/* Raised from the drawn 10.5 to the §05 mixed-case floor of 11. */
.rd-crun-tab__row-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  margin-top: 3px;
}

/* The banked total. A property of the LIST — drawn as the last row inside the
   same scrollable region, not pinned or sticky, and `margin-top: auto` is
   what still puts it at the pane's foot when the list is short. */
.rd-crun-tab__foot {
  flex: none;
  margin-top: auto;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 14px 16px;
  border-top: 1px solid var(--rd-line);
}

.rd-crun-tab__foot-lab {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
}

/* The numeric role — see `.rd-crun-tab__row-mark` above. */
.rd-crun-tab__foot-val {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 14px;
  color: var(--rd-fg);
}

/* ── The detail pane: kicker, name, plan, meta, the active form ───────── */

.rd-crun-tab__detail {
  box-sizing: border-box;
  min-height: 100%;
  padding: 20px 28px;
  display: flex;
  flex-direction: column;
  gap: 15px;
}

.rd-crun-tab__kicker {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

.rd-crun-tab__name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 900;
  /* 62, not the drawn 68 — every 900-weight display heading in this corpus
     pairs with 62 (`_legacy-tokens.css`'s `.rd-h1`/`.rd-h1-alt`,
     `kit-tablet-shell.css`'s own title); `rangeday check css` enforces the
     allowed-stretch set and 68 is not in it. */
  font-stretch: 62%;
  letter-spacing: .01em;
  font-size: 22px;
  color: var(--rd-fg);
  margin-top: -7px;
}

/* The plan. `StageDiagram` fits and never crops on its own (its own part's
   `width: 100%; height: auto; max-height` on the scene) — this only gives it
   room to do that inside the flex column. */
.rd-crun-tab__plan {
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.rd-crun-tab__meta {
  flex: none;
  display: flex;
  gap: 22px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
}

.rd-crun-tab__form {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* `Score This Stage` is capped at the drawn 300 (`rd-tablet-livefire.jsx:160`,
   `<div style={{ width: 300 }}>`). The detail pane is fluid and the form is a
   stretch column, so without this the control grew with the pane — at 1194
   landscape roughly twice the drawn width. `max-width` rather than `width` so a
   narrow pane still gets a full-width control. The cap is on this wrapper and
   not on `.rd-crun-tab__form`, which also holds the undrawn `ScoreInputs`. */
.rd-crun-tab__form-act {
  max-width: 300px;
}

/* The already-scored readout — a stated deviation, not the drawn column
   (see the page's module doc, "a done stage, selected"). */
.rd-crun-tab__done {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  border: 1px solid var(--rd-line);
}

.rd-crun-tab__done-lab {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
}

/* The numeric role — see `.rd-crun-tab__row-mark` above. The readout itself is
   a stated deviation; the voice its number speaks in is not. */
.rd-crun-tab__done-val {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 15px;
  color: var(--rd-fg);
}

.rd-crun-tab__note {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

/* KEPT PAST THE DRAWING — Finish Course, persistent under whichever stage is
   selected. `margin-top: auto` pins it to the pane's foot the same way the
   list's banked total is pinned to its own. */
.rd-crun-tab__course-foot {
  flex: none;
  margin-top: auto;
  padding-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.rd-crun-tab__gate-note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  text-align: center;
}

.rd-crun-tab__state,
.rd-crun-tab__error {
  padding: 24px 28px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
}

/* Same fallback, at tablet: the mark centres on the empty ground the shell
   leaves (43 Loading States.html:52). */
.rd-crun-tab__state {
  display: flex;
  justify-content: center;
  padding: 48px 28px;
  color: var(--rd-fg3);
}
.rd-crun-tab__error { color: var(--rd-danger); }

/* ---------------------------------------------------------------------------
   §T-drills · THE TABLET RENDERING — the drill catalogue

   Drawn: **`03 (Tablet) Training`** row "The List Beside The Drill" —
   `ScreenTabDrills`, in its two frames (one selected; the 103-character
   name). Rust: `pages/training.rs`, the `(DeviceClass::Tablet,
   Surface::DrillIndex)` arm.

   ⚠️ A THIRD BLOCK IN THIS PART, filed with §T-courses and §T rather than in
   a part of its own: same arc, same owner, and `.rd-tdrills__*` is disjoint
   from every other prefix here. The phone drill list is NOT in this file —
   it is `s03-drills.css`, a different owner's part, and nothing here reaches
   into it.

   ⚠️ ONE COLUMN, NOT A GRID, and the drawing argues it from a measurement:
   "twenty real drill names came back from the corpus and one is 103
   characters ... A 103-character name in a 460px card clamps to three lines
   and truncates; in a 380px column beside a detail pane it reads in two."
   So the list pane takes `RdPanes`'s default 380 and the name clamps to two.

   ⚠️ THE CLAMP IS THE KIT'S, AND THIS PART MUST NOT FIGHT IT.
   `kit-clamp.css` sits above every page section and warns that "a page rule
   declaring `display` or `overflow` on the same element, at equal
   specificity, WINS and silently unclamps it". `.rd-tdrills__name` and
   `.rd-tdrills__sub` therefore declare NEITHER — they are type and colour
   only, and the two-line and one-line behaviour is `rd-clamp-2` /
   `rd-clamp-1` at the call site.

   ⚠️ TYPE FLOORS. `01 Foundations` §05: 11px mixed-case, 9.5px tracked
   uppercase. The drawn 10.5px sub-line is raised to 11, exactly as
   §T-courses raised its own; the 9.5px kicker is legal because it is
   condensed, tracked and uppercase, and the transform is declared beside the
   size for that reason rather than for decoration.

   ⚠️ font-stretch 62 ON THE DETAIL TITLE, NOT THE DRAWN 68. 68 is outside
   `ALLOWED_STRETCH` (62, 72, 75-78, 100) and `rangeday check css` refuses it
   by design. 62 is the display member of the designer's band statement ("62
   display, 75-78 labels and controls, 100 body"), and `s15-tablet.css` took
   the same decision for the same drawn 68 at a neighbouring size.
   --------------------------------------------------------------------------- */

/* The header action. The shell's slot owns the 44px box, the inset, the accent
   border and the type role and "writes no copy and sets no cursor"
   (`kit-tablet-shell.css` §5), so this declares only what an <a> brings with it
   that the slot cannot know about — `s15-tablet.css`'s own reading. */
.rd-tdrills__act {
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-decoration: none;
}

/* The filter strip is `RdTabChipRow` (`kit-tab-chip-row.css`), at the kit's
   base padding — this part declared it by hand until 2026-08-19, as three other
   screens did. It sits above the scroll rather than inside it, so changing slice
   never means scrolling back to the top of a list to find the control that
   changed it. */
/* The rows own the scroll, and the drawn `overflow: hidden` is not
   transcribed — the same stated deviation `kit-tablet-panes.css` and
   `kit-tablet-landing-cards.css` both record, arrived at for the same reason: a
   still frame holds nine drills and a shipped list holds two hundred.

   It scrolls HERE rather than at `.rd-tpanes__list` (which offers `overflow-y:
   auto` of its own) because this element is `flex: 1; min-height: 0` inside a
   pane of definite height — so this box never grows the pane, the pane's own
   scrollbar never appears, and the filter strip above stays put. Two scrollbars
   for one gesture is what the alternative produces. */
.rd-tdrills__rows {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
}

.rd-tdrills__state,
.rd-tdrills__error {
  padding: 14px 16px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
}

.rd-tdrills__state { color: var(--rd-fg3); }
.rd-tdrills__error { color: var(--rd-danger); }

/* The empty state, in the LIST column rather than over the whole screen: the
   slice is empty, and the slice is here. `07`'s own empty-state row rules the
   copy unchanged — "the tablet does not get a different empty state, it gets
   the same one with room" — so the sentence is the phone's. */
.rd-tdrills__empty {
  padding: 20px 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: flex-start;
}

.rd-tdrills__empty-lead {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--rd-fg2);
}

.rd-tdrills__cta {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 11px;
  color: var(--rd-acc);
  text-decoration: none;
}

/* One row. `minHeight: 62, padding: '13px 16px', gap: 10`, a hairline under
   every row and a 3px left rule that is transparent until the row is the
   selected one — "Selection carries the rail's volt left rule. On the phone the
   selected drill is the screen and nothing marks it; here something has to."

   `align-items: flex-start` is the drawing's: a two-line name must not push the
   star to the vertical centre of a row its neighbour does not have. */
.rd-tdrills__row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  min-height: 62px;
  padding: 13px 16px;
  box-sizing: border-box;
  text-align: left;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  border-left: 3px solid transparent;
  cursor: pointer;
}

.rd-tdrills__row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* The selected row: the volt edge and the raised ground, both drawn. */
.rd-tdrills__row--sel {
  border-left-color: var(--rd-acc);
  background: var(--rd-surf2);
}

.rd-tdrills__row-text {
  flex: 1;
  min-width: 0;
}

/* `tdisp(700, 78, '.01em')` at 12.5, `lineHeight: 1.25`. NO `display` and NO
   `overflow` — see the banner: the clamp is `rd-clamp-2`'s and this rule would
   win the tie and unclamp a 103-character name. */
.rd-tdrills__name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .01em;
  font-size: 12.5px;
  line-height: 1.25;
  color: var(--rd-fg);
}

/* The drill's DESCRIPTION, one line. Raised from the drawn 10.5 to the §05
   mixed-case floor of 11, as §T-courses raised its own sub-line. */
.rd-tdrills__sub {
  margin-top: 4px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.4;
  color: var(--rd-fg3);
}

/* "The star keeps its 34px rail and its 13px glyph. Bigger screen, same
   finger." The drawn rail is `width: 34, minHeight: 34` with `marginRight: -8`
   and `paddingRight: 8`, which pulls the glyph to the row's own inset while
   keeping the finger's box outside it. */
.rd-tdrills__star {
  flex: none;
  width: 34px;
  min-height: 34px;
  margin-right: -8px;
  padding: 0 8px 0 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  background: none;
  border: 0;
  line-height: 1;
  color: var(--rd-fg3);
  cursor: pointer;
}

.rd-tdrills__star--on { color: var(--rd-acc); }

.rd-tdrills__star:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* The page door, carried from the phone list. Undrawn here — the drawn fixture
   is nine rows and never pages — and kept because a catalogue that stops at 20
   with no way on is a catalogue with 180 drills missing. */
.rd-tdrills__more {
  padding: 12px 16px 20px;
}

/* In flight, the sweep joins the label at 10px of gap (kit-loading.css §8). */
.rd-tdrills__more-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  min-height: 44px;
  background: none;
  border: 1px solid var(--rd-line);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 11px;
  color: var(--rd-fg2);
  cursor: pointer;
}

.rd-tdrills__more-btn:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* ── The detail pane ─────────────────────────────────────────────────────── */

/* Drawn `padding: '20px 28px', gap: 14`. The 28px matches the header's own
   gutter, so the drill's name lines up under the page title. */
.rd-tdrills__detail {
  box-sizing: border-box;
  flex: 1;
  min-height: 0;
  padding: 20px 28px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.rd-tdrills__unpicked {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg3);
}

.rd-tdrills__head {
  flex: none;
}

/* `tdisp(700, 78, '.12em')` at 9.5 — legal ONLY as a condensed, tracked,
   uppercase label (`01 Foundations` §05). The transform is the reason the size
   is allowed, not decoration. */
.rd-tdrills__kicker {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* `tdisp(900, 68, '.01em')` at 22, `lineHeight: 1.1`, `marginTop: 8` — at
   width 62, and the banner says why. */
.rd-tdrills__title {
  margin: 8px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 900;
  font-stretch: 62%;
  letter-spacing: .01em;
  font-size: 22px;
  line-height: 1.1;
  color: var(--rd-fg);
}

/* `maxWidth: '64ch'` is the drawn measure and it is doing real work here: the
   pane is ~590px and a description set to its full width is a line nobody
   tracks back. */
.rd-tdrills__desc {
  margin: 10px 0 0;
  max-width: 64ch;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--rd-fg2);
}

/* THE REASON FOR THE DEVICE. `flex: 1; min-height: 0` is the whole of it: the
   plan takes the room the meta line and the button do not, and never more —
   "a plan wider than its pane is a cropped plan, which is the one thing row 05
   forbids". The scene inside is `StageDiagram`'s and fits contain on its own. */
.rd-tdrills__plan {
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

/* Drawn `display: flex, gap: 22` at 11.5. A row of facts, each one read off a
   wire field — see `detail_facts`, which states which drawn fact is absent and
   why. */
.rd-tdrills__meta {
  flex: none;
  display: flex;
  flex-wrap: wrap;
  gap: 22px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
}

/* Drawn `width: 300` — a button that spans a 590px pane reads as a banner. */
.rd-tdrills__run {
  flex: none;
  width: 300px;
  max-width: 100%;
}

/* One fact. `nowrap` so a wrapped row breaks BETWEEN facts rather than inside
   one — `4.0` on one line and `par` on the next is two facts a reader has to
   reassemble. */
.rd-tdrills__fact {
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   §T-hist · THE TABLET RENDERING — History, the run list beside the readback

   Drawn: **`03 (Tablet) Training`**, rows "The Run, Read Back Beside The
   Others" and "An Empty State That Can Afford A Sentence" — component
   `ScreenTabHistory`. Rust: `pages/history_tablet.rs`.

   A FOURTH BLOCK IN THIS PART, filed with §T-courses, §T and §T-drills for
   the same reason those three sit together: same arc, same owner, and
   `.rd-thist__*` is disjoint from every other prefix here. The phone flat
   list is NOT in this file — it is `s12.css`, another lane's part (see that
   file's own note on why `.rd-history__*` sits there instead of beside
   `score_history.rs`).

   ⚠️ THE DETAIL PANE OWNS NO TYPE OF ITS OWN. `.rd-thist__detail` is a
   padding box around `ScoreDetailBody`'s mounted markup, which carries its
   own type from wherever it already resolves (Tailwind utility classes on
   the phone route, unchanged) — this section styles the LIST pane and the
   pane's own gutter, nothing the shared readback renders.

   ⚠️ TYPE FLOORS. `01 Foundations` §05: 11px mixed-case, 9.5px tracked
   uppercase. The row's date/time sub-line and the truncation notice are both
   raised to the 11px floor, matching every sibling section in this file.
   --------------------------------------------------------------------------- */

.rd-thist__wait,
.rd-thist__error {
  padding: 20px 28px;
}

/* The drawn empty frame — row "An Empty State That Can Afford A Sentence" —
   replaces the panes rather than sitting inside one of them, the same
   full-pane treatment `kit-tablet-landing-cards.css`'s siblings take for
   their own "nothing here" frames. */
.rd-thist__empty {
  padding: 40px 28px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
  max-width: 52ch;
}

.rd-thist__empty-acts {
  display: flex;
}

.rd-thist__list {
  display: flex;
  flex-direction: column;
  padding: 8px 16px 16px;
}

.rd-thist__banner {
  margin: 0;
}

.rd-thist__truncated {
  margin-top: 12px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* Drawn `padding: '20px 28px'`. The 28px matches the header's own gutter, so
   the readback lines up under the page title, the same measure §T-drills'
   own detail pane takes. */
.rd-thist__detail {
  box-sizing: border-box;
  padding: 20px 28px;
}

.rd-thist__none {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg3);
}

/* One row. `min-height: 62px`, a hairline under every row and a 3px left rule
   that is transparent until the row is selected — the same selection
   treatment §T-drills' own rows carry, and the same reason: "On the phone the
   selected run is the screen and nothing marks it; here something has to." */
.rd-thist__row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  min-height: 62px;
  padding: 13px 4px;
  box-sizing: border-box;
  text-align: left;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  border-left: 3px solid transparent;
  text-decoration: none;
  cursor: pointer;
  /* The tablet list holds the same MAX_RUNS = 1000 runs as the phone's, so
     off-screen rows skip layout and paint. 62px is the row's own declared
     `min-height`, which under the global `box-sizing: border-box` already
     covers the 13px padding and the hairline; `auto` keeps the measured
     height for any row that overruns it. */
  content-visibility: auto;
  contain-intrinsic-size: auto 62px;
}

.rd-thist__row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-thist__row--on {
  border-left-color: var(--rd-acc);
  background: var(--rd-surf2);
}

.rd-thist__row-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* NO `display` and NO `overflow` — `kit-clamp.css` wins the tie at equal
   specificity and would silently unclamp the name; `rd-clamp-1` at the call
   site is the whole of the clamp. */
.rd-thist__row-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg);
}

.rd-thist__row-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

.rd-thist__row-end {
  flex: none;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* The leader treatment the phone row already gives this figure
   (`.rd-history__row-result`, `s12.css`), read at the tablet's own size: the
   result is the reason the row exists, so it is the loudest thing in it. */
.rd-thist__row-result {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  font-size: 15px;
  color: var(--rd-acc);
}

/* ---------------------------------------------------------------------------
   §T-score · THE TABLET RENDERING — Scoring, the plan beside the controls

   Drawn: **`03 (Tablet) Training`**, row "The Plan Is The Index, And Now You
   Can Read It" — component `ScreenTabScorePlan`. Rust: `pages/score_entry.rs`,
   the `DeviceClass::Tablet` arm.

   A FIFTH BLOCK IN THIS PART, filed with §T-courses, §T, §T-drills and §T-hist
   for the reason those four sit together: same arc, same owner, and
   `.rd-run-tab__*` is disjoint from every other prefix here. The phone column
   is NOT in this file — it is `s03-run.css`, another lane's part, and nothing
   here declares or overrides a selector that part names. What this section DOES
   do is re-parent four of that part's classes (`.rd-run__chips`,
   `.rd-run__doors`, `.rd-run__stats`, `.rd-run__result`, `.rd-run__act`) into
   new regions — they are all context-free rules with no `.rd-run` ancestor in
   their selectors, so a re-layout can mount them anywhere without touching
   them. That is the whole claim of this arm: same machinery, new regions.

   ⚠️ THE FIXED COLUMN IS ON THE RIGHT, so it is NOT `RdPanes`. `kit-tablet-
   panes.css` documents that exact case and declines to own it — "two right-hand
   side-columns, which the drawn `borderLeft` settles on its own" — so the
   geometry is here, mirroring `.rd-cb-tab__library` (`s07-builder.css`), which
   is the same 340px right-hand column at the same 1px rule.

   ⚠️ 340 IS THE DRAWN TRADE, NOT A LEFTOVER. "Steppers stay one column at 340px
   — phone width, deliberately. Five zone rows across 1100px moves the thumb
   further for every hit and turns a glance into a scan … The room that would
   have widened the controls went to the plan instead."

   ⚠️ THE PLAN'S BOX IS THE PANE, NOT THE DRAWN 580x430. The drawing derives its
   own number by arithmetic from a 1194 canvas (1194 − rail 208 − column 340 −
   padding = 590), and that arithmetic does not hold on an iPad mini. So the
   region is fluid and the scene fit-contains inside it, which is the property
   row 05 actually rules on: "fit-contain, never cropped."

   ⚠️ ONE OVERRIDE OF A SHARED RULE, AND IT IS THE DRAWN CEILING RATHER THAN A
   PREFERENCE. `sx-shared.css` caps `.rd-sdg__scene svg` at 24rem (384px). That
   cap is a guard against a desktop viewport drawing the scene enormous inside a
   scrolling phone column — written when this diagram had nowhere else to live —
   and it does not crop, it LETTERBOXES: with `width: 100%` fixed, a shorter
   max-height shrinks the whole drawing inside a pane that was already the right
   size. On the one screen whose entire purpose is a plan you can read, that is
   the defect rather than the guard. Raised to the drawn 430 and scoped to this
   mount by its parent class, so no other consumer of `.rd-sdg` moves.

   ⚠️ TYPE FLOORS. `01 Foundations` §05: 11px mixed-case, 9.5px tracked
   uppercase. This section USED to declare no type of its own at all, and the
   read-back below is why it now does: §T-score's regions were geometry over
   markup that came whole from `s03-run.css`, `ScoreInputs` and the kit, and the
   read-back is the first block here with type of its own. **Every size below
   clears the floor** — the drawn 10.5px sub-lines are raised to 11, exactly as
   `.rd-crun-tab__row-sub` records two sections up — and every numeral wears the
   numeric role rather than `ui-monospace` (see `.rd-run-tab__run-val`). The
   labelled figures are `RdStat`'s and declare nothing here.
   --------------------------------------------------------------------------- */

/* ── The three states that are one region wide ────────────────────────── */

/* Loading and a failed load. Each fills the content region on its own: before
   the drill lands there is nothing to put in a column, and a load that failed
   has no form to stand beside. */
.rd-run-tab__state {
  flex: 1;
  min-width: 0;
  min-height: 0;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 24px 28px;
  overflow-y: auto;
}

/* ⚠️ THE SAVED RUN IS NO LONGER ONE FULL-WIDTH REGION. It is the CONTENT of
   `.rd-run-tab__col` — the same fixed 340 the form's controls sat in — so the
   frame does not change shape underneath a shooter at the moment they save.
   The read-back keeps the left. Geometry only: the card inside is
   `s03-run.css`'s, unchanged. */
.rd-run-tab__done {
  flex: 1;
  min-width: 0;
  min-height: 0;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 20px;
  overflow-y: auto;
}

/* ── The plan region, left, fluid ─────────────────────────────────────── */

.rd-run-tab__plan {
  flex: 1;
  min-width: 0;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 20px 18px;
  /* The REGION scrolls, not the document — `kit-tablet-shell.css` §1 and §7
     leave every pane to own its own overflow, and a 40-element scene under a
     chip strip and a stat block is taller than 834px on a real drill. */
  overflow-y: auto;
}

.rd-run-tab__scene { flex: none; }

/* See the section header. Scoped by the parent so `stage_attachment.rs`,
   `admin/drill_form.rs` and this page's own phone column keep the 24rem cap. */
.rd-run-tab__scene .rd-sdg__scene svg { max-height: 430px; }

/* Everything this route carries as Drill Detail, under the plan rather than
   dropped — see the page's module doc. */
.rd-run-tab__facts { flex: none; }

/* ── The control column, right, fixed 340 ─────────────────────────────── */

/* ⚠️ THE DIVIDER IS `border-left`, on the fixed pane's OWN edge — the drawn
   `borderLeft: 1px solid t.line`, and the same rule `kit-tablet-panes.css`
   states for the side-columns it documents and does not own. A `border-right`
   on the plan region would put the rule on the fluid side, where it moves. */
.rd-run-tab__col {
  flex: none;
  width: 340px;
  box-sizing: border-box;
  border-left: 1px solid var(--rd-line);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.rd-run-tab__col-notice {
  flex: none;
  padding: 14px 20px 0;
}

/* The steppers scroll; the foot does not. A shooter reaching for Save Run must
   not have to scroll a zone list to find it, and the run's figure is the thing
   the drawn foot pins to the bottom of the column. */
.rd-run-tab__col-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 16px 20px 0;
}

/* `marginTop: 'auto'` in the drawing. A flex-none foot under a flex-1 scroller
   is the same result without depending on the scroller's content height. */
.rd-run-tab__foot {
  flex: none;
  box-sizing: border-box;
  padding: 0 20px;
  border-top: 1px solid var(--rd-line);
}

/* The foot's own rule already separates it, so the result strip's must not
   double it — `.rd-run__result` carries a `border-top` for the phone column,
   where nothing else divides it from the cluster above. */
.rd-run-tab__foot .rd-run__result {
  border-top: none;
  padding-top: 0;
}

/* ── The read-back, left, fluid — a drill with no plan, and the receipt ─── */

/* ⚠️ THIS REPLACES THE RETIRED `rd-run-tab__solo` PAIR (the region and its
   `-inner`), WHICH ARE GONE — named here without their leading dots on
   purpose, because `score_entry.rs`'s own test asserts this part declares no
   selector by that name and a guard that reads its own documentation is the
   failure this repo has recorded seven times.

   Those were the "honest minimum" for a plan-less drill: one 420px
   centred column — the phone measure — on an iPad. Half of *custom* drills
   carry a plan, which is what that treatment was written against; zero of the
   21 seeded T.REX drills do, so it was the treatment every T.REX drill got.

   Drawn: `11 (Tablet) Hit Factor`, `rd-tablet-solo.jsx:103` — the fixed control
   column keeps phone width and the freed region takes a read-back. Same region
   box as `.rd-run-tab__plan` above it, for the reason the two are one screen:
   the frame must not change shape between a planned drill and a plain one, and
   it must not change again when the run is saved.

   THE REGION SCROLLS, NOT THE DOCUMENT — `kit-tablet-shell.css` §1 and §7 leave
   every pane to own its overflow, and `min-height: 0` is what makes that bound
   real rather than a claim. */
.rd-run-tab__read {
  flex: 1;
  min-width: 0;
  min-height: 0;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 20px 18px;
  overflow-y: auto;
}

/* §1 — the drill's own words. `rd-tablet-training.jsx:117`: body size, capped at
   64ch, no label over it. The measure is the drawn one and not the pane's: a
   description running the full width of a 590px region is a line nobody
   finishes. */
.rd-run-tab__read-desc {
  flex: none;
  max-width: 64ch;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--rd-fg2);
}

/* `rd-tablet-training.jsx:291` — the drawn word for a drill never run. */
.rd-run-tab__read-none {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg3);
}

/* §2 and §3 side by side — the drawn figure row, `rd-tablet-training.jsx:232`
   ("display: flex, gap: 34"). The stats themselves are `RdStat`, so nothing
   here declares their type. */
.rd-run-tab__read-figs {
  flex: none;
  display: flex;
  gap: 34px;
  flex-wrap: wrap;
}

.rd-run-tab__read-fig {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* The run's stamp under its figure — the drawn kicker at
   `rd-tablet-training.jsx:225`, moved beneath the number it dates. 11px is the
   `01 Foundations` §05 mixed-case floor exactly, not below it. */
.rd-run-tab__read-when {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* §4 — `rd-train-detail.jsx:40`'s sentence, at the size the drawing gives it. */
.rd-run-tab__read-standing {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--rd-fg2);
}

/* §5 — the recent runs list. `flex: none`: it is the last block in a scrolling
   region and must keep its rows' full height rather than compressing them. */
.rd-run-tab__read-runs {
  flex: none;
}

/* One run row — the drawn 62px, `rd-tablet-training.jsx:214`. ⚠️ NOT A TAP
   TARGET and deliberately no 56px reasoning: this is a read-back beside a
   half-entered run, and a row that offered to navigate would take that run with
   it. Nothing here declares a cursor, a hover or a link. */
.rd-run-tab__run {
  min-height: 62px;
  box-sizing: border-box;
  padding: 13px 0;
  border-bottom: 1px solid var(--rd-line);
}

/* The drawn row name — `tdisp(700, 78, '.01em')` at 12px
   (`rd-tablet-training.jsx:215`), the same face `.rd-crun-tab__row-name` spells
   two sections up. One-line clamp, as drawn (`tclamp(1)`). */
.rd-run-tab__run-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .01em;
  font-size: 12px;
  line-height: 1.25;
  color: var(--rd-fg);
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 1;
}

/* Date left, figure right, on one baseline — the drawn `justifyContent:
   space-between, alignItems: baseline`, `rd-tablet-training.jsx:216`. */
.rd-run-tab__run-line {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
  margin-top: 5px;
}

/* Raised from the drawn 10.5 to the `01 Foundations` §05 mixed-case floor of
   11 — the same raise `.rd-crun-tab__row-sub` records two sections up. */
.rd-run-tab__run-when {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* ⚠️ THE NUMERIC ROLE, NOT A SYSTEM MONO — the ruling `.rd-crun-tab__row-mark`
   above carries in full (audit r7, doc 06 D3): the drawing's `mono` is
   `rd-train-kit.jsx:72`, Archivo 800 at 75% stretch with tabular numerals, and
   `ui-monospace` survives only for verbatim technical strings. Spelled out
   rather than swapping to `.rd-num`, which also carries a 17px size this row
   does not draw at. */
.rd-run-tab__run-val {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--rd-fg2);
}

/* ---------------------------------------------------------------------------
   §T-home · THE TABLET RENDERING — Home, one tile shorter than the phone's

   Drawn: **`03 (Tablet) Training`** row 01, "Home Keeps Its Job, Minus One
   Tile" — component `ScreenTabHome` (`rd-tablet-training.jsx:37`). Rust:
   `pages/home.rs`, the `DeviceClass::Tablet` arm.

   A SIXTH BLOCK IN THIS PART, filed with §T-courses, §T, §T-drills, §T-hist
   and §T-score for the reason those five sit together: same arc, same owner,
   and `.rd-home-tab__*` is disjoint from every other prefix here — including
   the phone's OWN `.rd-home__*`, which lives in the unowned legacy pool
   (`_legacy-kit-components-and-screens.css`) rather than here. Nothing in THIS
   block declares or overrides a `.rd-home__` selector.

   ⚠️ CORRECTED 2026-08-21: that last sentence used to be about the whole part.
   §S1-home-door at the foot of this file now declares two phone-Home names
   (`.rd-home--shelf`, `.rd-home__door`) for the door `44 The Doors.html` puts
   under the tiles, because the pool is delete-only and the modifier has to live
   in the part that owns the ruling. It declares no name that pool declares, so
   the disjointness this block relies on is unchanged — read §S1-home-door for
   the argument in full.

   ⚠️ TYPE FLOORS. `01 Foundations` §05: 11px mixed-case, 9.5px tracked
   uppercase. Every drawn value on this screen already clears both — the
   kicker on the (unbuilt) resume card sits at the tracked floor exactly and
   nothing else here is tracked uppercase — so nothing is raised.
   --------------------------------------------------------------------------- */

.rd-home-tab {
  flex: 1;
  min-width: 0;
  padding: 22px 28px;
  display: flex;
  flex-direction: column;
  gap: 18px;
  overflow-y: auto;
  overflow-x: hidden;
}

/* Two columns, not four — *"four tiles across 940px gives each one 230px and
   a subtitle that wraps to three lines … 460px keeps [the name as the target]
   true."* */
.rd-home-tab__tiles {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

.rd-home-tab__tile-link { text-decoration: none; color: inherit; display: block; }

.rd-home-tab__tile {
  min-height: 74px;
  padding: 0 18px;
  display: flex;
  align-items: center;
  gap: 14px;
  border: 1px solid var(--rd-line);
  border-radius: 0;
}

.rd-home-tab__tile-link:focus-visible .rd-home-tab__tile {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

.rd-home-tab__tile-copy { flex: 1; min-width: 0; }

.rd-home-tab__tile-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 76%;
  letter-spacing: .03em;
  font-size: 14.5px;
  color: var(--rd-fg);
}

.rd-home-tab__tile-state {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  margin-top: 4px;
}

.rd-home-tab__chevron {
  flex: none;
  width: 11px;
  height: 11px;
  color: var(--rd-fg3);
}

/* The drawn footnote — Live Fire's absence, explained, pinned to the bottom
   of the column exactly as `marginTop: 'auto'` draws it. */
.rd-home-tab__footnote {
  margin-top: auto;
  max-width: 68ch;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  line-height: 1.55;
}

/* ---------------------------------------------------------------------------
   §T-assess · THE TABLET RENDERING — Assessments, the two kinds side by side

   Drawn: **`08 (Tablet) Assessments`** — component `ScreenTabAssess`. Rust:
   `pages/assessment_quals.rs`, the `DeviceClass::Tablet` arm.

   A SEVENTH BLOCK IN THIS PART, filed with §T-courses, §T, §T-drills, §T-hist,
   §T-score and §T-home for the reason those six sit together: same arc, same
   owner, and `.rd-aq-tab__*` is disjoint from every other prefix here —
   including the phone's OWN `.rd-tdl-*`, which lives in `s03-drills.css`,
   another lane's part. Nothing in this block declares or overrides a
   `.rd-tdl-` selector.

   THE WHOLE SHAPE: the category axis stops being a chip and becomes the two
   `RdPanes` columns — qualifiers left (fixed 400, `DrillCard` rows, unowned by
   this part), assessments right (fluid, a two-up card grid, `.rd-aq-tab__card`
   below). See the page's own module doc for what the drawn declared state and
   scored figure do NOT get built, and why: neither is on this page's wire.

   ⚠️ THE THUMBNAIL CAP IS SCOPED, THE SAME MECHANISM §T-score USES FOR ITS OWN
   PLAN PANE. `sx-shared.css` caps `.rd-sdg__scene svg` at 24rem (384px) as a
   guard against a desktop viewport drawing the scene enormous in a scrolling
   phone column. A two-up card is smaller than that guard was ever sized for,
   so `.rd-aq-tab__thumb` lowers the cap rather than raising it — scoped to
   this mount by its parent class, so `stage_attachment.rs`, `drill_form.rs`
   and every other `StageDiagram` consumer keep their own cap untouched.

   ⚠️ TYPE FLOORS. `01 Foundations` §05: 11px mixed-case, 9.5px tracked
   uppercase. The kickers are 9.5px tracked uppercase, the same role
   `hit_factor.rs`'s `rd-hf-tab__log-head` already uses for the identical
   "Label — N thing" shape; the card name and meta clear 11 and hold at it,
   like §T-courses' own card.
   --------------------------------------------------------------------------- */

/* ── The left pane — qualifiers ───────────────────────────────────────── */

/* ⚠️ THIS PANE DECLARES NO WRAPPER AND NO FILTER STRIP, AND BOTH ABSENCES ARE
   CORRECTIONS MADE 2026-08-19.

   `.rd-aq-tab__quals` used to box this column with `padding: 20px 18px` and
   `.rd-aq-tab__filters` used to declare the chip strip with `gap: 8`,
   `flex-wrap: wrap`, a 14px bottom margin and NO divider.
   `rd-tablet-livefire.jsx:26-27` draws neither: the qual list is the Drills
   list pane verbatim — `flex:'none', display:'flex', gap:7,
   padding:'14px 16px', borderBottom:'1px solid t.line'`, byte-for-byte
   `rd-tablet-training.jsx:97`. The wrapper's padding pushed the chips 20px
   below the header where Drills' start at 14, and took the hairline with it.
   That is the "Assessments' content sits lower than Drills" seen at staging.

   The strip is now `RdTabChipRow` (`kit-tab-chip-row.css`) and the pane's three
   children carry their own padding, exactly as §T-drills' do. */

/* ── The right pane — assessments ─────────────────────────────────────── */

.rd-aq-tab__assess {
  flex: 1;
  min-width: 0;
  min-height: 0;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  /* The drawn gutter — `rd-tablet-livefire.jsx:58` renders this pane with
     `padding: '14px 22px 18px'`. It was `20px 24px`, which pushed the kicker
     6px below where the left pane's own kicker starts (13px, above) and made
     the two columns' first lines disagree. Pinned 2026-08-27, audit r7 doc 08,
     D6. */
  padding: 14px 22px 18px;
  overflow-y: auto;
}

/* The kicker over each pane — "Qualifications — declared" / "Assessments —
   scored", verbatim from `TABLET_QUAL_KICKER` / `TABLET_ASSESS_KICKER`. The
   text-transform does the capitalising; the constants stay mixed-case so a
   test can quote them exactly as written. */
.rd-aq-tab__kicker {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .12em;
  font-size: 9.5px;
  color: var(--rd-fg3);
  /* `rd-tablet-livefire.jsx:29` — `padding: '13px 16px 8px'`. Its own gutter
     rather than a wrapper's, now that the left pane has no wrapper: this kicker
     sits over a full-bleed row list and has to line up with the strip's 16px,
     which a margin under a padded box could not do. */
  padding: 13px 16px 8px;
}

/* The RIGHT pane keeps the wrapper it always had (`.rd-aq-tab__assess`, still
   padded), so its kicker takes that box's gutter and only needs the gap under
   itself. Scoped by the parent, so the rule above is untouched. */
.rd-aq-tab__assess > .rd-aq-tab__kicker {
  padding: 0;
  margin-bottom: 12px;
}

/* Loading / error / empty, shared type role across both panes — the same
   sizes §T's own `.rd-crun-tab__state` / `.rd-crun-tab__error` use. */
.rd-aq-tab__state,
.rd-aq-tab__error,
.rd-aq-tab__empty {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
}

/* In the LEFT pane these three are full-bleed children now, so they carry the
   gutter themselves — the same 14px 16px `.rd-tdrills__state` uses one screen
   over, for the identical position. The right pane's sit inside
   `.rd-aq-tab__assess` and keep its padding. */
.rd-tpanes__list > .rd-aq-tab__state,
.rd-tpanes__list > .rd-aq-tab__error,
.rd-tpanes__list > .rd-aq-tab__empty {
  padding: 14px 16px;
}

.rd-aq-tab__state,
.rd-aq-tab__empty { color: var(--rd-fg3); }
.rd-aq-tab__error { color: var(--rd-danger); }

/* The qualifiers list. `DrillCard` is the phone's own row, unowned by this
   part (`s03-drills.css`) — this is only the vertical stack around it, the
   same shape `.rd-tdl-list` already declares for the phone arm. */
/* ⚠️ IT OWNS THE SCROLL, and it does so HERE rather than at
   `.rd-tpanes__list` for the reason `.rd-tdrills__rows` states verbatim:
   `flex: 1; min-height: 0` inside a pane of definite height means this box
   never grows the pane, the pane's own scrollbar never appears, and the filter
   strip above stays put. Before 2026-08-19 the scroll was on the padded
   `.rd-aq-tab__quals` wrapper, which scrolled the chips away with the rows. */
.rd-aq-tab__rows {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  display: grid;
  gap: 6px;
  align-content: start;
}

/* The assessments grid — two up, the drawn width. */
.rd-aq-tab__grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  align-content: start;
}

/* One card. A `<button>`, the same reasoning §T-courses' own card states:
   the card IS the control. */
.rd-aq-tab__card {
  appearance: none;
  width: 100%;
  /* `rd-tablet-livefire.jsx:62` — the card's `inner` prop is `padding: 11`,
     `gap: 7`. Was 14/8. Audit r7 doc 08, D6. The card's 11px meta stays where
     it is: that one is a ruled type-floor deviation from the drawn 10 (see the
     block head above), not drift.
     ⚠️ NO BRACES IN THIS COMMENT — `assessment_quals.rs`'s geometry pin slices
     this rule's body at the first closing brace, so a quoted JSX object here
     would hide every declaration under it from the test. */
  padding: 11px;
  display: flex;
  flex-direction: column;
  gap: 7px;
  border: 1px solid var(--rd-line);
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.rd-aq-tab__card:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* Present only when the drill carries a stage layout — see the page's own
   per-row conditional. `StageDiagram` supplies its own label and box; this
   only lowers the shared scene cap to card scale. */
/* 78px — the drawn thumbnail box (`rd-tablet-livefire.jsx:63`,
   `<div style={{ … height: 78 }}>`). Was 108, which was this lane's own guess
   at "card scale" rather than a read value. Audit r7 doc 08, D6. */
.rd-aq-tab__thumb .rd-sdg__scene svg { max-height: 78px; }

.rd-aq-tab__card-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .02em;
  font-size: 12.5px;
  color: var(--rd-fg);
}

.rd-aq-tab__card-meta {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   §T-cruns · THE TABLET RENDERING — Course Runs, the index beside the run

   NO STILL FRAME. The shape was ratified as "the History two-pane, applied",
   so every measure below is read off §T-hist rather than invented: the 340px
   list column (`ListWidth::Narrow`), the 62px row with a hairline under it and
   a 3px left rule that is transparent until selected, and the 20px/28px pane
   gutter that lines the readback up under the page title. Rust:
   `pages/course_runs_tablet.rs`.

   AN EIGHTH BLOCK IN THIS PART, filed with §T-courses, §T, §T-drills, §T-hist,
   §T-score, §T-home and §T-assess for the reason those seven sit together:
   same arc, same owner, and `.rd-tcruns__*` is disjoint from every other prefix
   here — including the phone index's OWN `.rd-cruns__*`, four hundred lines up
   in this same part. Nothing in this block declares or overrides a
   `.rd-cruns__` selector; the screen RE-PARENTS two of them (`.rd-cruns__more`
   and `.rd-cruns__more-btn`, the Load more door) into the list pane, which is
   the mechanism §T-score already uses for four of `s03-run.css`'s classes —
   both rules are context-free, with no `.rd-cruns` ancestor in their selectors,
   so a re-layout mounts them anywhere without touching them.

   ⚠️ THE DETAIL PANE OWNS NO TYPE OF ITS OWN. `.rd-tcruns__detail` is a padding
   box around `RunDetailBody`'s markup, which carries its own type from
   `.rd-run__*` (this part, unchanged) — this section styles the LIST pane and
   the pane's own gutter, nothing the shared readback renders.

   ⚠️ TYPE FLOORS. `01 Foundations` §05: 11px mixed-case, 9.5px tracked
   uppercase. The row name is the uppercase display role the phone row already
   gives it, so it may sit at 12.5px; the date sub-line and the `s` unit are
   mixed case and hold at the 11px floor rather than at the phone's 10px and
   9.5px — a tablet is not a smaller screen than a phone.
   --------------------------------------------------------------------------- */

.rd-tcruns__wait,
.rd-tcruns__error {
  padding: 20px 28px;
}

/* The empty frame replaces the panes rather than sitting inside one of them —
   §T-hist's own full-pane treatment, for its own reason: a list with nothing
   in it and a detail pane with nothing to show are one state, not two empty
   boxes side by side. */
.rd-tcruns__empty {
  padding: 40px 28px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
  max-width: 52ch;
}

.rd-tcruns__list {
  display: flex;
  flex-direction: column;
  padding: 8px 16px 16px;
}

.rd-tcruns__banner {
  margin: 0;
}

.rd-tcruns__detail {
  box-sizing: border-box;
  padding: 20px 28px;
}

.rd-tcruns__none {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg3);
}

/* One row — §T-hist's own selection treatment, for the same reason it gives:
   "On the phone the selected run is the screen and nothing marks it; here
   something has to." */
.rd-tcruns__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  min-height: 62px;
  padding: 13px 4px;
  box-sizing: border-box;
  text-align: left;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  border-left: 3px solid transparent;
  text-decoration: none;
  cursor: pointer;
}

.rd-tcruns__row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-tcruns__row--on {
  border-left-color: var(--rd-acc);
  background: var(--rd-surf2);
}

.rd-tcruns__row-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* The phone row's own name role (`.rd-cruns__name`), re-declared at the
   tablet's own size rather than borrowed: that rule carries its own ellipsis
   machinery and this one clamps through `rd-clamp-1` at the call site.
   NO `display` and NO `overflow` here — `kit-clamp.css` wins the tie at equal
   specificity and would silently unclamp the name. */
.rd-tcruns__row-name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .04em;
  font-size: 12.5px;
  color: var(--rd-fg);
}

.rd-tcruns__row-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

.rd-tcruns__row-end {
  flex: none;
  display: flex;
  align-items: baseline;
  gap: 3px;
}

/* The run's total. `.rd-num` carries the numeric role (Archivo 800/75/tabular)
   and the size is the call-site override, the same split `.rd-cruns__total`
   takes — read at §T-hist's own result size, because the total is the reason
   the row exists. */
.rd-tcruns__row-total {
  font-size: 15px;
  color: var(--rd-acc);
}

.rd-tcruns__row-unit {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   §S1-home-door · THE DOOR TO PRO ON THE PHONE HOME

   Drawn: **`44 The Doors.html`** row 03, "Mobile Home" — component
   `ScreenHomeDoor` (`rd-doors.jsx:128` ("function ScreenHomeDoor")), the fixture
   at `:148-160` ("staff && ("). Rust: `pages/home.rs`, the
   `DeviceClass::Phone` arm. The fixture itself is
   `RdSwitchDoor` and every one of its values lives in `kit-switch-door.css`;
   what is authored here is the SHELF it sits on.

   ⚠️ THIS BLOCK DECLARES `.rd-home__` AND `.rd-home--` SELECTORS, WHICH §T-home
   ABOVE SAYS IT DOES NOT. That sentence was true until this block landed and is
   corrected in place there. The phone's `.rd-home__*` rules live in
   `_legacy-kit-components-and-screens.css`, which is UNOWNED · DELETE-ONLY — no
   lane may add to it — so the two rules the door needs go in the part that owns
   the ruling instead. That is the arrangement order.txt records for
   `.rd-auth__mark--wait` and `.rd-btn__face--inflight`, and it is safe here for
   the reason the banner gives: train.css is LAST, so a modifier declared here
   wins its tie against the pool rather than losing it.

   Neither rule OVERRIDES a selector that file declares — `.rd-home--shelf` and
   `.rd-home__door` are both new names, so no existing pair is re-ordered.
   --------------------------------------------------------------------------- */

/* `rd-doors.jsx:132` ("flex: 1, minHeight: 0") — the phone content column is
   `flex: 1, minHeight: 0`, and the door hangs off the bottom of it (`:147`, a
   `<div style={{ flex: 1 }} />` between the tiles and the fixture). `.rd-home` in the pool is already the
   flex column with `min-height: 0`; the one thing it is missing is the stretch,
   because until now nothing on this screen sat at the foot.

   A modifier rather than an edit to `.rd-home`: the pool is delete-only, and a
   bare `.rd-home { flex: 1 }` here would be this part overriding a selector
   that file declares rather than adding a name of its own. */
.rd-home--shelf {
  flex: 1;
}

/* The shelf. Two jobs, both geometry:

   `margin-top: auto` IS THE DRAWN SPACER (`rd-doors.jsx:150` ("<div style={{
   flex: 1 }}></div>")), with no element in it — the same call `.rd-door` and
   `.rd-tshell__rail` make. It has to be
   HERE rather than relied on from `.rd-door`: the fixture's own auto margin is
   inert once it is nested inside a wrapper, which `.rd-prom__foot` records for
   the Pro drawer's foot.

   The negative inline margin cancels the fixture's own foot padding
   (`kit-switch-door.css` §1, `padding: 12px 12px 14px`) so the drawn box lands
   flush with the tiles above it, which is how `rd-doors.jsx:149`
   ("paddingBottom: 14") draws it — its wrapper is that padding and nothing
   else, and the box spans the
   screen's gutter. The 14px below is the fixture's own and matches the drawn
   value exactly, so nothing is added under it.

   The hairline the fixture carries has no counterpart in the drawn phone frame;
   that divergence is recorded in `pages/home.rs` and routed, not styled away
   here — the fixture is one object at every foot. */
.rd-home__door {
  margin-top: auto;
  margin-inline: -12px;
}
/* ==== S07-BUILDER · 07 Course Builder — the shooter's course builder · owner: lane-builder ==== */

/* ---------------------------------------------------------------------------
   `/training/courses/new` (`pages/course_builder.rs`).
   Drawn: **07 Course Builder** — `ScreenCourseName` (Stage One),
   `ScreenCourseStages` with its add-stage sheet and empty state (Stage Two),
   and `ScreenCourseSaved` (Stage Three). Source: `rd-course-builder.jsx`.

   ⚠️ WHY THIS IS A PART AND NOT A BLOCK IN train.css. `train.css` covers
   drawings 03–08 and its banner names its own split trigger: *"splits when a
   second owner takes any part of the arc"*. Drawing 07 is a second owner
   taking part of the arc, so the condition the spine wrote is met and this is
   the split it asked for. Drawing 06's own rules — `.rd-courses*`, including
   the `+ New Course` control this builder gives a destination — stay where they
   are, with the spine.

   Position: LAST, after `train.css`. Every selector here is `.rd-cb*`, disjoint
   from every part either side, so this position re-orders no existing pair. It
   sits after the arc rather than before it for the same reason the arc sits
   after the legacy pool: nothing here should ever lose a specificity tie to a
   sheet it was written against.

   BORROWED AND NEITHER DECLARED NOR OVERRIDDEN: `.rd-shell*` (kit-shell.css),
   `.rd-chamfer*`, `.rd-btn*`, `.rd-stat*`, `.rd-chip*`, `.rd-label*`,
   `.rd-h*`, `.rd-text*`, `.rd-loading*` — the page composes the kit and paints
   none of it.

   What is page CSS is the five shapes the kit has no vocabulary for: the
   bare-labelled name field, the numbered stage row with its handle, the derived
   trio's inner face, the add-stage sheet (scrim + card + pick rows), and the
   landing's runs strip.

   ⚠️ THE OFFLINE STRIP WAS THE SAME KIT GAP `s21-bag.css` AND `s13-plan.css`
   BOTH RECORDED — a grey banner with no `BannerTone` to carry it, and this was
   the THIRD independent site. `BannerTone::Neutral` landed on 2026-08-17
   (`01 Foundations` §07, both themes) and all three blocks kept their promise;
   `rd-cb__offline` is gone and the page calls the tone. (Retired name written
   without its leading dot on purpose — `s21-bag.css`'s header carries the
   measured reason: a dotted retired name in a comment satisfies the page's own
   class sweep and reports a deleted rule as resolved.)

   Two values did not survive the move, both deliberately: `--rd-fg2` text became
   the tone's `--rd-fg` (*"the distinction is the text colour, not the border"*),
   and `text-wrap: pretty` has no kit equivalent. This site is also the one that
   mounts the tone INSIDE a pane rather than in the shell slot — see
   `course_builder.rs` at the strip, and `.rd-cb__foot`'s 8px grid gap, which is
   what spaces it now that the strip declares nothing.
   --------------------------------------------------------------------------- */

/* `position: relative` is load-bearing, not tidiness: the add-stage sheet is
   absolutely positioned and the drawing insets it to the SCREEN's content area
   (`inset: -16` off a padded column), not to the viewport. Without a positioned
   ancestor here the scrim would escape the shell and cover the tab bar. */
.rd-cb {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  padding-top: 4px;
}

/* The loading and error returns, which mount the same shell as the body. */
/* The no-claim fallback stands centred on empty ground
   (43 Loading States.html:52). */
.rd-cb__state {
  display: flex;
  justify-content: center;
  padding: 40px 0;
}

.rd-cb__error {
  padding: 11px 13px;
  color: var(--rd-danger);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  line-height: 1.45;
}

/* The accent eyebrow above the title — `New course`, and `Saved · yours` on the
   landing. The label role carries the type; this only spaces it. */
.rd-cb__eyebrow {
  margin-bottom: 9px;
}

.rd-cb__lede {
  margin: 13px 0 16px;
}

/* ── Stage One — the name ────────────────────────────────────────────── */

/* The inner face of the accent-stroked field box. */
.rd-cb__field {
  padding: 13px 13px 12px;
}

/* ⚠️ NOT `RdTextField` — the drawn field carries no label and the primitive
   has no no-label form. The question above it IS the label, so this is a bare
   input wearing the field's type. Recorded as a kit want; it goes the day the
   primitive takes a labelled-by-the-heading form. */
.rd-cb__name {
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: var(--rd-fg);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 15px;
  line-height: 1.2;
}

.rd-cb__name::placeholder {
  color: var(--rd-fg3);
}

.rd-cb__name:focus {
  outline: none;
}

/* ⚠️ SPECIFICITY DEFENCE, not decoration — do not flatten this to one class.

   The pre-kit backfill styles bare inputs by ATTRIBUTE:
     input[type="text"], … { border: 1px solid #d1d5db; outline: none; }
                                                        _legacy-base.css:115
     input:focus, … { box-shadow: 0 0 0 2px #3b82f6; }   _legacy-base.css:157
   Those are (0,1,1) and `.rd-cb__name` above is (0,1,0), so THEY WIN:
   undefended, a 1px #d1d5db rectangle draws INSIDE the chamfer and a 2px blue
   ring lands on focus — a raw hex in a screen whose colours are token roles,
   over a stroke this screen now moves on focus itself.

   The compound is (0,2,0) and outranks both. It is the pattern au5.css:76-97
   documents ("THIS WILL BITE EVERY KIT PRIMITIVE THAT BINDS A REAL <input>"),
   scoped to this screen, editing nothing in the backfill block, and correct
   still if that block is ever removed. */
.rd-cb__field .rd-cb__name {
  border: 0;
  box-shadow: none;
}

/* Designer, export-15 reply item 6: the name field's stroke is `t.ctrl` AT REST
   and accent on FOCUS ONLY — the same ruling the tablet arm carries, and the
   phone frame draws the field accent-stroked WITH a caret in it
   (`rd-course-builder.jsx:27-28`), i.e. focused. Rest half is `Stroke::Ctrl`
   at the call site; this is the focus half. `:focus-within` because the
   focusable thing is the input inside the box while the stroke is the box's
   outer layer. (0,2,0) beats `.rd-box--stroke-ctrl`. */
.rd-cb__field-wrap .rd-box:focus-within { background: var(--rd-acc); }

/* The signal line under the field, drawn unconditionally — the offline gate
   stated this rule before the builder existed. */
.rd-cb__signal {
  margin: 8px 2px 0;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  line-height: 1.45;
}

/* The bottom block on every step: pushed down, gapped, with the step marker or
   the derived numbers above the terminal action. */
.rd-cb__foot {
  margin-top: auto;
  padding-bottom: 12px;
  display: grid;
  gap: 8px;
}

/* A disabled button states its reason (kit §4.4), and the reason goes directly
   above it in the quiet role. */
.rd-cb__reason {
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.45;
  text-align: center;
  text-wrap: pretty;
}

.rd-cb__step {
  text-align: center;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
}

/* ── Stage Two — the stages ──────────────────────────────────────────── */

.rd-cb__sub {
  margin: 7px 0 13px;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
}

/* The empty card. Its sentence is the courses list's own, because it is the
   same thing being described. */
.rd-cb__none {
  padding: 20px 16px;
  text-align: center;
}

.rd-cb__none-t {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 10px;
  color: var(--rd-fg2);
}

.rd-cb__none-b {
  margin-top: 7px;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.45;
  text-wrap: pretty;
}

.rd-cb__stages {
  display: grid;
  gap: 7px;
}

/* One stage. The inner face of the row's chamfer box. */
.rd-cb__stage {
  padding: 10px 12px;
  display: flex;
  gap: 10px;
  align-items: center;
  min-height: 48px;
}

/* The position. Monospaced, because it is an index the eye scans down. */
.rd-cb__n {
  flex: none;
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 10px;
  color: var(--rd-fg3);
}

.rd-cb__stage-text {
  flex: 1;
  min-width: 0;
}

.rd-cb__stage-name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .05em;
  font-size: 11.5px;
  color: var(--rd-fg);
}

.rd-cb__stage-sub {
  margin-top: 2px;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
}

/* ⚠️ THE DRAWN HANDLE, AS A CONTROL. The drawing draws a drag handle and
   pointer-drag is not built (HTML5 drag-and-drop does not fire on touch, and
   this is a phone drawing), so the glyph is a button that moves its stage one
   place earlier. The glyph is the drawing's 12px; the hit area is 40 × 40,
   which is larger than the glyph and smaller than the 44px ideal — the row's
   drawn 48px height with its 10px padding is what bounds it, and stretching
   the row to reach 44 would change a drawn dimension to satisfy an undrawn
   control. The negative margins spend the row's padding rather than its
   height. */
.rd-cb__up {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  min-height: 40px;
  margin: -6px -12px -6px 0;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
}

.rd-cb__up:disabled {
  cursor: default;
  opacity: .4;
}

.rd-cb__up:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-cb__grip {
  stroke: var(--rd-ctrl);
  fill: none;
}

.rd-cb__order {
  margin: 7px 2px 0;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
}

.rd-cb__add {
  margin-top: 9px;
}

/* The derived trio's inner face. Three equal columns, because the three
   numbers are read across as one statement. */
.rd-cb__trio {
  padding: 11px 13px;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 10px;
}

/* The caption that says the numbers are derived — and qualifies them when a
   stage declares none. */
.rd-cb__derived {
  text-align: center;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.45;
  text-wrap: pretty;
}

/* ── The add-stage sheet ─────────────────────────────────────────────── */

/* Covers the screen's content area, not the shell chrome — the drawing insets
   it to the page gutter and lets the sheet sit on the bottom edge. */
.rd-cb__sheet {
  position: absolute;
  inset: 0;
  z-index: 2;
}

/* The way out. The drawing draws no close control, so the scrim IS the control
   and is a real button with a name rather than a div that happens to be
   tappable. */
.rd-cb__scrim {
  position: absolute;
  inset: 0;
  width: 100%;
  padding: 0;
  border: 0;
  background: rgba(0, 0, 0, .72);
  cursor: pointer;
}

.rd-cb__sheet-card {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 12px;
}

.rd-cb__sheet-in {
  padding: 15px 15px 14px;
}

.rd-cb__sheet-sub {
  margin: 7px 0 10px;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  line-height: 1.45;
  text-wrap: pretty;
}

/* The gap below the search box is the BOX's, so it rides a wrapper — a margin
   on `inner_class` would move the face inside a box that has not moved, which
   is the defect the tablet arm carried. Drawn on the outer:
   `rd-course-builder.jsx:94` — `<TOut … style={{ marginBottom: 8 }}>`. */
.rd-cb__search-wrap { margin-bottom: 8px; }

.rd-cb__search {
  padding: 8px 11px;
}

.rd-cb__search-in {
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: var(--rd-fg);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
}

.rd-cb__search-in::placeholder {
  color: var(--rd-fg3);
}

.rd-cb__search-in:focus {
  outline: none;
}

/* SPECIFICITY DEFENCE — see `.rd-cb__field .rd-cb__name` above for the full
   note. Same trap, same (0,2,0) compound. */
.rd-cb__search .rd-cb__search-in {
  border: 0;
  box-shadow: none;
}

.rd-cb__sheet-none {
  padding: 10px 2px 2px;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.45;
  text-wrap: pretty;
}

.rd-cb__picks {
  display: grid;
  gap: 6px;
  max-height: 244px;
  overflow-y: auto;
}

/* One pickable drill. A button, because it toggles — the mark at its left is one
   control in two states, not two affordances.

   ⚠️ THE BUTTON IS BARE; THE FACE IS AN `RdChamferBox` INSIDE IT. The drawn row
   is a stroked chamfer (`rd-course-builder.jsx:99`, `TOut … r={8}`), and the kit
   rule forbids re-cutting that corner by hand — so the stroke and the fill are
   the box's, and everything here is reset so the browser's own button chrome
   does not draw a second, square edge around it. Do NOT put a `border`, a
   `background` or a `clip-path` back on this element: a bordered box with a
   clip-path is the bare-diagonal defect `48 Kit Rules` names by that name. */
.rd-cb__pick {
  appearance: none;
  display: block;
  width: 100%;
  padding: 0;
  background: none;
  border: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

/* The chamfer box's INNER — the row's layout surface, carrying exactly the
   padding, gap and height the drawing gives the row. */
.rd-cb__pick-inner {
  display: flex;
  align-items: center;
  gap: 9px;
  min-height: 42px;
  padding: 8px 11px;
}

.rd-cb__pick:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* ⚠️ **THE PICKED MARK — 13px, 1.5px, drawn.** `ScreenPlanPick`'s row opens on
   `width: 13, height: 13, border: 1.5px solid (on ? t.acc : t.ctrl),
   background: on ? t.acc : 'transparent'`
   (`rd-events-lab-shooter.jsx:35`), and James ruled 27 Aug that this is the one
   add-drill picker shape — the shooter's add-drill sheet and THIS picker both.
   It replaces the `Add` / `Added` word that sat at the right of this row
   (`rd-cb__pick-act` and `rd-cb__pick-act--on`, both retired here and written
   without their leading dots: a dotted retired name satisfies this screen's own
   class sweep and would report a deleted rule as resolved).

   Hand-rolled rather than called, because there is no kit square-mark primitive
   to call — `kit/row.rs` carries no selected state and neither `RdSegmented` nor
   `RdChipSelector` is a list row. The live precedents for the same absence are
   page-local for the same reason and this block is cut to match them:
   `rd-ev-pick__box` / `--on` (`adm1-pr3.css:201`, the admin event form's drill
   picker — 16px, 1.5px, accent fill when on) and `rd-plan__pick-box` / `--on`
   (`s13-plan.css`, the shooter sheet this ruling's other half re-dressed), both
   written without their leading dots because they are ANOTHER part's live names
   and a dotted mention here would offer this part a class it does not own. No
   token is minted: `--rd-ctrl` is the resting border every control in the
   product takes, `--rd-acc` the fill.

   ⚠️ **BOTH ARMS OF THIS SCREEN WEAR THIS ONE BLOCK.** The phone sheet row and
   the tablet `Drill library` row (§T) both carry `rd-cb__pick-box`, and there is
   deliberately **no** `rd-cb-tab__pick-box` twin: one page, one part, one shape.
   The `-tab__` prefix earns its keep where the arms genuinely differ — the row
   is a stroked chamfer card here and a 54px divider row there — and a mark that
   is 13px on one arm and 13px on the other is not two shapes, it is one shape
   copied, which is how the two arms drift to two sizes.

   Presentational only. The row's `aria-pressed` is what states the pick to a
   screen reader, so this element is empty, unlabelled and not the target. */
.rd-cb__pick-box {
  flex: none;
  width: 13px;
  height: 13px;
  border: 1.5px solid var(--rd-ctrl);
  background: transparent;
}

.rd-cb__pick-box--on {
  border-color: var(--rd-acc);
  background: var(--rd-acc);
}

/* ⚠️ **THE STACKED PAIR STAYS, AND THAT IS NOT AN OVERSIGHT.** The shooter
   sheet's re-dress put its name and meta on ONE line because `ScreenPlanPick`
   draws them that way. THIS row is drawn stacked — `rd-course-builder.jsx:101`
   is a `flex: 1, minWidth: 0` wrapper over a name and a `marginTop: 2` sub — so
   the mark is the only thing the ruling moves here. Two pickers, one MARK, each
   still on its own drawn row. */
.rd-cb__pick-text {
  flex: 1;
  min-width: 0;
}

.rd-cb__pick-name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .05em;
  font-size: 11px;
  color: var(--rd-fg);
}

.rd-cb__pick-sub {
  margin-top: 2px;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9px;
}

/* ── Stage Three — the landing ───────────────────────────────────────── */

.rd-cb__chips {
  display: flex;
  gap: 6px;
  margin: 10px 0 12px;
}

.rd-cb__saved-lab {
  margin-top: 11px;
  margin-bottom: 7px;
}

/* The runs strip. Stated where the runs card will live, not left as a hole. */
.rd-cb__runs {
  padding: 11px 13px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 46px;
  margin-top: 11px;
}

.rd-cb__runs-b {
  margin-top: 4px;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
}

.rd-cb__runs-n {
  flex: none;
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 15px;
  color: var(--rd-fg3);
}

/* The promise, verbatim from the ruling's two bounding lines. */
.rd-cb__promise {
  text-align: center;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.45;
  text-wrap: pretty;
}

.rd-cb__pair {
  display: flex;
  gap: 8px;
}

.rd-cb__pair > * {
  flex: 1;
  min-width: 0;
}

/* Delete, edit-mode only — ruled 18 Aug §2b, mirrored from `s13-plan.css`'s
   `.rd-plan__danger`: quiet, danger-toned, unfilled, furthest from the
   thumb. Undrawn on this screen for the same reason it was undrawn there —
   nothing here is drawn twice. */
.rd-cb__danger {
  width: 100%;
  min-height: 44px;
  margin-top: 4px;
  border: 0;
  background: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 10px;
  color: var(--rd-danger);
  cursor: pointer;
}

/* ---------------------------------------------------------------------------
   §T · THE TABLET RENDERING

   Drawn: **`07 (Tablet) Course Builder`** — component
   `ScreenTabCourseBuilder`, row 01 "Course Left, Library Right". Rust:
   `pages/course_builder.rs`, the `DeviceClass::Tablet` arm.

   ⚠️ A SECOND BLOCK IN THIS PART, NOT A SECOND PART FILE — same reasoning
   `s11.css` and `train.css` §T both give: one screen, two devices, one
   file, one owner. `.rd-cb-tab__*` is disjoint from `.rd-cb__*` above.

   ⚠️ THE LIBRARY'S 340 IS A BARE NUMBER, NOT `ListWidth`. This pane is the
   FIXED, right-hand side of a reversed pair — course fluid-left, library
   fixed-right — which is `kit-tablet-panes.css`'s own documented
   side-column precedent (`panes.rs`: "two right-hand side-columns … none
   of them calls TabPanes"), not `RdPanes`'s shape. See the page's module
   doc for the full argument.

   ⚠️ TYPE FLOORS. `01 Foundations` §05: 11px mixed-case, 9.5px tracked
   uppercase. Every drawn size below sits at or above both floors already
   — none needed raising.

   ⚠️ Both `.rd-cb-tab__course` and `.rd-cb-tab__library` own their own
   vertical scroll, for `kit-tablet-panes.css`'s own reason:
   `.rd-tshell__content` is `overflow: hidden`, so nothing above these two
   columns scrolls the document.
   --------------------------------------------------------------------------- */

.rd-cb-tab__state,
.rd-cb-tab__error {
  padding: 24px 28px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
}

/* Same fallback at tablet, centred on the ground the shell leaves. */
.rd-cb-tab__state {
  display: flex;
  justify-content: center;
  color: var(--rd-fg3);
}
.rd-cb-tab__error { color: var(--rd-danger); }

/* The Saved landing, mounted bare in the shell's content region — "do not
   invent a new frame". `saved_step`'s own eyebrow and heading carry the
   page's title, so this is inset only, never a second column. */
.rd-cb-tab__saved {
  box-sizing: border-box;
  max-width: 640px;
  padding: 24px 28px;
  overflow-y: auto;
}

/* The two-pane row. A ROW because the shell's content region already is one
   (`kit-tablet-shell.css` §7) — same argument `RdPanes` makes for its own
   fragment, applied here to a hand-rolled pair. */
.rd-cb-tab__body {
  flex: 1;
  min-width: 0;
  display: flex;
  overflow: hidden;
}

/* ── Course, left, fluid ─────────────────────────────────────────────── */

.rd-cb-tab__course {
  flex: 1;
  min-width: 0;
  box-sizing: border-box;
  padding: 20px 26px;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overflow-x: hidden;
}

/* ⚠️ THE SIZING IS ON THE WRAPPER, NOT ON THE CHAMFER BOX'S INNER.

   `RdChamferBox` is two nested layers and the OUTER *is* the stroke
   (`_legacy-kit-chamfer-button.css`, `.rd-box` / `.rd-box__inner`). Its
   `inner_class` prop reaches the inner only, so a `max-width` passed there caps
   the FILL while the stroke-coloured outer stays a full-width block — at tablet
   width that exposed ~950px of bare accent beside a 420px field. Staging,
   2026-08-19.

   The drawing puts the geometry on a wrapper around the label and the field
   (`rd-tablet-authoring.jsx:242` — `{ marginBottom: 16, maxWidth: 420 }`), so
   that is where it goes. Anything that must bound the BOX rather than its face
   — width, margin — belongs on this wrapper; only padding and the type belong
   on `inner_class`. */
.rd-cb-tab__field-wrap { max-width: 420px; }

.rd-cb-tab__field-lab { margin-bottom: 7px; }

.rd-cb-tab__field {
  padding: 13px 13px 12px;
}

/* Designer, export-15 reply item 6: the name field's stroke is `t.ctrl` AT REST
   and accent on FOCUS ONLY. The rest half is `Stroke::Ctrl` at the call site;
   this is the focus half. `:focus-within` because the focusable thing is the
   input INSIDE the box while the stroke is the box's outer layer — the same
   shape `.rd-scorein__step:focus-within` uses (`s03-splits.css:44`).
   (0,2,0), so it outranks `.rd-box--stroke-ctrl`'s (0,1,0). */
.rd-cb-tab__field-wrap .rd-box:focus-within { background: var(--rd-acc); }

.rd-cb-tab__name-in {
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: var(--rd-fg);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 15px;
  line-height: 1.2;
}

.rd-cb-tab__name-in::placeholder { color: var(--rd-fg3); }
.rd-cb-tab__name-in:focus { outline: none; }

/* ⚠️ SPECIFICITY DEFENCE, not decoration — do not flatten this to one class.

   The pre-kit backfill styles bare inputs by ATTRIBUTE:
     input[type="text"], … { border: 1px solid #d1d5db; outline: none; }
                                                        _legacy-base.css:115
     input:focus, … { box-shadow: 0 0 0 2px #3b82f6; }   _legacy-base.css:157
   Those are (0,1,1) and `.rd-cb-tab__name-in` above is (0,1,0), so THEY WIN:
   undefended, a 1px #d1d5db rectangle draws INSIDE the chamfer and a 2px blue
   ring lands on focus — a raw hex in a screen whose colours are token roles,
   over a stroke this screen now moves on focus itself.

   The compound is (0,2,0) and outranks both. It is the pattern au5.css:76-97
   documents ("THIS WILL BITE EVERY KIT PRIMITIVE THAT BINDS A REAL <input>"),
   scoped to this screen, editing nothing in the backfill block — which is not
   this lane's to touch — and correct still if that block is ever removed. */
.rd-cb-tab__field .rd-cb-tab__name-in {
  border: 0;
  box-shadow: none;
}

.rd-cb-tab__stages-lab { margin: 18px 0 8px; }

.rd-cb-tab__none {
  max-width: 52ch;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--rd-fg3);
  text-wrap: pretty;
}

/* ⚠️ DIVIDER ROWS, NOT CARDS — and no gap, because the rule between two rows IS
   the separation. `rd-tablet-authoring.jsx:250` draws the course pane's stages
   as 56px rows with `borderBottom: 1px solid t.line`, which is the same idiom
   `.rd-cb-tab__pick` already ships in the library pane on the right-hand side of
   this very screen. This stack used to be a `display: grid; gap: 8` of chamfer
   cards, which put two row idioms on one drawn screen. */
.rd-cb-tab__stages {
  display: grid;
}

.rd-cb-tab__stage {
  display: flex;
  gap: 16px;
  align-items: center;
  min-height: 56px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-cb-tab__n {
  flex: none;
  font-family: ui-monospace, monospace;
  font-size: 12px;
  color: var(--rd-fg3);
}

.rd-cb-tab__stage-text { flex: 1; min-width: 0; }

/* The plain body role at 13px — the drawn row's name (`:252`), not the
   condensed display face this used to wear when the row was a card. */
.rd-cb-tab__stage-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg);
}

.rd-cb-tab__stage-sub {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* ⚠️ REMOVE, not the phone's move-up handle — the drawn tablet row's own
   action (`rd-tablet-authoring.jsx:256`). A word, in the tracked-uppercase
   accent role, so it reads as the row's one control and not as a label. */
.rd-cb-tab__remove {
  flex: none;
  min-height: 44px;
  padding: 0 4px;
  border: 0;
  background: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-fg3);
  cursor: pointer;
}

.rd-cb-tab__remove:hover,
.rd-cb-tab__remove:focus-visible {
  color: var(--rd-danger);
  outline: none;
}

/* KEPT PAST THE DRAWING — the trio, its caption, the offline strip, the save
   error and Create/Save/Delete. Not drawn on this frame; see the module doc. */
.rd-cb-tab__foot {
  margin-top: auto;
  padding-top: 16px;
  display: grid;
  gap: 10px;
  max-width: 420px;
}

.rd-cb-tab__trio { padding: 10px 0; }

.rd-cb-tab__derived {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  text-align: center;
}

.rd-cb-tab__danger {
  width: 100%;
  min-height: 44px;
  margin-top: 2px;
  border: 0;
  background: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-danger);
  cursor: pointer;
}

/* ── Library, right, fixed 340 ───────────────────────────────────────── */

/* ⚠️ THE FIXED SIDE IS `border-left`, not `border-right` — the drawn
   `borderLeft: 1px solid t.line` and the same rule `kit-tablet-panes.css`
   states for the two 300px side-columns it documents and does not own:
   the divider is drawn on whichever side the fixed pane's OWN edge is. */
.rd-cb-tab__library {
  flex: none;
  width: 340px;
  box-sizing: border-box;
  border-left: 1px solid var(--rd-line);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.rd-cb-tab__lib-head {
  flex: none;
  padding: 14px 16px;
  border-bottom: 1px solid var(--rd-line);
}

/* Same wrapper rule as the name field above: a margin on `inner_class` pushes
   the FILL down inside a box that has not moved, so 8px of bare stroke shows
   above the search field and the face hangs low. The gap between the label and
   the box is the box's, so it rides the wrapper. */
.rd-cb-tab__search-wrap { margin-top: 8px; }

.rd-cb-tab__search {
  padding: 9px 11px;
}

.rd-cb-tab__search-in {
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: var(--rd-fg);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
}

.rd-cb-tab__search-in::placeholder { color: var(--rd-fg3); }
.rd-cb-tab__search-in:focus { outline: none; }

/* SPECIFICITY DEFENCE — see `.rd-cb-tab__field .rd-cb-tab__name-in` above for
   the full note. Same trap, same (0,2,0) compound. */
.rd-cb-tab__search .rd-cb-tab__search-in {
  border: 0;
  box-shadow: none;
}

.rd-cb-tab__lib-rows {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
}

.rd-cb-tab__lib-none {
  padding: 20px 16px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

.rd-cb-tab__pick {
  appearance: none;
  width: 100%;
  min-height: 54px;
  padding: 0 16px;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  background: none;
  display: flex;
  align-items: center;
  gap: 12px;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: inherit;
}

.rd-cb-tab__pick:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-cb-tab__pick-text { flex: 1; min-width: 0; }

.rd-cb-tab__pick-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
}

.rd-cb-tab__pick-sub {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* ⚠️ **THE PANE'S MARK IS `.rd-cb__pick-box`, AND THERE IS NO `-tab__` TWIN
   HERE ON PURPOSE.** The tablet row wears the phone row's mark class verbatim —
   one page, one part, one shape (see that block above). `rd-cb-tab__pick-act`
   and `rd-cb-tab__pick-act--on`, the pane's retired `Add` / `Added` rules, are
   deleted and named here without their leading dots for the reason this part's
   phone block records: a dotted retired name satisfies the screen's own class
   sweep and reports a deleted rule as resolved. */
/* ==== S17-TABLET · 17 (Tablet) Leaderboard — the board surface · owner: lane-lib ==== */

/* ---------------------------------------------------------------------------
   The tablet rendering of `/leaderboard/:drill_id?:from`, and of the drill-less
   entry `/leaderboard` the 18 Aug ruling created (`pages/leaderboard.rs`).
   Drawn: `ScreenTabLeaderboard`, four frames — the
   board, the light/all-time board, the nobody-has-run-it board, and the PICK
   state ("From the Community tab · no drill in hand").

   ⚠️ SIBLING OF s17.css, NOT AN EXTENSION OF IT. `.rd-lb*` is the phone screen;
   `.rd-tlb*` here is the tablet one. The two name spaces are disjoint and this
   file declares nothing `s17.css` declares and overrides nothing it declares —
   which is what lets the two renderings live in one component without either
   lane's rules reaching the other's device.

   ⚠️ THE ROWS ARE s17.css's ROWS, BORROWED WHOLE AND NOT REDECLARED. The drawn
   tablet row is the same three columns as the phone's (rank · who · value, with
   your own row accented on all three) at 46px instead of 48. Two px is not a
   second row primitive, so the tablet board mounts the SAME markup and the same
   `rank_class` / `name_class` / `value_class` helpers, and this file adds only
   the two-up grid that holds them (§6). `.rd-lb__row` and its children are
   borrowed and NOT redeclared: this file names no rd-tlb row, rank, name or
   value class at all, and `the_tablet_board_mounts_the_same_row_as_the_phone`
   pins that by substring.

   ⚠️ THAT TEST MATCHES PROSE TOO, WHICH IS WHY THIS PARAGRAPH TALKS AROUND THE
   FOUR NAMES INSTEAD OF SPELLING THEM. The guard is a plain `contains` over the
   whole part, so a comment that quotes the class it promises not to declare
   fails the test as loudly as the declaration would. Do not "fix" that by
   loosening the guard: a substring check over the whole file is what makes it
   impossible to sneak a second row primitive past it in any syntax.

   ⚠️ AMENDED — TWO GRID-SCOPED RULES REACH THE BORROWED ROW, and the sentence
   above used to say none did. The two drawings genuinely disagree in exactly
   two places, both of them paint rather than anatomy:

     · the rank column is 26px here and 20 on the phone, because the tablet's
       rank is an ORDINAL (`1st`) and the phone's is a numeral (`1`);
     · your own row carries a `--rd-surf2` tint here and none on the phone.

   Both live in §6, both are scoped under `.rd-tlb__grid`, and neither can be
   seen by the phone list. They are overrides of two properties on two borrowed
   classes — not a second row primitive, which would be a duplicate ANATOMY
   (its own flex box, gap, min-height and hairline) that then drifts silently
   when either device changes. Stated here rather than left to be discovered,
   because "overrides nothing" was load-bearing prose and is now false.

   ANATOMY — the drawing's own split:

     [ .rd-tshell__content, a flex row ]
       [ .rd-tlb__side  300px, fixed, 1px right rule ]
       [ .rd-tlb__board fluid ]

   ⚠️ 300 IS NOT `RdPanes`, AND THAT IS CHECKED RATHER THAN ASSUMED.
   `kit-tablet-panes.css`'s own ladder note names this very column as one of the
   four structural 300px columns that are NOT list panes: *"the leaderboard's
   standing-and-period column (`rd-tablet-social.jsx:327`), a filter block whose
   first child is the words `Your standing`."* The corpus frame hand-rolls it and
   calls no `TabPanes`, so this file hand-rolls it too. Admitting 300 to
   `ListWidth` to reuse the primitive would have put a sixth step on that scale
   on the strength of an element that is not a list.

   ⚠️ THE SCROLL IS A STATED DEVIATION, FOR `kit-tablet-panes.css`'s REASON.
   The drawing writes `overflow: 'hidden'` on both columns, which is right for a
   still frame holding a nine-row fixture and wrong in a shipped app: nothing
   above these two elements scrolls (`.rd-tshell` is `overflow-y: hidden` and
   `.rd-tshell__content` is `overflow: hidden`, kit-tablet-shell.css §1 and §7),
   so a forty-row board or a hundred-drill picker would be unreachable with no
   scrollbar anywhere on screen to say so. Each column owns its own scroll.
   --------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
   1. The standing column — 300px, fixed, ruled off from the board.
   --------------------------------------------------------------------------- */
/* `ScreenTabLeaderboard`, the left column — width 300, flex none, borderRight 1px t.line,
   padding '20px 22px', flex column, gap 20.

   `min-height: 0` is what makes the scroll above actually reach: a flex child
   defaults to `min-height: auto` and refuses to shrink below its content, which
   would push the overflow up into the shell region that is `hidden`. */
.rd-tlb__side {
  flex: none;
  width: 300px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 20px;
  min-height: 0;
  overflow-y: auto;
  padding: 20px 22px;
  border-right: 1px solid var(--rd-line);
}

/* The column's section labels — `Drills`, `Your standing`, `Period`. One role,
   three uses: `tdisp(700, 78, '.12em')` at 9px
   (`ScreenTabLeaderboard`, all three of that column's labels).

   9px is below the tablet mixed-case floor and above the condensed-uppercase
   one — `01 Foundations` §05: *"Mixed-case text never goes below 11px on the
   tablet. Condensed uppercase labels with tracking may go to 9.5px"* — and this
   is the second kind. The drawn 9 is 0.5px under even that, and it is
   transcribed rather than floored up because the same value is drawn on every
   label of every tablet frame in the corpus; floor-correcting one screen would
   make this column disagree with the eleven beside it. Flagged, not fixed. */
.rd-tlb__label {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .12em;
  font-size: 9px;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   2. Your standing — the loudest thing on the page.
   --------------------------------------------------------------------------- */
/* `ScreenTabLeaderboard`, the standing — mono, 42px, `t.acc`, lineHeight 1, marginTop 7.
   The document's own note: *"The standing is 42px mono in accent — the loudest
   thing on the page, as it is on the phone."*

   `--rd-acc` and never `--rd-volt`: this is TEXT, and the token layer binds text
   and strokes to acc while volt is the accent as a fill. The two hold the same
   hex in dark, so writing volt here would be correct by coincidence and render a
   near-invisible figure on light.

   Qualified by the block rather than written bare, so it beats `.rd-num`'s own
   size regardless of which part the assembler appends last — `.rd-stat`'s rule
   makes the same move for the same reason. */
.rd-tlb__standing .rd-tlb__standing-fig {
  display: block;
  margin-top: 7px;
  font-size: 42px;
  line-height: 1;
  color: var(--rd-acc);
}

/* `of 40 this month` (`ScreenTabLeaderboard` — tbody, 12, fg3, marginTop 6). */
.rd-tlb__standing-of {
  margin-top: 6px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   3. The period group — three options, STACKED.
   --------------------------------------------------------------------------- */
/* `ScreenTabLeaderboard`, the period block — a grid at gap 7 holding three `TabChip`s at
   h=40. The document rules the shape in as many words: *"Period stays three
   options, stacked. Not a dropdown and not a wider segmented control — three is
   few enough to show, and stacking them in a 300px column keeps each one a
   full-width target."*

   ⚠️ NOT `RdChipSelector`, AND THE REASON IS THE ROW. That primitive IS the
   kit's chip control and it is a `flex` row with `flex-wrap` — the group is the
   component, by its own spec §7 — so mounting it here and turning it into a
   column from outside would be a screen re-laying-out a primitive's internals.
   The kit has no stacked-chip variant and no `TabChip`; see the component's own
   note on relay-3 §1.

   ⚠️ INERT, AND DRAWN AS THE LIVE CONTROL ANYWAY — the same state, for the same
   measured reason, as the phone group `s17.css` carries: nothing on the wire
   carries a period, so all three would return the same board today. The cells
   are `disabled` and the note below them says so. */
.rd-tlb__periods {
  display: grid;
  gap: 7px;
  margin-top: 9px;
}

/* One cell. `TabChip`, in the tablet kit — minHeight h, flex,
   align center, padding '0 13px', `tdisp(700, 78, '.08em')` at 10px, chamfer 8,
   ground `t.chip`, label `t.fg2`. Full-width because the column is the target
   size, per the stacking argument quoted above. */
.rd-tlb__period {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 40px;
  box-sizing: border-box;
  padding: 0 13px;
  background: var(--rd-chip);
  border: 0;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700; font-stretch: 78%; letter-spacing: .08em;
  font-size: 10px;
  color: var(--rd-fg2);
  text-align: left;
  cursor: not-allowed;
}

/* ⚠️ THE SELECTED STATE IS A FILL FLIP, NOT AN ACCENT OUTLINE, ruled 18 Aug.
   `17 (Tablet) Leaderboard`: *"The filter and period chips lose the accent
   outline… the selected state is a fill flip, volt on dark and ink on light,
   because the contrast has to be structural in daylight and that rationale does
   not weaken on a tablet."*

   Both halves come off the token pair the substrate already carries for exactly
   this — `--rd-chip-on-fill` is `--rd-volt` in dark and `--rd-fg` in light, and
   `--rd-chip-on-label` is its readable partner. Writing the two hexes here would
   have re-declared the ruling in a place nothing else reads.

   The modifier ADDS to the base, so lighting a cell never re-bases it and the
   cell keeps its geometry and its type role at the moment it changes state. */
.rd-tlb__period--on {
  background: var(--rd-chip-on-fill);
  color: var(--rd-chip-on-label);
}

/* Why the group above does nothing. Same sentence the phone prints, same
   reason. */
.rd-tlb__note {
  margin-top: 7px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   4. The scope sentence — the tablet's clearest dividend on this page.
   --------------------------------------------------------------------------- */
/* `ScreenTabLeaderboard`, the scope sentence — tbody 11, fg3, lineHeight 1.55, `marginTop:
   'auto'` so it sits at the foot of the column whatever is above it. The
   document: *"The scope sentence stays… Room to keep it visible is the tablet's
   clearest dividend on this page."* */
.rd-tlb__scope {
  margin-top: auto;
  padding-top: 20px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.55;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   5. The picker — the drill list, in the standing column's place.
   --------------------------------------------------------------------------- */
/* `ScreenTabLeaderboard`'s pick state — a grid at gap 7 of 52px rows, each a 1px
   `t.ctrl` boundary around a name and a meta line.

   ⚠️ `--rd-ctrl`, NOT `--rd-line`. The token layer's own distinction: *"--rd-line
   divides; --rd-ctrl bounds a control."* These rows are buttons — picking one
   fills the pane — so the boundary is a control's, and it is also the WCAG
   1.4.11 floor for one. The drawing writes `t.ctrl` and it is right. */
.rd-tlb__picks {
  display: grid;
  gap: 7px;
  margin-top: 9px;
}

.rd-tlb__pick {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  width: 100%;
  min-height: 52px;
  box-sizing: border-box;
  padding: 0 13px;
  background: none;
  border: 1px solid var(--rd-ctrl);
  text-align: left;
  cursor: pointer;
}

.rd-tlb__pick:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -3px;
}

/* The picked row's name — tdisp(800, 78, '.04em') at 12. */
.rd-tlb__pick-name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .04em;
  font-size: 12px;
  color: var(--rd-fg);
}

/* The picked row's second line — tbody 11, fg3. The drawn text is
   `6 rds · yours`; the source half is the word `yours` because every drill this
   list can offer is the caller's own — the same collapse `course_builder.rs`
   makes on the add-stage sheet, and for the same reason. */
.rd-tlb__pick-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   6. The board pane — two-up.
   --------------------------------------------------------------------------- */
/* `ScreenTabLeaderboard`, the board pane — flex 1, minWidth 0, padding '18px 26px'.
   `min-width: 0` is not a default that happened to work: without it the pane
   refuses to shrink below its content and pushes the 300px column narrower than
   the width §1 just fixed. */
.rd-tlb__board {
  flex: 1;
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
  padding: 18px 26px;
}

/* The two-up board — `gridTemplateColumns: '1fr 1fr', gap: '0 30px'`.
   The document's argument: *"Forty names in one column at 1194px is a narrow
   ribbon in a wide pane. Two columns fit twice as many above the fold, which is
   what makes the standing checkable."*

   Row order runs ACROSS then down, which is `grid-auto-flow: row`'s default and
   is what the drawn fixture shows (1st and 2nd side by side). Stated because the
   alternative — a column flow, 1..20 down the left and 21..40 down the right —
   is the other reasonable reading of a two-up board and the drawing settles it. */
.rd-tlb__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 30px;
}

/* THE TWO PLACES THIS FILE TOUCHES A BORROWED ROW, both scoped INSIDE
   `.rd-tlb__grid` so the phone list cannot see either. See the banner's
   amendment at the head of this file for why these two and only these two.

   1. The ordinal's column. The drawn tablet rank is `{ord(i + 1)}` in a 26px
      right-aligned column (`rd-tablet-social.jsx:403`) — `1st`, `22nd`, `103rd`
      — against the phone's bare numeral in 20 (`rd-leaderboard.jsx:66`). Three
      and four glyphs do not fit a column sized for two, and the suffix must not
      be what wraps: the whole point of an ordinal on a two-up grid is that the
      right-hand column starting at `21st` reads as a continuation rather than a
      second board starting at 21. The string is chosen in Rust
      (`RankStyle::Ordinal`); only its width is decided here. */
.rd-tlb__grid .rd-lb__rank { width: 26px; }

/* 2. Your own row's surface tint — `background: me ? t.surf2 : 'transparent'`
      (`rd-tablet-social.jsx:402`), and row 01 states it: *"Your row keeps the
      accent and the surface tint"* (`17 (Tablet) Leaderboard.html:53`).

      ⚠️ THE TINT IS THE TABLET'S AND NOT THE PHONE'S, which is why this rule is
      here rather than in s17.css. The phone frame gives your row the three
      accents on a transparent ground (`rd-leaderboard.jsx:65-73`); on a
      two-up grid of forty rows the accent alone is a colour difference the eye
      has to hunt for, and the tint is the band that makes it findable — which
      is the frame's own argument for the two-up board.

      The `--me` modifier is emitted on both devices by `row_class`; only this
      rule paints it. Full-bleed by design: the row is a flex box with no
      padding of its own, so the tint runs the column's width and stops at the
      30px grid gutter, which is what the drawing shows. */
.rd-tlb__grid .rd-lb__row--me { background: var(--rd-surf2); }

/* The pick-state and empty-state sentences in `ScreenTabLeaderboard`
   — `maxWidth: '52ch'`, tbody 13, fg2, lineHeight 1.6. One rule for both,
   because they are the same element carrying two sentences. */
.rd-tlb__prose {
  max-width: 52ch;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  line-height: 1.6;
  color: var(--rd-fg2);
}

/* The scope group's chip row, and the org switcher under it.

   ⚠️ BOTH LIVE IN THE STANDING COLUMN NOW. This rule read
   `margin-bottom: 16px` while the chips sat above the board, where they were
   kept as the phone screen's own control on a frame that drew none. Row 02 v0.2
   draws the group in the column — a `Scope` label, its chips, and the org chips
   9px below them inside the same group, above Period — so the spacing is the
   label-to-control step the Period group already uses (`.rd-tlb__periods`), not
   a pane margin.

   ⚠️ STATED DEVIATION: the drawn chips are a `display: grid` STACK of
   full-width `TabChip`s in the 300px column, and these are `RdChipSelector`'s
   wrapping row. The stack is a still-frame layout; the row is the kit's one
   accessible chip control (roving tabindex, one radiogroup, the adjudicated
   fill-flip lit state), and reaching into `.rd-chip-row` from a page part to
   restack it has no precedent in this build. Three scope chips fit one line in
   this column; org names wrap onto a second. */
.rd-tlb__scopes { margin-top: 9px; }
.rd-tlb__orgs   { margin-top: 9px; }

/* The kit blocks the pane still mounts above and below the board — the offline
   banner, the error banner and the position strip. Undrawn on this frame and
   not invented here: they are the phone screen's own, kept because a tablet
   rendering that dropped them would take away the last working path to a
   capability. Only their spacing in this pane is this file's. */
.rd-tlb__banner { margin: 0; }
.rd-tlb__strip  { margin-top: 14px; }
/* ==== S14-TABLET · 14 (Tablet) People — the list beside the person · owner: lane-3 ==== */

/* ⚠️ ADDED 2026-08-18. Drawn as `ScreenTabPeople` in `14 (Tablet) People`
   (`rd-tablet-social.jsx:87`), row 01 "The List And The Person", in all four
   drawn frames: a friend selected, Requests, Light, and Nobody yet. Screen:
   apps/web/src/pages/friends.rs, which carries the selection ruling, the three
   omitted stats and every carried / undrawn flag. Only the values live here.

   ⚠️ THIS PART DOES NOT COVER THE CHALLENGES LIST PANE. `14 (Tablet)` row 02
   ("The Board Beside The List") is `ScreenTabChallengeInvite`, whose list pane
   and detail already ship as `.rd-chal-t__*` in s14-chal.css — the `/challenges`
   LIST route joined the chrome seam in this same change and REUSES those names
   rather than declaring a second set. A screen family drawn across two documents
   is still one family; two name spaces for one drawn row would be the drift the
   corpus ledger exists to catch.

   ⚠️ NO `@media` HERE, and that is the standing rule for every tablet screen
   part: the breakpoint is asked ONCE in Rust (`crate::device::use_device_class`
   picks `RdTabletShell`), and the only width query in the app is
   kit-tablet-shell.css §8. A query here would paint this screen at phone width
   on a page whose Rust arm never mounted it.

   Chrome comes from the kit and nothing below re-declares any of it:
   `RdTabletShell` (`.rd-tshell*`), `RdPanes` (`.rd-tpanes*`, which owns the
   380px list column and both scrolls), `RdTabletRail` (`.rd-trail*`),
   `RdSectionTabs` (`.rd-stabs*`) and `RdTabChip` (`.rd-tchip*`). What is left
   is the one thing the primitives have no shape for — the rows inside the list
   pane, and the person panel beside them.

   ⚠️ ADDED 2026-08-19: the Add Friend modal, `14 (Tablet) People` row 04 v0.2
   (`ScreenTabPeople`'s `add` prop) — RULED design relay 5 item 7. Four rules
   below (`.rd-tppl__addmodal-panel/-body/-head/-cancel`); everything else the
   modal shows is `.rd-af__*` reused verbatim from s14-add.css, per the
   comment on each rule. */

/* ── The header action ──────────────────────────────────────────────── */

/* `action="+ Add Friend"` (`rd-tablet-social.jsx:90`). The 44px box and its
   position are `RdTabletShell`'s and live in kit-tablet-shell.css; this is only
   the control reset, so whatever fills that slot paints like the drawn label
   rather than like a browser default. Same shape `.rd-tev__act` takes on
   Events.

   ⚠️ **A `<button>` RESET SINCE relay-5 item 7**, and the two extra lines
   (`background`, `border`, `padding`, `font`, `cursor`) are the whole of the
   change. This class dressed an `<a>` before the ruling moved the header
   action from `Route::AddFriend` to the one Add Friend modal — a press with
   no navigation — so the element in this slot is a `button` now, and a
   `<button>` carries UA chrome an `<a>` never did. `text-decoration`/`color`
   stay for the same reason `.rd-tev__act` states: the box's own colour and
   type come from `.rd-tshell__action`, and this only lets the control fill it
   invisibly. */
.rd-tppl__act {
  text-decoration: none;
  color: inherit;
  display: flex;
  align-items: center;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  cursor: pointer;
}

/* The one disabled user of this reset today is the plan form's action slot
   (saving/offline). The `RdButton` it replaced greyed out when inert; a bare
   reset would stay full accent with a pointer cursor while refusing the
   press, so the grey rides the reset for every borrower. */
.rd-tppl__act:disabled {
  color: var(--rd-fg3);
  cursor: default;
}

/* ── The list pane ──────────────────────────────────────────────────── */

/* The chip strip above the rows is `RdTabChipRow` at `TabChipRowPad::Tight`
   (`kit-tab-chip-row.css`) — `rd-tablet-social.jsx:128`. This part declared it
   by hand until 2026-08-19, as three other screens did. */

/* The count nested INSIDE the Requests chip — `rd-tablet-social.jsx:131`:
   "{...mono, fontSize: 10}". It is a class of its own rather than the shared
   `.rd-num`, which is the 17px figure role and would be two-thirds of the chip.
   The chip's own `.rd-tchip__label` supplies the 7px gap between the word and
   this number, so there is no margin here. */
.rd-tppl__count {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10px;
  font-variant-numeric: tabular-nums;
}

/* The rows' own column (`rd-tablet-social.jsx:135`, "flex: 1, minHeight: 0,
   overflow: 'hidden'"). `.rd-tpanes__list` already owns the fixed width and the
   scroll; this is the column inside it. */
.rd-tppl__list {
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

/* The WARM refresh mark — the phone list's twin, and the same ruling
   (docs/design/kit/L1-loading.md §4.1). The rows keep their values for the
   whole read; this 13px sweep is the only change (12 until the ladder ruling,
   C-43.1, 27 Aug 2026). */
.rd-tppl__refresh {
  display: flex;
  justify-content: flex-end;
  padding: 4px 16px;
}

/* One row — `rd-tablet-social.jsx:137`: minHeight 64, gap 13, padding '0 16px',
   a bottom hairline and a 3px left rule that is transparent until selected.

   A `<button>` rather than a div, because the drawn row IS the selection
   target: 380 x 64 of it, not the name inside it. The reset below is what makes
   a button paint like the drawn row. */
.rd-tppl__row {
  width: 100%;
  min-height: 64px;
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 0 16px;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  border-left: 3px solid transparent;
  background: none;
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
}

.rd-tppl__row:last-child { border-bottom: 0; }

/* The selected row: accent rule and the raised ground together, one modifier
   (`rd-tablet-social.jsx:137`, `i === sel && tab === 'Friends'`). */
.rd-tppl__row--on {
  border-left-color: var(--rd-acc);
  background: var(--rd-surf2);
}

/* A request row is not a selection — the drawing paints no accent rule on the
   Requests tab at all, because the decision is the Accept on the row and not
   which row is lit. Kept as a class of its own so the two shapes cannot drift
   into one another. */
.rd-tppl__row--flat { cursor: default; }

.rd-tppl__ident {
  flex: 1;
  min-width: 0;
}

.rd-tppl__name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg);
}

.rd-tppl__meta {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  margin-top: 3px;
}

/* The request row's controls — `rd-tablet-social.jsx:143`: "display: 'flex',
   gap: 7, flex: 'none'". */
.rd-tppl__acts {
  display: flex;
  gap: 7px;
  flex: none;
}

/* Accept, on the row — `rd-tablet-social.jsx:144`: minHeight 40, padding
   '0 12px', a 1px accent border and accent text, tracked micro-label. Outlined
   and never filled: the drawing's governing rule for this whole document is
   *"anything drawn per row cannot take a fill"*, and five requests would put
   five volt fills on one pane. */
.rd-tppl__accept,
.rd-tppl__no {
  min-height: 40px;
  display: flex;
  align-items: center;
  padding: 0 12px;
  background: var(--rd-hollow-fill);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: 0.08em;
  font-size: 9.5px;
  cursor: pointer;
}

.rd-tppl__accept {
  border: 1px solid var(--rd-acc);
  color: var(--rd-acc);
}

/* ⚠️ CARRIED, NOT DRAWN — see friends.rs. The tablet frame draws Accept alone;
   `DeclineFriend` ships and this is its only door on this device, so the
   control stays and the TONE is the phone screen's ruling, unchanged:
   *"Declining a friend request is not destructive, it is just an answer."*
   Neutral, never danger. */
.rd-tppl__no {
  border: 1px solid var(--rd-ctrl);
  color: var(--rd-fg2);
}

/* ── The person panel ───────────────────────────────────────────────── */

/* `rd-tablet-social.jsx:155` — "padding: '26px 30px', display: 'flex',
   flexDirection: 'column', gap: 20". `.rd-tpanes__detail` already sets the
   column and the scroll; this adds the inset and the drawn 20px rhythm. */
.rd-tppl__panel {
  flex: 1;
  min-height: 0;
  padding: 26px 30px;
  display: flex;
  flex-direction: column;
  gap: 20px;
  overflow: hidden;
}

/* The identity block — `rd-tablet-social.jsx:156`, "gap: 16", with the large
   avatar (`<Av lg />`). */
.rd-tppl__id {
  display: flex;
  align-items: center;
  gap: 16px;
}

.rd-tppl__id-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 900;
  font-stretch: 62%;
  letter-spacing: 0.01em;
  font-size: 24px;
  color: var(--rd-fg);
}

.rd-tppl__id-tag {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px;
  color: var(--rd-fg3);
  margin-top: 4px;
}

/* The relationship sentence — `rd-tablet-social.jsx:171`: 11.5px, fg3,
   line-height 1.55, capped at 54ch. The drawing spends a decided-here item on
   keeping it: *"It is the most useful thing on the page and it costs nothing to
   keep visible."* */
.rd-tppl__rel {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
  line-height: 1.55;
  max-width: 54ch;
}

/* The foot — `rd-tablet-social.jsx:174`: "marginTop: 'auto', display: 'flex',
   gap: 12". `margin-top: auto` is what pins it to the bottom of the pane
   however short the panel above it is. */
.rd-tppl__foot {
  margin-top: auto;
  display: flex;
  align-items: center;
  gap: 12px;
}

/* The drawn 240px measure on the one fill (`rd-tablet-social.jsx:175`). The
   button inside is `RdButton`, so this wrapper carries the width and nothing
   else. */
.rd-tppl__challenge { width: 240px; }

/* Remove friend — `rd-tablet-social.jsx:176`: minHeight 44, tracked
   micro-label, danger tone, and NO border and NO fill. The drawing's own
   decided-here item, quoted in friends.rs: *"Remove friend stays quiet text
   beside the primary, never a button. Width does not make a destructive action
   deserve more weight."* */
.rd-tppl__remove {
  min-height: 44px;
  display: flex;
  align-items: center;
  border: 0;
  background: none;
  padding: 0 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: 0.1em;
  font-size: 9.5px;
  color: var(--rd-danger);
  cursor: pointer;
}

/* ── The two pane-level states ──────────────────────────────────────── */

/* "Nobody yet" — `rd-tablet-social.jsx:150`: the whole detail pane centred, one
   sentence at 46ch. This is the drawn empty frame, and its copy is the
   drawing's verbatim. */
.rd-tppl__empty {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px;
}

.rd-tppl__empty-p {
  max-width: 46ch;
  text-align: center;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-fg3);
  line-height: 1.6;
}

/* ⚠️ UNDRAWN — OURS, and flagged as ours. `14 (Tablet)` parks this exact
   question under Open — This Device: *"Nothing is selected on first load.
   Drawn with the first friend selected … An empty pane with a sentence may be
   more honest."* The drawing answers it one way (first friend selected) and the
   screen follows the drawing, so this state is reachable only on the Requests
   chip, where there is no person to show. It borrows the drawn empty pane's
   geometry rather than inventing a second one. */
.rd-tppl__none {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px;
}

/* ── The Add Friend modal (relay-5 item 7, ADDED 2026-08-19) ─────────── */

/* `14 (Tablet) People` row 04 v0.2, `ScreenTabPeople`'s `add` prop
   (`rd-tablet-social.jsx:87-125`). RULED: James, over the designer's lean —
   modal off the People header action, one modal, results under the field
   inside it, the QR doors route away rather than stacking. See
   `pages/friends.rs`'s `TabAddFriendModal`.

   The scrim and the small QR panel are NOT redeclared here — this state and
   the `show_my_code` swap both mount `.rd-af__over` / `.rd-af__over-panel` /
   `.rd-af__over-body` verbatim (s14-add.css), the same classes
   `add_friend.rs`'s own `show_my_code` overlay uses. Only the two things the
   drawing genuinely draws differently from that 250px code card — this
   panel's width, and its own body layout — get a modifier here, the same
   `--code`-style modifier `s18-foot.css` gives `.rd-prof__over-panel--code`
   for the sibling case. */

/* 430 — the drawn panel width (`rd-tablet-social.jsx:91`, "w={430}"). A
   modifier on `.rd-af__over-panel` rather than a second panel class: same
   specificity, decided by source order (`order.txt` lists this part after
   s14-add.css), so this wins the tie exactly as `.rd-prof__over-panel--code`
   does for its own base rule. */
.rd-tppl__addmodal-panel {
  max-width: 430px;
  /* The TabModal mechanics `tablet_shell.rs` cites for its own follow-up —
     "the 78% max height" — rather than the code card's bare `max-height:
     100%`, which sizes to the scrim's own padding and never clips a panel
     this much taller than a QR code. */
  max-height: 78vh;
}

/* The drawn `padding: '20px 22px'` (`rd-tablet-social.jsx:92`) and the drawn
   flex column (`rd-tablet-social.jsx:90-125` nests search, results and the
   doors top-to-bottom) — a column, not `.rd-af__over-body`'s bare padding,
   because `.rd-af__doors`'s own `margin-top: auto` (s14-add.css) needs a
   flex-column ancestor to pin the doors at the foot the way it does on the
   phone page's `.rd-af` root. Without this the doors sit wherever the
   results end, on a panel with a lot more empty space below a short result
   list than the phone's capped 448px column ever leaves. */
.rd-tppl__addmodal-body {
  display: flex;
  flex-direction: column;
  padding: 20px 22px;
}

/* The drawn heading (`rd-tablet-social.jsx:93`): `tdisp(900, 62, '.01em')` at
   20px, uppercase, `lineHeight: 1`. Two lines on the phone
   ("Add a" / "friend"), one here — the drawn tablet frame is a single line,
   "Add A Friend". */
.rd-tppl__addmodal-head {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 900;
  font-stretch: 62%;
  letter-spacing: .01em;
  font-size: 20px;
  line-height: 1;
  color: var(--rd-fg);
}

/* The drawn foot line, `rd-tablet-social.jsx:122`: `textAlign: 'center'`,
   `tdisp(700, 78, '.1em')` at 10px, `fg3`, `marginTop: 14`. The modal's own
   way out — a page has `RdShell`'s back row; a modal has no back row, so this
   is what names the exit. */
.rd-tppl__addmodal-cancel {
  margin-top: 14px;
  width: 100%;
  border: 0;
  background: none;
  padding: 4px 0;
  text-align: center;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 10px;
  color: var(--rd-fg3);
  cursor: pointer;
}
/* ==== S47 · 47 (Mobile) RO Range Day — the lit row and the Pro door · owner: lane-drills ==== */
/*
   Two of drawing 47's rows, and they are the two that sit on screens some
   OTHER document owns — which is exactly why they are a part of their own
   rather than a block appended to either of those parts.

   ROW 01, `.rd-roev*` — the lit staffed-event row on the SHOOTER events list
   (`pages/events.rs`, `EventsPhone`). 47 row 01: *"The event where they hold
   the scorer assignment, while it is live, is the one lit row: accent border,
   volt Run chip."* The list itself is `15 Events` and `s15.css` (owner:
   lane-reskin) owns every `.rd-evl*` name on it. This row is 47's, not 15's,
   so it takes 47's own name space and neither part declares or overrides a
   selector the other names.

   ROW 09, `.rd-rodoor*` — the Pro half of the hand-off, on the console event
   detail (`pages/admin/event_detail.rs`, Overview). 47 row 09: *"the Pro side:
   the RO's mobile Pro Events row carries Run The Day, which opens this arc.
   Pro shows the day; it never scores it."* The console screen is `32 Pro
   Events` and `adm1-*` owns its `.rd-ev*` names; the door is 47's.

   ⚠️ THE TWO NAME SPACES ARE DISJOINT FROM EVERY PART EITHER SIDE, and this
   part declares none of theirs and overrides none of theirs. So its position
   in `order.txt` re-orders no existing pair and nothing here is decided by
   source order. No `@media`: both surfaces are the phone/console renderings
   the components already choose between in Rust.

   ⚠️ THE LIT ROW HAS NO MUTED TWIN, and that is the ruling rather than an
   omission. 47 row 01: the assignment line is *"true for whoever holds the
   assignment and absent otherwise"* — an unassigned reader gets no row at
   all, so there is no second, quieter treatment to style. A `--muted`
   modifier appearing here later would be a new state nobody drew.

   ⚠️ AND NEITHER OF THESE IS A PERMISSION. James, 21 Aug 2026: any org RO,
   instructor or admin may score any event. `event_role` decides whether the
   row is LIT; it decides nothing about the door, which any console reader
   gets. *"The light marks whose job it is, not who is allowed."*
*/

/* ── Row 01 · the lit staffed-event row ───────────────────────────────────── */

/* A whole-row control, so the `<button>` is outside the chamfer box and carries
   no paint of its own — the construction `.rd-evl-row` uses one part up, and
   for the same reason: a `<button>` cannot nest and the chamfer is a two-layer
   clip. The Run chip is therefore a `<span>`: it is the row's label, not a
   second target inside it. */
.rd-roev {
  display: block;
  width: 100%;
  margin-top: 9px;
  padding: 0;
  background: none;
  border: 0;
  text-align: left;
  cursor: pointer;
}

.rd-roev:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* 56px is the phone touch floor on every scoring surface — 47's own ruling,
   and this row is the first of them. */
.rd-roev__inner {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 56px;
  padding: 13px 14px;
}

.rd-roev__copy { flex: 1; min-width: 0; }

.rd-roev__name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .04em;
  font-size: 13px;
  color: var(--rd-fg);
}

/* The assignment line. `--rd-fg2` rather than the `--rd-fg3` the quiet rows
   above it take: this is the one line on the list that is about the reader's
   own job today. */
.rd-roev__sub {
  margin-top: 4px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--rd-fg2);
}

/* The volt Run chip — James picked it from three treatments. A FILL, so the
   token is `--rd-volt` and the ink is `--rd-on-volt`; the accent-as-stroke
   token would be the wrong half of substrate §1.1 here. */
.rd-roev__run {
  flex: none;
  padding: 9px 13px;
  background: var(--rd-volt);
  color: var(--rd-on-volt);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 10px;
}

/* ── Row 09 · the Pro door ────────────────────────────────────────────────── */

/* Sits in the console Overview under the fields and above Delete, with its
   own rule: the day is a different act from editing the record, and Delete is
   a different act again. */
.rd-rodoor {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--rd-line);
  display: grid;
  gap: 7px;
  justify-items: start;
}

.rd-rodoor__btn {
  min-height: 44px;
  padding: 0 18px;
  background: var(--rd-volt);
  color: var(--rd-on-volt);
  border: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 11px;
  cursor: pointer;
}

.rd-rodoor__btn:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* The destination, named under the control — 47 row 09 draws it there and says
   why in the same breath: *"Opens in the shooter app — scoring lives there."* */
.rd-rodoor__note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  line-height: 1.45;
  color: var(--rd-fg3);
}

/* ── The fold · 47's back row at phone width ──────────────────────────────── */

/* ⚠️ WHY THIS BLOCK IS 47'S AND NOT 36'S. The range-day family's chrome is
   drawn twice: `36 (Tablet) Pro Actual Range Day` draws the tablet bar, and
   `47 (Mobile) RO Range Day` rules what happens below 1024 — "the range-day bar
   folds into the board … the bar's facts — event name, stage — live in the
   board's back row and chips, as drawn." The bar is 36's part (`adm-rday.css`,
   which declares NO WIDTH BREAKPOINT and means it); the folded row is 47's, so
   it takes 47's own name space here. Neither part declares or overrides a name
   the other does, and the choice between them is made in Rust — `RangeDayLayout`
   matches on the device class — which is why there is no `@media` in this file
   and no second part.

   The name space was checked before it was chosen: it returned zero hits across
   `apps/web/`, `cli/` and `docs/`, against a known-positive control on this
   part's own row-01 name space.

   ⚠️ THE ORIGINAL THESE FOUR RULES ARE TRANSCRIBED FROM IS `kit-shell.css` —
   the phone shell's own bar, back control, chevron and label. Same drawn row
   (`TShell`), same 42px, same type role, same leading bleed; this part carries
   its own copy because the fold's row is mounted by the range-day layout INSIDE
   the shell's content region rather than by the shell itself, and a lane may
   not reuse another part's names for a different mount. Read that file
   alongside this block before changing either. */

/* The column the fold hands the page. `ShellLayout` already supplied the
   viewport frame, the tab bar and the scrolling content region at this width,
   so this is emphatically NOT a second `100dvh` — it is a column that fills the
   region it was handed, which is what lets the row below stay put while the
   page moves. `min-height`, not `height`: a page shorter than the region must
   not be stretched, and one taller must be allowed to hand its own scroll to
   the region. */
.rd-roback-app {
  display: flex;
  flex-direction: column;
  min-height: 100%;
}

/* 47's row, through `TShell`: `flex: none`, `height: 42`, `padding: 0 18px`,
   the label 9px after the chevron. `flex: none` is the pinned half — the row is
   the way out and may not scroll away under a thumb. */
.rd-roback {
  flex: none;
  display: flex;
  align-items: center;
  height: 42px;
  padding: 0 18px;
  box-sizing: border-box;
}

/* One control spanning chevron, gap and label rather than a 9x15 glyph with a
   caption beside it — the same reasoning the phone shell's own back control
   records, and it matters more here: this row is worn on a range, outdoors,
   often with gloves on. It takes the row's full 42px as its hit area.

   The leading bleed reaches the screen edge for a thumb coming off the bezel;
   the negative margin and the equal padding cancel, so the chevron does not
   move and the label keeps the left edge the drawing puts it on. */
.rd-roback__btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  height: 100%;
  flex: 0 1 auto;
  min-width: 0;
  margin-inline-start: -18px;
  padding-inline-start: 18px;
  padding-inline-end: 0;
  background: none;
  border: 0;
  cursor: pointer;
  font: inherit;
  color: inherit;
  text-align: start;
  -webkit-appearance: none;
  appearance: none;
}

.rd-roback__btn:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* 9x15, and it never shrinks.

   ⚠️ --rd-acc, NEVER --rd-volt: this is a STROKE, and a stroke takes acc, which
   goes deep warm brown in light theme where volt yields an unreadable tan glyph.
   47 ships both themes on every frame — "the kit was tuned for direct sunlight
   and this is the surface that cashes that in" — so the light arm is not a
   hypothetical here. `stroke` inherits, so it reaches the path. */
.rd-roback__chevron {
  flex: none;
  display: block;
  width: 9px;
  height: 15px;
  stroke: var(--rd-acc);
}

/* The shell-title type role: 10px, weight 700, stretch 78%, tracking .16em,
   uppercase, colour fg2 — 47's row exactly.

   Single line with an ellipsis, which the drawing does not declare and we need:
   the row is fixed at 42px and our labels are the family's own exit words
   today, but the same row is what an event or drill name would land in next. A
   wrapping label in a fixed-height box overflows it.

   NO FAMILY DECLARED: the body role family is the shell's already and this row
   inherits it, exactly as the kit's own shell label does. Declaring it again
   here would be this part restating another's decision, and would be the line
   left behind the day that decision changes. */
.rd-roback__label {
  font-weight: 700;
  font-stretch: 78%;
  font-size: 10px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--rd-fg2);
  min-width: 0;
  flex: 0 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── Standings at phone · the column, and only the column ─────────────────── */

/* ⚠️ UNDRAWN, AND STUBBED RATHER THAN PARKED. No document draws this screen at
   phone width: `36 (Tablet) Pro Actual Range Day` draws it at 1194 only, and
   `47 (Mobile) RO Range Day` routes the RO past it — 47's landing is the
   shooter events list and 47's board is one squad's shooter list, a level
   below. What rules it instead is `51 Shell Lab` row 08, Ranks — "Read-only
   standings: rank number, row, mono values, no acts on rows" — which the
   shipped standings body already is, plus 47's own phone chrome, which is what
   this block supplies and all it supplies.

   WHY THIS BLOCK IS 47'S AND NOT 36'S, the same split the fold above records:
   the tablet column with its 760px measure is 36's, in `adm-rday.css`, which
   declares NO WIDTH BREAKPOINT and means it. The phone column is 47's, so it
   takes 47's name space here, and the choice between the two is made in Rust —
   `RangeDayStandings` matches on the device class and hands the mount the
   class — which is why there is no `@media` in this file and no second part.
   The name space was checked before it was chosen: zero hits across
   `apps/web/`, `cli/` and `docs/`, against a known-positive control on this
   part's own fold name space.

   ⚠️ AND IT DECLARES NOTHING ABOUT THE BODY. The rows, the head and the foot
   inside this column are the standings body's, owned by `s15-stand.css`, which
   two surfaces mount; a descendant override of one of its rules from here
   would be this part overriding another's declarations. So the flag below is a
   flag and not a workaround. */

/* The page column at phone. Geometrically the family page column from 36's
   part with two numbers changed, and it is written out rather than composed
   because composing it would mean wearing 36's cap and gutter and then
   unsaying them:

   - 18px sides, which is 47's own frame gutter and the same number the folded
     back row above takes, so the row's label and the board share one left
     edge. 36's is 32, drawn for a tablet held at arm's length.
   - no width cap. 760px is a measure for a 1194px frame; at 320-414 it is
     unreachable, and carrying it would say this column had been thought about
     at a width where it had not.
   - 14px of air under the 42px back row, 24px at the foot, so the last row and
     the scoping sentence clear the tab bar the room ruling keeps below them.

   `overflow: auto` and NOT `hidden`, for the reason the shell states about its
   own content region: the drawn Pro row is four columns on one line with 324px
   of fixed track in it, which does not fit the 284px a 320pt phone leaves —
   `hidden` would conceal that, `auto` contains it here instead of handing it
   to the shell, and it is a no-op from 375pt up where the row fits. The real
   fix is a phone rendition of that row and it belongs to the body's owner;
   FLAGGED, not worked around.

   Height stays indefinite, so this is a horizontal scroller only and the one
   live vertical scroller per mount is still the shell's own content region —
   the standings body's inner list rule stays inert exactly as it does at
   tablet, which is what its `ruling_one_scroller_is_live_per_mount` asks for. */
.rd-rostand {
  flex: 1;
  min-height: 0;
  overflow: auto;
  padding: 14px 18px 24px;
}

/* ── The squad board at phone · `51 Shell Lab` row 04, Boards ─────────────── */

/* ⚠️ UNDRAWN, AND STUBBED RATHER THAN PARKED. No document draws this screen at
   phone width. `36 (Tablet) Pro Actual Range Day` draws it at 1194 only, and
   `47 (Mobile) RO Range Day` never reaches it — 47's landing is the shooter
   events list under Home and 47's board is one squad's shooter list, a level
   below this one. What rules it instead is `51 Shell Lab` row 04, Boards:
   "Live-operation surfaces run mid-act. Exactly one live element (accent border
   or live word), per-row state words, mono values on the right edge, and the
   next act pinned at the foot. Touch floors: 56 phone, 68 tablet." Every rule
   below is one clause of that, plus 47's own floor ruling — "56px is the phone
   touch floor on scoring surfaces" — and 47's 18px frame gutter, which is the
   same number the folded back row above this column takes so the row's label
   and the board share one left edge.

   THREE STUBS, FLAGGED TO THE DESIGNER and written out on the component: the
   squad-level live element (47's `Up next` marks a shooter, not a squad), the
   second chip (47's board can say `Stage 3 of 5` because it is looking at one
   squad; this one is looking at all of them), and the state line carrying the
   bay as well as the progress.

   WHY THIS BLOCK IS 47'S AND NOT 36'S, the same split the two blocks above
   record: the tablet card grid is 36's, in `adm-rday.css`, which declares NO
   WIDTH BREAKPOINT and means it. The phone board is 47's, so it takes 47's own
   name space here, and the choice between the two is made in Rust —
   `RangeDayEvent` matches on the device class and mounts one arm or the other —
   which is why there is no `@media` in this file and no second part. The name
   space was checked before it was chosen: zero hits across `apps/web/`, `cli/`
   and `docs/`, against a known-positive control on this part's own fold name
   space.

   ⚠️ AND IT DECLARES NOTHING THE STATE BLOCKS DECLARE. The loading treatment,
   the transport failure, the empty board and the offline block are the shared
   ones in `adm-rday.css`, which carry no width geometry; the phone arm mounts
   them unchanged and this part neither restates nor overrides one of their
   rules.

   No `border-radius` anywhere: this family's cuts are `RdChamferBox`, and these
   rows are not chamfered — row 04's row is a rule, not a card.

   ⚠️ NO FAMILY DECLARED ANYWHERE IN THIS BLOCK, the same call the fold above
   records: every face here is the body role, which is the shell's already and
   which this column inherits. Restating it would be this part repeating
   another's decision, and would be the line left behind the day that decision
   changes. What IS declared is where a role departs from the inherited one —
   the weight, the stretch, the tracking and the tabular figures. */

/* The column. `RangeDayLayout`'s phone arm already supplied the region and the
   back row above it, so this is not a second viewport: a flex column that fills
   what it was handed, with the chip strip and the foot pinned and only the body
   between them scrolling. */
.rd-roboard {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

/* Row 04's chip strip. `flex: none` — the counts are the folded bar's facts and
   the bar did not scroll away either. */
.rd-roboard__chips {
  flex: none;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 18px;
}

.rd-roboard__chip {
  font-weight: 700;
  font-stretch: 78%;
  font-size: 8.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--rd-fg3);
  border: 1px solid var(--rd-line);
  padding: 4px 7px;
}

/* The first chip is the lit one, as drawn. --rd-acc and never --rd-volt: this
   is a boundary and a word, and volt on a light ground is an unreadable tan —
   47 ships both themes on every frame. */
.rd-roboard__chip--on {
  color: var(--rd-acc);
  border-color: var(--rd-acc);
}

.rd-roboard__chip-gap { flex: 1; }

/* The cross-link to standings, text and not a control: "it is a switch of view,
   not an act on the day". A `<button>` because it navigates on press and the
   kit will not have a bare span do that. */
.rd-roboard__jump {
  flex: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-weight: 700;
  font-stretch: 78%;
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--rd-acc);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.rd-roboard__jump:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* The one live vertical scroller on this mount. The chip strip and the foot are
   outside it on purpose. */
.rd-roboard__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0 18px 8px;
}

.rd-roboard__list {
  display: flex;
  flex-direction: column;
}

/* ⚠️ 56 IS THE FLOOR AND IT IS A RULING, NOT A ROUNDING. 47: "56px is the phone
   touch floor on scoring surfaces. 68 was a tablet ruling for a gloved thumb at
   arm's length; the phone number covers the steppers, the keypad and the board
   rows." `min-height`, so a squad whose state line wraps grows past it rather
   than clipping.

   A rule under each row and none on the last, which is row 04's construction: a
   list of rows, not a stack of cards. */
.rd-roboard__row {
  min-height: 56px;
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 0;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  text-align: left;
  cursor: pointer;
  box-sizing: border-box;
}

.rd-roboard__row:last-child { border-bottom: 0; }

.rd-roboard__row:disabled { cursor: default; }

.rd-roboard__row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* THE ONE LIVE ELEMENT — an accent edge on the row and the accent word inside
   it, which is row 04's "accent border or live word" taken as both on the same
   single row. Exactly one row ever carries this: `live_squad` answers once and
   the board marks the answer, so the count is one by construction.

   ⚠️ NO PADDING, AND THAT IS THE POINT OF AN INSET EDGE. An indent on the live
   row would push its index, name and state 10px right of every other row's,
   and the index column exists precisely to hold one left edge down the board.
   Row 04's live element is an accent border, not an indentation.

   It repeats the 56 floor rather than relying on the base rule, and that is
   deliberate: the floor is the touch ruling, and a modifier that could be
   written without it is a row that could ship 40px tall. Pinned by
   `ruling_the_phone_board_holds_the_boards_anatomy`, which reads every row rule
   in this part and requires the number in each. */
.rd-roboard__row--live {
  min-height: 56px;
  box-shadow: inset 2px 0 0 var(--rd-acc);
}

.rd-roboard__row--live .rd-roboard__state { color: var(--rd-acc); }

/* Row 04's mono index column: 11px wide, 11px type, tertiary, and blank on
   every row that is not complete. It holds its width empty so the names below
   it keep one left edge. */
.rd-roboard__ord {
  flex: none;
  width: 11px;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 11px;
  color: var(--rd-fg3);
}

.rd-roboard__copy {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* The row header: 12px body, one line, ellipsised — an org may name a squad
   anything and the row is a fixed track. */
.rd-roboard__name {
  font-size: 12px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The state word: 8.5px, 78% stretch, .1em tracking, tertiary — accent only on
   the live row, through the modifier above. It wraps rather than clipping,
   because it carries the bay and the next stage and both are what an RO is
   reading the row for. */
.rd-roboard__state {
  font-weight: 700;
  font-stretch: 78%;
  font-size: 8.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  line-height: 1.4;
  color: var(--rd-fg3);
}

/* Row 04's mono value on the right edge. The drawn `mono` is NOT a monospace
   family — it is Archivo at 800/75% with tabular figures, so a column of counts
   aligns without changing typeface, the same reading `adm-rday.css` records for
   the stage index it draws. */
.rd-roboard__val {
  flex: none;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 15px;
  color: var(--rd-fg);
}

/* "tertiary — when there is not" — a day with no stages has no count to print,
   and the em-dash is an absence spelled rather than a blank. */
.rd-roboard__val--none { color: var(--rd-fg3); }

/* Row 04's foot: the next act pinned, reassurance under it, `flex: none` so it
   does not scroll. The top rule separates it from the last row, which would
   otherwise read as another row of the list. */
.rd-roboard__foot {
  flex: none;
  border-top: 1px solid var(--rd-line);
  padding: 12px 18px 16px;
}

/* ⚠️ NO RULE FOR THE FOOT ACT, AND THAT IS THE POINT. Row 04's act is
   `TBtn dense` — the kit's own filled primary, volt ground and `--rd-on-volt`
   ink over a two-layer chamfer — which `RdButton { kind: Kind::Primary }`
   already is. Restating its ground here would be this part overriding the
   kit's declarations, and hand-rolling it would be the second filled button in
   the product. It also keeps its own height rather than the row floor: 56 is
   47's ruling for "the steppers, the keypad and the board rows", and the kit's
   act is drawn at its own number. */

/* The reassurance line: 10.5px, tertiary, centred under the act. */
.rd-roboard__note {
  margin-top: 8px;
  text-align: center;
  font-size: 10.5px;
  line-height: 1.45;
  color: var(--rd-fg3);
}

/* ── Today at phone · `51 Shell Lab` row 01, Lists ────────────────────────── */

/* ⚠️ UNDRAWN, AND STUBBED RATHER THAN PARKED. No document draws this screen at
   phone width. `36 (Tablet) Pro Actual Range Day` draws it at 1194 only, and
   `47 (Mobile) RO Range Day` never reaches it — 47's own first ruling is that
   the RO lands on the shooter events list under Home, every time, which is the
   row this part opens with. What rules it instead is `51 Shell Lab` row 01,
   Lists: "Rows with a scannable right edge, filters above, one page act. On the
   phone the act floats bottom-right over the faded list end; on the tablet it
   holds the header."

   AND THE NUMBERS ARE NOT INVENTED, because 47 draws this list's SIBLING at the
   same level: its own row 01, a phone frame of a list of events an RO is
   standing in front of. The 18px frame gutter, the 25px display heading over a
   label-role line, the unlit row with a name over a quieter sub-line and a
   right-edge cell, the rule beneath each row, and the tertiary foot sentence
   are all that frame's. The 18 is also what the folded back row above this
   column takes, so the row's label and the list share one left edge.

   THREE DEPARTURES, EACH STATED ON THE COMPONENT AS WELL: the rows take 47's 56
   floor rather than the frame's incidental 54, because 47 rules 56 "the phone
   touch floor on scoring surfaces" and these rows are the doors onto the day
   being scored; there is no filter strip and no count line, because one
   calendar day for the orgs this account staffs is the whole filter; and there
   is no page act, because nothing on this screen is an act and a floating
   button would have to invent something to press.

   ⚠️ AND A DESIGNER QUESTION SITS UNDER THE WHOLE BLOCK: does this route exist
   as a phone destination at all? It is a real, bookmarkable route today, so it
   renders something at every width. The answer cannot be a redirect — the route
   block refuses one by name — so if the ruling is no, what replaces this is an
   honest signpost screen, not a bounce.

   WHY THIS BLOCK IS 47'S AND NOT 36'S, the same split the three blocks above
   record: the tablet page column and its chamfered rows are 36's, in
   `adm-rday.css`, which declares NO WIDTH BREAKPOINT and means it. The phone
   list is 47's, so it takes 47's own name space here, and the choice between
   the two is made in Rust — `RangeDayToday` matches on the device class and
   mounts one arm or the other — which is why there is no `@media` in this file
   and no second part. The name space was checked before it was chosen: zero
   hits across `apps/web/`, `cli/` and `docs/`, against a known-positive control
   on this part's own board name space.

   ⚠️ AND IT DECLARES NOTHING THE STATE BLOCKS DECLARE. The loading treatment,
   the transport failure and the four blessed interim states are the shared ones
   in `adm-rday.css`, which carry no width geometry; the phone arm mounts them
   unchanged and this part neither restates nor overrides one of their rules.

   ⚠️ WHICH LEAVES ONE OPEN QUESTION, FLAGGED AND NOT CLOSED HERE: those state
   blocks carry 36's TABLET type scale — a 15px title over a 13px body — and
   they now paint inside a 47 frame whose own scale is 12 for a row name, 10 for
   its sub-line, 10.5 for the foot and 8.5 for a label. The states therefore
   read a step larger than everything around them. It is left alone on purpose:
   restating their type here would be this part overriding another's
   declarations for a reason nobody has ruled, and the states are blessed copy
   in a blessed treatment. Whether a phone rendition of those four blocks is
   wanted is a designer question, asked in the PR rather than answered in this
   file.

   No `border-radius` anywhere: this family's cuts are `RdChamferBox`, and these
   rows are not cut at all — 47's unlit rows are ruled, not chamfered. The lit
   one is, and it is a different row on a different screen, already in this part.

   ⚠️ NO FAMILY DECLARED ANYWHERE IN THIS BLOCK, the same call the blocks above
   record: every face here is the body role, which is the shell's already and
   which this column inherits. What IS declared is where a role departs from the
   inherited one — the weight, the stretch, the tracking and the tabular
   figures. */

/* The column. `RangeDayLayout`'s phone arm already supplied the region and the
   back row above it, so this is not a second viewport: a flex column that fills
   what it was handed, with the head and the foot pinned and only the body
   between them scrolling. */
.rd-rotoday {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

/* 47's frame opens with 4px of air above the heading, then the head sits on the
   frame gutter. `flex: none` — the heading names the screen and a screen whose
   name scrolls away is one an RO has to scroll back up to identify. */
.rd-rotoday__head {
  flex: none;
  padding: 4px 18px 10px;
}

/* 47's display role at the size its own landing takes: weight 900, stretch 62%,
   no tracking, 25px, and the tight line the frame draws. */
.rd-rotoday__h1 {
  font-weight: 900;
  font-stretch: 62%;
  text-transform: uppercase;
  letter-spacing: 0;
  font-size: 25px;
  line-height: .92;
  color: var(--rd-fg);
  margin: 0;
}

/* The date, in 47's label role — 700 / 78% / .2em at 8.5px, tertiary. Its own
   landing puts `Today` here; this screen IS today, so the line answers the next
   question instead and says which day that is. */
.rd-rotoday__when {
  margin-top: 12px;
  font-weight: 700;
  font-stretch: 78%;
  font-size: 8.5px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--rd-fg3);
}

/* The one live vertical scroller on this mount. The head and the foot are
   outside it on purpose. */
.rd-rotoday__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0 18px 8px;
}

.rd-rotoday__list {
  display: flex;
  flex-direction: column;
}

/* ⚠️ 56 IS THE FLOOR AND IT IS A RULING, NOT A ROUNDING. 47: "56px is the phone
   touch floor on scoring surfaces. 68 was a tablet ruling for a gloved thumb at
   arm's length; the phone number covers the steppers, the keypad and the board
   rows." Its own unlit list rows are drawn at 54; these are the doors onto the
   day being scored, so they take the floor and not the frame's incidental
   number. `min-height`, so a long event name that wraps grows the row past it
   rather than clipping.

   A rule under each row and none on the last, which is row 01's construction: a
   list of rows, not a stack of cards. */
.rd-rotoday__row {
  min-height: 56px;
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 0;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  text-align: left;
  cursor: pointer;
  box-sizing: border-box;
}

.rd-rotoday__row:last-child { border-bottom: 0; }

.rd-rotoday__row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-rotoday__copy {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

/* The row header at 12px, one line, ellipsised — an org may name an event
   anything and the row is a fixed track. 47's own unlit row draws 12.5; this
   part already draws a phone row name at 12 on the board, and two phone row
   families in one part reading at two sizes is a difference nobody chose. */
.rd-rotoday__name {
  font-size: 12px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The sub-line: 47's own 10px tertiary. It carries the bay, which is what an RO
   navigates a range by — the 14 Aug correction's own words, and the reason the
   bay took the slot the time used to hold. */
.rd-rotoday__sub {
  font-size: 10px;
  line-height: 1.4;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The scannable right edge row 01 asks for. Size and colour are the lab's own
   right-edge cell — 10px, secondary — and the one departure is row 04's mono
   treatment, 800 at 75% with tabular figures, because the cell leads with a
   count and a column of counts should align down the edge. Not a monospace
   family: it is Archivo, the same reading `adm-rday.css` records for the stage
   index it draws. */
.rd-rotoday__val {
  flex: none;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 10px;
  color: var(--rd-fg2);
}

/* The foot: 47's PLACEMENT and 47's treatment — the scoping sentence at the
   bottom of the column, 10.5px tertiary, `flex: none` so it does not scroll.
   The WORDS are 36's, `ScreenRODay`'s own foot, said once in the product and
   shared by both arms; this part supplies where it sits and how it reads, not
   what it says. The top rule separates it from the last row, which would
   otherwise read as another row of the list. */
.rd-rotoday__foot {
  flex: none;
  border-top: 1px solid var(--rd-line);
  padding: 12px 18px 16px;
  font-size: 10.5px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

/* ── Row 01 · the lit staffed-event row, at TABLET geometry ───────────────── */

/* ⚠️ UNDRAWN AT THIS WIDTH, AND CARRIED ANYWAY. `47 (Mobile) RO Range Day` is a
   phone document and `15 (Tablet) Events` draws no lit row, so this block is
   the tablet rendering of 47's row 01 rather than a drawing of its own. James
   ruled on 24 Aug 2026 that the RO's door to scoring is the Events list, per
   event, on the scorer assignment; a door that exists at one width and not the
   other is the missing-door defect one breakpoint along — the same argument
   `s15-tablet.css`'s own `.rd-tev__door` carries for the Range Day row.

   ⚠️ A SEVENTH DISJOINT TABLET NAME SPACE. `.rd-troev*` is declared here and
   nowhere else: `s15-tablet.css` owns `.rd-tev*` and every `kit-tablet-*` part
   owns its own, and neither side declares or overrides a selector the other
   names. So this block re-orders no existing pair and nothing in it is decided
   by source order — the same reasoning this part's banner makes for `.rd-roev*`
   over `s15.css`.

   ⚠️ NO MEDIA QUERY, like everything else in this part. The tablet list is
   entered by COMPONENT (`crate::device::DeviceClass` picks `RdTabletShell`),
   never by rule, so these classes only render inside a shell that has already
   decided which width it is.

   ⚠️ AND NO ACCENT LEFT RULE. `.rd-tev-row--on` is this list's SELECTION — a
   3px accent rule and a raised ground — and selection is a different claim from
   "this row is your job today". The lit row wears the drawn accent BORDER,
   which nothing else on this pane wears, so the two treatments cannot be read
   as each other.

   ⚠️ NO MUTED TWIN HERE EITHER — the ruling this part's banner records. An
   unassigned reader gets no row, so a modifier of this block — a `--` suffix
   on any of its names — would be a state nobody drew at either width. The
   tablet screen's own test reads this file for one, which is why the rule is
   stated here without spelling the selector it forbids. */

/* Padded and ruled like the ordinary rows around it so the lit one sits IN the
   list rather than on top of it: `.rd-tev-row`'s own 16px gutter and bottom
   rule, with the chamfer box inset in the middle of them. */
.rd-troev {
  display: block;
  padding: 8px 16px 12px;
  border-bottom: 1px solid var(--rd-line);
  text-decoration: none;
  cursor: pointer;
}

.rd-troev:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* 68px, not the phone's 56. The tablet floor is the gloved-thumb-at-arm's-length
   number `47`'s own touch ruling sets aside for tablets, and it lands under
   `.rd-tev-row`'s 78px so the lit row does not stand taller than the list. */
.rd-troev__inner {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 68px;
  padding: 13px 16px;
}

.rd-troev__copy { flex: 1; min-width: 0; }

/* The list's own row-name size (12.5px), not the phone row's 13px display cut:
   this row sits in a column of `.rd-tev-row__name`s and a different size would
   make the lit row read as a different KIND of thing rather than the same thing
   lit. */
.rd-troev__name {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .04em;
  font-size: 12.5px;
  color: var(--rd-fg);
}

/* The assignment line, `--rd-fg2` for the reason the phone's twin gives: it is
   the one line on this list about the reader's own job today, and the quiet
   rows around it take `--rd-fg3`. */
.rd-troev__sub {
  margin-top: 4px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.4;
  color: var(--rd-fg2);
}

/* The volt Run chip — 47's own pick, a FILL, so `--rd-volt` with `--rd-on-volt`
   ink. One step up from the phone's 10px because everything else on this pane
   is. */
.rd-troev__run {
  flex: none;
  padding: 10px 15px;
  background: var(--rd-volt);
  color: var(--rd-on-volt);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800; font-stretch: 78%; letter-spacing: .1em;
  font-size: 11px;
}
/* ===========================================================================
   LAB1 — the phone floating page act, and the list end it floats over.
   Drawn in `51 Shell Lab.html`, section 01 `Lists`, phone frame (`LabListsM`
   inside `LabMob`).  owner: lane-3

   ⚠️ Cited by SECTION and COMPONENT NAME, never by JSX line number — the rule
   `pages/admin/events.rs` states at the top of its own file, and here it is
   also a hard requirement: `rd-shell-lab.jsx` is NOT in
   `docs/design/kit/corpus-ledger.txt`, so a `file.jsx:line` citation into
   document 51 would fail `rangeday check citations` as an unknown corpus file.
   Reported rather than worked around: the ledger is regenerated from the
   gitignored snapshot, which this checkout does not carry.

   THE RULING, verbatim from section 01's own body copy:

     "Rows with a scannable right edge, filters above, one page act. On the
      phone the act floats bottom-right over the faded list end; on the tablet
      it holds the header."

   And the rule that makes it mandatory rather than optional, from the same
   document's lede — capability parity, ruled 21 Aug:

     "anything one device can do or read, the other can; only the presentation
      changes. Filters, acts, search, inspectors, commits and value columns
      appear on both sides of every section, and a control missing on one side
      is a defect in the contract, not a device adaptation."

   ---------------------------------------------------------------------------
   ANATOMY

     [ .rd-pgact-clear — in flow, at the end of the list: the space the two
                         fixed elements below cannot reserve for themselves ]
     [ .rd-pgact-fade  — the list end, faded, behind everything, inert ]
     [ .rd-pgact       — the act itself, bottom-right, over the fade ]

   The first two are separate because they are two drawn objects: `LabMob` takes
   `act` and `fade` as SEPARATE props, and the Lists frame is the one that
   passes both. A page that wanted the act without the fade (or the fade without
   the act) is a shape the drawing already admits. The third is ours, and its
   own comment says why.

   ---------------------------------------------------------------------------
   ⚠️ FIXED, NOT ABSOLUTE — the one deliberate divergence, recorded rather than
   transcribed.

   `LabMob` gives its content region `position: relative`, its own `overflow`
   and a viewport-height frame, then hangs the act off it at
   `position: absolute; right: 0; bottom: 16`.

   ⚠️ **THE PREMISE UNDER THIS NOTE CHANGED ON 2026-08-27, AND THE DECLARATION
   DID NOT.** What stood here was: *"Our phone Pro shell does not scroll that
   way: `.rd-prom` is `min-height: 100dvh` and the DOCUMENT scrolls, so an
   absolutely-positioned act would ride the end of the list off the bottom of
   the window and a mask on the list would fade content nobody has reached
   yet."* That is no longer true of this app — `.rd-prom` is now a bounded,
   clipped frame with `.rd-prom__body` as its one scroller (`adm0-mobile.css`
   §1, and `kit-shell.css` §0d for why), which is the arrangement `LabMob`
   draws. The sentence is quoted rather than deleted because the reasoning it
   recorded is what a reader would otherwise reconstruct wrongly from the
   `fixed` below.

   `position: fixed` STAYS, and it is now a difference in spelling rather than
   in behaviour: `.rd-prom` fills the viewport exactly, so viewport-anchored and
   frame-anchored are the same pixels. Switching to the drawn `absolute` would
   need `position: relative` on an element in another part, would put the act
   inside a scroll container's stacking context beside a `position: fixed;
   inset: 0` overlay, and would buy nothing visible.

   ⚠️ AND THE FADE HAS AN OPTION IT DID NOT HAVE. The note on `.rd-pgact-fade`
   below says the drawn mask *"belongs on a scroll container this page does not
   have"*. This page now has one. Moving the fade from an overlay onto a mask on
   the scroller would be closer to `LabMob` — and it is deliberately NOT done
   here: the mask would have to be opt-in per page (the fade is mounted by
   consumers, not by the shell), and this change is a scrolling fix, not a
   re-transcription. Flagged in the design relay and left where it is.

   `right: 18px` rather than the drawn `0`: the drawn `0` is the inside
   edge of `LabMob`'s 18px frame padding, and `.rd-prom` carries that padding
   (`padding: 6px 18px 0`, adm0-mobile.css) on an element the act is not
   inside.
   =========================================================================== */

/* The act. `background: t.volt, color: t.onVolt, tcham(10), minHeight: 52,
   display: flex, alignItems: center, padding: '0 24px',
   pdisp(800, 78, '.1em'), fontSize: 12, border: 1.5px solid t.acc`.

   ⚠️ 52px, and it is UNDER the 56px touch floor `47 (Mobile) RO Range Day`
   rules — deliberately, and reported rather than silently corrected. That floor
   is written about the RO entry rows and the phone keypad, and 51 states 52 for
   its own fixture in a document ruled later. Transcribed as drawn: a number
   nudged here to satisfy a rule written about a different object is how two
   drawings end up disagreeing through us, and which floor governs is the
   designer's to say. Flagged in the PR that added this. */
.rd-pgact {
  position: fixed;
  right: 18px;
  bottom: 16px;
  /* THE ACT LAYER, `kit-shell.css` §0e. Was a bare `2`. The number is the
     kit's now because the ordering is a product rule — *"acts are always above
     list fades"*, James 2026-08-28 — and not this part's local arrangement. */
  z-index: var(--rd-z-act);
  min-height: 52px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  padding: 0 24px;
  background: var(--rd-volt);
  color: var(--rd-on-volt);
  border: 1.5px solid var(--rd-acc);
  /* `tcham(10)` — the same clip-path polygon `kit-row.css` transcribes for
     `r: 10`, written as geometry rather than borrowed, because this element is
     not in that family and reaching into another part's class to get a corner
     is the drift the corpus ledger exists to catch.

     ⚠️ THE CLIP EATS THE DROP SHADOW, IN THE DRAWN FRAME TOO. `LabMob` sets
     `boxShadow: '0 10px 28px rgba(0,0,0,.6)'` on the very element it clips,
     and a clip-path removes what falls outside the path — the shadow included.
     So the shadow is not transcribed: writing a declaration that renders
     nothing would be a claim we draw a shadow. Recorded here instead, because
     the absence is the interesting half. */
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%);
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .1em;
  text-transform: uppercase;
  font-size: 12px;
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
}

/* ⚠️ AN INSET RING, for `kit-row.css`'s reason one part over: a `clip-path`
   clips an `outline` away, so the drawn focus ring has to be painted inside
   the path or it does not exist. */
.rd-pgact:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--rd-on-volt);
}

/* The faded list end. `LabMob`'s `fade` mask, stated as an overlay:

     mask: linear-gradient(to bottom,
             black                 calc(100% - 150px),
             rgba(0,0,0,.35)       calc(100% - 70px),
             transparent           calc(100% - 12px))

   Read from the BOTTOM edge, that mask leaves the content fully lit until
   150px up, 35% lit at 70px and gone by 12px. An overlay of the app ground at
   0% / 65% / 100% over the same three distances composites to the same
   picture, and it is the form available to us: the mask belongs on a scroll
   container, which this page did not have when the overlay was written and now
   does. See the FIXED note above for why the overlay stays and what the
   alternative now is.

   Inert by construction — the act sits over it and rows sit under it, and
   neither may lose a tap to a gradient. */
.rd-pgact-fade {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  /* THE FADE LAYER, `kit-shell.css` §0e. Was a bare `1`. Restated as the token
     so this pair cannot drift out of order, and so the one other fade in the
     pool (`.rd-prom-fade`, `adm-mob.css` §3.5) is provably on the same rung. */
  z-index: var(--rd-z-fade);
  height: 150px;
  pointer-events: none;
  background: linear-gradient(
    to top,
    var(--rd-app-bg) 12px,
    color-mix(in srgb, var(--rd-app-bg) 65%, transparent) 70px,
    transparent 150px
  );
}

/* ⚠️ THE CLEARANCE THE DRAWING DOES NOT NEED AND WE DO — the second, and last,
   consequence of `fixed` (see the divergence note in the banner).

   `LabMob` scrolls its list INSIDE a viewport-height frame, so the last row can
   always be brought out from under the act. Our list is in the document flow
   under a `fixed` pair, and a `fixed` element reserves no space: without this,
   the final event row sits permanently behind the act with no scroll position
   that clears it. An obscured last row is a defect, not fidelity.

   ⚠️ **AN ELEMENT, NOT A `padding-bottom`.** The padding would have to go on
   `.rd-pro-page` or `.rd-adm-ev`, both declared in parts this one does not own,
   and reaching into another part's class to make room for our own fixture is
   the drift the corpus ledger exists to catch. A spacer this part declares and
   this part's consumer mounts answers to nobody else.

   The height is the act's own drawn numbers and no new ones: `minHeight: 52`
   plus its `bottom: 16`, plus that offset again as the gap above it. */
.rd-pgact-clear {
  flex: none;
  height: 84px;
}
/* ==== S55 · 55 (Mobile) Shoot No Shoot — the Games band's first surface · owner: lane-drills ==== */
/*
   The three screens of `55 (Mobile) Shoot No Shoot`, and the first part off
   the Games band that `48 Kit Rules` opens.

   Spec: `docs/design/kit/SNS-55.md`.

   NAME SPACE: `.rd-sns*` — three roots (`.rd-sns` on the start screen,
   `.rd-sns-run` mid-run, `.rd-sns-over` on the receipt) and their elements.
   No other part declares one of these names and this part declares none of
   theirs, so its position in `order.txt` re-orders no existing pair and
   nothing here is decided by source order.

   ⚠️ NO WIDTH `@media`, EVER — AND THE PART NOW DRAWS TWO DEVICES. This
   banner used to say the absence was the band's phone-only product decision.
   `55 (Tablet) Shoot No Shoot` (2026-08-25) drew the twin the mobile document
   deferred, and its own prediction was right — "the buttons likely leave the
   bottom for the side rails, which is a new ruling, not a scale-up". §T below
   is that ruling built.

   The absence survives the twin because it was never really about phones. THE
   DEVICE IS CHOSEN IN RUST, by `use_device_class()`, once per screen and above
   the branch; a width query here would be a SECOND answer to the same
   question, invisible from the page and disagreeing with it at exactly the
   widths nobody tests. And an `orientation` query would contradict a ruling:
   James ruled 2026-08-18 that a portrait tablet classifies as
   `DeviceClass::Phone`, which `device.rs`'s `tablet_query()` implements with
   its `and (orientation: landscape)` clause.

   `the_only_media_query_is_a_readers_preference_the_device_branch_is_rust_side`
   pins the ABSENCE — it is the renamed and re-argued
   `phone_only_by_construction_no_width_media_query` — because an absence
   nothing asserts is undone by the first breakpoint someone adds without
   arguing for it. Its assertion became an EXACT two-name allow-list on
   2026-08-27 when the key caps landed; a width, a `max-width`, an `orientation`
   or any other prelude is still red.

   The `@media` preludes this part carries are TWO, both allowed by name and
   neither a breakpoint:

   1. `prefers-reduced-motion`, at the foot. A reader's preference, not a
      measurement of their device: it names no width and it cannot bring a
      tablet layout in through the side door. See the block's own banner for why
      it is a blanket rather than a list of names.
   2. `(hover: hover) and (pointer: fine)`, twice. It asks what the reader is
      holding rather than how wide it is — the kit's standing shape for exactly
      that (`kit-tablet-rail.css`: "they key on the input, not the width") — and
      `the_hover_gate_carries_the_key_caps_and_the_desktop_acts_and_nothing_else`
      holds BOTH blocks to an exact selector list, so it cannot become the door
      a tablet layout walks in through.

      The first block paints the undrawn desktop key caps (James, 2026-08-27).
      The second, in §T, brings the two acts together under the image on a
      desktop (James, 2026-08-28) — the ruling names the detection itself:
      "detection = input capability, not width". Two blocks and not one because
      they sit in the two sections they belong to, phone and §T; a single
      merged block would put tablet-only selectors above §T's own banner.

   ⚠️ NO `border-radius`. Every corner on these screens is a chamfer, drawn by
   `RdChamferBox`'s two-layer clip — the card, the thumbs and the START tile
   all compose it. A radius here would be a second, rounder corner vocabulary
   on the one screen whose controls are the largest in the app.

   ⚠️ GREEN IS SPENT, NEVER MINTED — M2. `48`'s band ruling puts the
   celebration green (`t.ok`) on a new personal best, and it names it "the Auth
   success token, no new colour". So every green below reads `--rd-ok` /
   `--rd-on-ok` out of `kit-ok.css`; this part declares no colour value of its
   own. Three surfaces spend it and they are all on the receipt, all conditional
   on a best: `.rd-sns-over__flood`, `.rd-sns-over__hero--best` and the NEW BEST
   tag's ink.

   ⚠️ AND THE TAG IS THE FIRST REAL CONSUMER OF `--rd-on-ok`. `kit-ok.css` minted
   the pair together and shipped only the fill with a surface: its own banner
   records that no `TBtn` kind `ok` was built, because the one drawn call site is
   an assessment screen this app does not ship. `.rd-sns-over__best-tag-face` is
   the first place in the app where the companion INK is painted on top of
   `Fill::Ok` — which also makes it the first place the light theme's inverted
   rule (white on the deep green, where volt takes black in both themes) is
   actually exercised.

   ⚠️ NO GREEN ANYWHERE ELSE. The start screen's personal-best card is
   deliberately NOT green: `48` spends the colour on the MOMENT a best is set,
   and a permanently green card would spend a status colour on chrome, which is
   the one thing `kit-ok.css` rules out in so many words.

   ⚠️ THE TWO DRAWN WIDTHS ARE RESOLVED — 2026-08-27. 55 draws the thumb words
   at font-stretch 66% and the run-over headline at 64%; the design reply
   (`~/trex-arms/uploads/design-reply-2026-08-27.md` §C-45.1) rules the 64-66
   small-display cluster into ONE named role at 66 — "the existing Tab Title
   width, adopted for the drawer name and doc 55's thumb words (the failure
   headline's 64 is drift to 66)". Both roles below are now 66%, and 66 is in
   `ALLOWED_STRETCH` (cli/src/css.rs) with that citation. 64 is retired, not
   separately allowed.
*/

/* ── Shared tokens ───────────────────────────────────────────────────────── */
/*
   `48`, Games band: "Two controls span the bottom edge at 96px each — double
   the 48dp floor, because the tap happens at a half-second window with no time
   to aim the thumb. Gap 10, chamfer 14, home-indicator clearance 18."

   The 96 is the PAINTED height — the outer chamfer layer — so the inner face
   below it carries 93 and the 1.5px border either side makes up the rest.
*/
.rd-sns,
.rd-sns-run,
.rd-sns-over {
  --rd-sns-thumb-h: 96px;
  --rd-sns-thumb-face-h: 93px;
  --rd-sns-thumb-gap: 10px;
  --rd-sns-safe-foot: 18px;
  --rd-sns-thumb-word: 26px;
  /* The undrawn key cap — see "The key caps" below. One size on both devices:
     it is a caption naming a key, not a control that scales with the thumb. */
  --rd-sns-thumb-key: 12px;
  --rd-sns-thumb-key-gap: 6px;
  --rd-sns-drain-h: 4px;
  --rd-sns-start-h: 118px;
  --rd-sns-start-word: 44px;
  --rd-sns-hero: 84px;
  --rd-sns-headline: 28px;
  /* The green flood's envelope, and the ONE place its length is spelled. Two
     elements run `rd-sns-flood` — the ground and the hero number — and before
     these tokens existed the ground carried the figure in its own shorthand
     while the number carried no length at all. `SNS_FLOOD_BEAT_MS` is asserted
     against `--rd-sns-wash-dur`, so the Rust timer names this declaration.
     See "The wash's ONE clock" below. */
  --rd-sns-wash-dur: 1100ms;
  --rd-sns-wash-ease: ease-out;
  /* The values the tablet document REDRAWS rather than inherits. Same idiom as
     the eight above and for the reason §T states: where the tablet only resizes
     the phone's own element, the value is a custom property and the `--tab`
     root re-declares it, so ONE rule paints the element on both devices. */
  --rd-sns-ready: 10px;
  --rd-sns-loading-count: 11px;
  --rd-sns-start-note: 8.5px;
  --rd-sns-card-pad: 13px 15px;
  --rd-sns-best-note: 9.5px;
  --rd-sns-best-note-gap: 10px;
  --rd-sns-clock: 15px;
  --rd-sns-best-tag-gap: 12px;
  --rd-sns-hero-gap: 18px;
  --rd-sns-hero-label-gap: 7px;
  --rd-sns-stats-gap: 16px;
  --rd-sns-commerce-gap: 14px;
  --rd-sns-failed-card: 88px;
  --rd-sns-failed-link: 10px;
  --rd-sns-failed-link-gap: 8px;
  --rd-sns-note: 10px;
  --rd-sns-note-gap: 12px;
  display: flex;
  flex: 1;
  min-height: 0;
  flex-direction: column;
}

/* ── The start screen ────────────────────────────────────────────────────── */

.rd-sns {
  padding-top: 6px;
}

.rd-sns__lede {
  margin-top: 7px;
}

/* The drawn `<div style={{ flex: 1, minHeight: 12 }} />` — the start screen
   pushes its act to the bottom edge. The middle is empty on a phone that has
   never set a best; once one exists the card below sits between this and the
   foot. */
.rd-sns__spacer {
  flex: 1;
  min-height: 12px;
}

.rd-sns__foot {
  flex: none;
  padding-bottom: 12px;
}

.rd-sns__ready {
  font-size: var(--rd-sns-ready);
  line-height: 1.5;
  color: var(--rd-fg3);
  margin-bottom: 10px;
}

/* ── The personal-best card ──────────────────────────────────────────────── */
/*
   The receipt's stats card, re-spent on the one screen a best is drawn on. Same
   chamfer 11, same `Stroke::Line` on `Fill::Surface`, same three-column grid —
   the page composes `RdChamferBox` and `RdStat` for both, so this part only has
   to say where the card sits and how tight the grid is.

   ⚠️ NO GREEN. See the head banner: `--rd-ok` is the moment, not the record.
   ⚠️ AND NO HEADING. Both `55` documents draw the box with nothing above it —
   "Personal best" is the first figure's own label — so the grid opens flush to
   the chamfer's padding and carries no top gap to clear a title with.
*/
.rd-sns__best {
  flex: none;
  margin-bottom: 14px;
}

.rd-sns__best-stats {
  padding: var(--rd-sns-card-pad);
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 12px;
}

.rd-sns__best-note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: var(--rd-sns-best-note);
  line-height: 1.5;
  color: var(--rd-fg3);
  margin-top: var(--rd-sns-best-note-gap);
  text-wrap: pretty;
}

/* ── The loading line ────────────────────────────────────────────────────── */
/*
   `43 Loading States`'s counting vocabulary, as 55 draws it above the act: the
   label left, a mono count right, a 3px track under both. The track is drawn
   at 3 rather than the drain's 4 — this is a progress reading, not the clock.

   ⚠️ The bar's width is an inline style the page rewrites, like the drain's,
   because it is a VALUE rather than a state. See the reduced-motion banner.
*/
.rd-sns__loading {
  margin-bottom: 10px;
}

.rd-sns__loading-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}

.rd-sns__loading-count {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--rd-sns-loading-count);
  color: var(--rd-fg2);
}

.rd-sns__loading-track {
  height: 3px;
  background: var(--rd-chip);
  margin-top: 6px;
}

.rd-sns__loading-fill {
  height: 3px;
  background: var(--rd-acc);
}

/* The refused act: 55 draws it as the same 118px chamfer-16 tile in surf2 with
   fg3 type — the shape of START, visibly spent. */
.rd-sns__start--off {
  height: var(--rd-sns-start-h);
  display: flex;
  align-items: center;
  justify-content: center;
}

.rd-sns__start-word--off {
  color: var(--rd-fg3);
}

/* The START tile is a button wrapping `RdChamferBox`: the chamfer is the kit's
   and the hit target is the whole tile. */
.rd-sns__start-act {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: inherit;
}

.rd-sns__start {
  height: var(--rd-sns-start-h);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
}

.rd-sns__start-word {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 900;
  font-stretch: 62%;
  letter-spacing: .02em;
  font-size: var(--rd-sns-start-word);
  line-height: .9;
  color: var(--rd-on-volt);
}

.rd-sns__start-note {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .14em;
  font-size: var(--rd-sns-start-note);
  color: var(--rd-on-volt);
  opacity: .65;
}

/* ── The run screen ──────────────────────────────────────────────────────── */

.rd-sns-run {
  padding-top: 10px;
  /* The screen is the game: it takes keyboard focus so the desktop keys work,
     and never draws a focus ring the shooter would read as a control. */
  outline: none;
}

.rd-sns-run__head {
  flex: none;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

/* "A small mono window value sits top-right for whoever wants the figure." */
.rd-sns-run__clock {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--rd-sns-clock);
  color: var(--rd-fg2);
}

/* "danger for the last third — red stays an alarm, and running out of time is
   one." */
.rd-sns-run__clock--urgent {
  color: var(--rd-danger);
}

.rd-sns-run__stage {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  overflow: hidden;
}

/* ── The target card ─────────────────────────────────────────────────────── */
/*
   `48`, Games band: "Product photographs render as a 4:5 card — chamfer 14,
   ctrl stroke, surf ground, full colour. The frame is what makes it read as a
   target that moves rather than a photo the app is showing."
*/
.rd-sns__photo {
  height: auto;
  aspect-ratio: 4 / 5;
  position: relative;
  overflow: hidden;
}

/*
   The decoded photograph. Absolutely positioned inside the frame's own
   `position: relative` so `object-fit: cover` fills the 4:5 whatever the
   catalogue's aspect ratio is — the frame is the drawn geometry and the
   photograph yields to it, never the other way round.

   `user-select: none` and the page's `draggable="false"` are the same latch:
   a photograph in a frame is one gesture away from being dragged out. Neither
   is a protection — see `libs/core/src/games/enc.rs`'s own threat model.
*/
.rd-sns__photo-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  user-select: none;
  -webkit-user-select: none;
}

/* ── The entry ───────────────────────────────────────────────────────────── */
/*
   55 draws a target entering from the right as a still: the card offset and
   slightly transparent, settling into place. The direction carries no meaning
   yet (designer question 5).

   ⚠️ A `transition` AND NOT A `@keyframes` ANIMATION, and the reason is
   mechanical rather than stylistic. The card is ONE element that outlives ten
   targets, and a CSS animation replays only when its element is re-created or
   its `animation-name` changes — so a keyframe slide would play on the first
   target and never again. The page instead drops `--entering` one repaint tick
   after each target goes up, and a property that changes transitions every
   time by construction.

   Reduced motion is handled twice, deliberately: the blanket at the foot stops
   the `transition`, and the block there ALSO neutralises the offset — without
   that second half a reader with motion off would still see the card jump from
   34% to rest with no tween, which is a worse artefact than the animation.
*/
.rd-sns__slide {
  transform: none;
  opacity: 1;
  transition: transform 160ms ease-out, opacity 160ms ease-out;
}

.rd-sns__slide--entering {
  transform: translateX(34%);
  opacity: .9;
}

/* ── The drain ───────────────────────────────────────────────────────────── */
/*
   "The window is a bar on the card, not a number. Dry Fire's 'one number, big
   enough to read from a bench' is for a phone you glance at; here the eyes are
   on the photograph and the drain is peripheral vision's job."
*/
.rd-sns__drain {
  height: var(--rd-sns-drain-h);
  background: var(--rd-chip);
  margin-top: 8px;
}

.rd-sns__drain-fill {
  height: var(--rd-sns-drain-h);
  background: var(--rd-acc);
}

.rd-sns__drain-fill--urgent {
  background: var(--rd-danger);
}

/* ── The two-thumb split ─────────────────────────────────────────────────── */
/*
   `48`, ruled band-wide: 96px painted, act RIGHT, restraint LEFT, gap 10,
   chamfer 14, home-indicator clearance 18. SHOOT is second in source order
   because it is on the right — see the page's own test.
*/
.rd-sns__thumbs {
  flex: none;
  display: flex;
  gap: var(--rd-sns-thumb-gap);
  padding-bottom: max(var(--rd-sns-safe-foot), env(safe-area-inset-bottom));
}

/* ⚠️ AUDITED 2026-08-29 AND FOUND CORRECT — recorded because it was REPORTED.
   James named HOLD and SHOOT in the NTOA rehearsal ("only registers on the
   text"), and the geometry here is already right: the `<button>` is the outer
   element, `RdChamferBox` is its child, and `min-height` below is the 96px
   painted box, three px taller than the face inside it. Nothing to re-size.

   The tap was being eaten one level up, by the browser: with no `touch-action`
   the press was a double-tap-zoom candidate for ~300ms, and a glove that moved
   a pixel in that window turned it into a pan with no click at all — which is
   why a dead-centre tap on the word worked and an edge tap did not. The fix is
   `touch-action: manipulation` in `kit-hit.css`, which covers this control and
   `.rd-sns__start-act` beside it. DO NOT "fix" this rule; there is nothing here
   to fix, and a well-meant re-size would move the drawn 96. */
.rd-sns__thumb {
  flex: 1;
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  font: inherit;
  color: inherit;
  /* The painted minimum. Doubling the 48dp floor is the ruling, not a
     preference: the tap happens inside a half-second window. */
  min-height: var(--rd-sns-thumb-h);
}

.rd-sns__thumb-face {
  height: var(--rd-sns-thumb-face-h);
  display: flex;
  align-items: center;
  justify-content: center;
}

.rd-sns__thumb-word {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 900;
  font-stretch: 66%;
  letter-spacing: .06em;
  font-size: var(--rd-sns-thumb-word);
  color: var(--rd-fg);
}

.rd-sns__thumb-word--on-volt {
  color: var(--rd-on-volt);
}

/* ── The key caps ────────────────────────────────────────────────────────── */
/*
   ⚠️ UNDRAWN — INVENTED COPY, RULED BY JAMES 2026-08-27: "desktop users playing
   Shoot / No-Shoot should get F and J so they do not have to move the mouse to
   the buttons; the buttons must still work." `55` names no key anywhere and the
   band rules no affordance, so the cap is deliberately the smallest thing that
   can name one: ONE mono letter under each control, muted ink, no box and no
   border. Left is F, right is J — home-row index fingers in the order the two
   controls appear on screen. `pages/shoot_no_shoot.rs`'s `key_action` is the
   other end of the same two facts.

   ⚠️ THE GATE IS `(hover: hover) and (pointer: fine)` AND IT IS NOT A
   BREAKPOINT. This part's banner refuses width, orientation and every other
   measurement of the DEVICE, and that refusal stands: the layout is still
   chosen in Rust by `use_device_class()`, once per screen. What this asks is a
   different question — does this reader have a POINTING DEVICE AND HOVER, which
   in practice means a keyboard — and it is the kit's own standing shape for
   exactly that (`kit-tablet-rail.css`: "they key on the input, not the width").
   A wide phone in landscape gets nothing; a small desktop window gets the caps.
   `the_only_media_query_is_a_readers_preference_the_device_branch_is_rust_side`
   allows this ONE prelude, by name, and the sibling test holds the block down
   to the cap alone.

   The keys work everywhere regardless — the handler is not gated on anything.
   This block only decides where the screen SAYS SO.
*/
.rd-sns__thumb-key {
  /* Paints only under the gate below. Every touch device renders exactly what
     it rendered before this landed. */
  display: none;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--rd-sns-thumb-key);
  line-height: 1;
  letter-spacing: .08em;
  text-align: center;
  color: var(--rd-fg2);
}

@media (hover: hover) and (pointer: fine) {
  /* The control becomes a column ONLY here, and only because it now has a
     second child. Off the gate the cap is `display: none`, the button keeps its
     one child and its box is untouched. */
  .rd-sns__thumb {
    display: flex;
    flex-direction: column;
    gap: var(--rd-sns-thumb-key-gap);
  }

  /* The same column, for the START and RUN AGAIN caps — James, 2026-08-29.
     `.rd-sns__key-stack` has NO ungated rule at all: off the gate it is a plain
     block wrapper around the one button it always wrapped, and the cap inside
     it is `display: none`, so a touch device is unchanged in both its box and
     its flow. The gap is the thumb's own token, so all three caps sit the same
     distance under their controls. */
  .rd-sns__key-stack {
    display: flex;
    flex-direction: column;
    gap: var(--rd-sns-thumb-key-gap);
  }

  .rd-sns__thumb-key {
    display: block;
  }
}

/* ── The run-over screen ─────────────────────────────────────────────────── */

.rd-sns-over {
  padding-top: 4px;
}

/* ── The green flood ─────────────────────────────────────────────────────── */
/*
   `48`, Games band: "A new personal best washes the screen t.ok … for a beat;
   THE RECEIPT PUNCHES THROUGH IN INK and it settles."

   Both halves of that sentence are mechanical here.

   THE WASH is one full-bleed fixed element, in the markup only when the run is a
   best (`pages/shoot_no_shoot.rs` renders it under `if new_best`), painted
   `--rd-ok` and animated `rd-sns-flood`: in over ~160ms, HELD for a beat, then
   out. Its RESTING state is `opacity: 0` — `--rd-sns-wash-t` at its registered
   initial value — which is what makes the reduced-motion gate at the foot a
   complete answer: stopping the animation leaves an invisible element rather
   than a green screen that never lifts.

   ⚠️ THE COLOUR IS `var(--rd-ok)` AND NOTHING ABOUT THE WASH IS THEME-KEYED.
   `kit-ok.css` declares the pair in BOTH of the token layer's two blocks —
   `:root, [data-theme="dark"]` at `#4EB56D` and `[data-theme="light"]` at
   `#2E6B3C` — and there is no `@media (prefers-color-scheme)` anywhere in this
   app (`theme.rs` stamps `data-theme` on the document element instead), so one
   rule serves both themes and there is no light-theme path to leave unbuilt.
   `the_wash_is_the_theme_token_in_both_themes` asserts that, because the light
   half is the half nobody looks at.

   THE PUNCH-THROUGH is the two rules' stacking. Every direct child of the
   receipt is positioned at `z-index: 1`; the flood is the one exception at 0. So
   the wash covers the shell's ground and the receipt's own ink sits on top of it
   for the whole beat, which is the drawn frame — not a green curtain the
   shooter waits out.

   `position: relative` on the children changes no geometry: they are flex items
   either way and none of them is offset.

   ⚠️ `pointer-events: none` — a full-bleed overlay that swallowed the first tap
   on "Run Again" would make the celebration cost a press.

   ⚠️ **1100ms IS ALSO A RUST CONSTANT, AND A TEST HOLDS THE TWO EQUAL.** It is
   spelled once, as `--rd-sns-wash-dur` below. The wash has two halves with two
   owners: this animation paints the GROUND and ends itself, and
   `shoot_no_shoot.rs` adds the `--flood` class that carries the INK and has to
   take it back. If that token and `SNS_FLOOD_BEAT_MS` drift, the
   receipt is left either re-inked over the page's ordinary ground or washed
   green under its ordinary ink, and neither state recovers without a
   navigation. `the_wash_and_its_ink_end_together` reads this rule and asserts
   the literal against the constant, so changing one names the other.
*/
.rd-sns-over > * {
  position: relative;
  z-index: 1;
}

/* ── The wash's ONE clock ─────────────────────────────────────────────────── */
/*
   ⚠️ **THE GROUND AND THE NUMBER RUN OFF THE SAME KEYFRAME, AND BEFORE
   2026-08-29 THEY DID NOT.** James, reading a round end: *"the screen glows
   green then settles to black, but the 'targets cleared' number SNAPS from ink
   to green instead of fading with the ground"*. The ground was a `@keyframes`
   on `opacity` and the number was a plain `color` on a class the page adds and
   removes — a tween beside a step, sharing nothing but a hope that 1100ms
   appeared in both places.

   That split has a second face, and it is the one that reads as a LIGHT-THEME
   bug. The class lands at t=0 and the ground needs 165ms to arrive, so for the
   first six frames the receipt is already re-inked `--rd-on-ok` over the page's
   own ground — `#FFFFFF` on paper in the light theme. The screen blanks and
   then is green, which is *"the green wash never fades in"* as an eye reports
   it. One cause, both complaints.

   THE FIX IS THAT THE PROGRESS IS THE ANIMATED THING. `rd-sns-flood` no longer
   animates `opacity`; it animates `--rd-sns-wash-t`, a registered `<number>`
   from 0 to .9 and back on the drawn envelope. The ground spends it as its
   `opacity` and the number spends it as a `color-mix` position, so the two can
   only ever be at the same point of the same curve — there is no second
   duration, no second easing and no second set of stops to keep in step.

   ⚠️ `@property` IS REQUIRED, not decoration. An UNregistered custom property
   has no type, so a keyframe on it interpolates DISCRETELY — it would flip at
   50% and the number would snap again, in a way no test that reads the sheet
   could see. `syntax: "<number>"` is what makes it tween. `inherits: false`
   keeps it a per-element clock: the ground and the number are siblings and each
   runs its own copy of the one animation, which is what the shared NAME buys.

   ⚠️ THE DURATION AND THE EASING ARE TOKENS, DECLARED ONCE — in the part's
   token block at the head, with every other `--rd-sns-*` literal. Two
   `animation` shorthands naming the same keyframes with hand-copied numbers is
   the drift this section was written to end. `--rd-sns-wash-dur` is also the value
   `SNS_FLOOD_BEAT_MS` is asserted against, so the Rust timer still names the
   sheet — see `the_wash_and_its_ink_end_together`.

   ⚠️ THE INITIAL VALUE IS 0 AND THAT IS THE REDUCED-MOTION ANSWER. With
   `animation: none` from the blanket at the foot, `--rd-sns-wash-t` is its
   registered 0: the ground computes `opacity: 0` and the number computes the
   mix at 0%, which is `var(--rd-ok)` — `48`'s settled frame, green hero and no
   wash, arrived at by the tokens rather than by restating a colour.
*/
@property --rd-sns-wash-t {
  syntax: "<number>";
  inherits: false;
  initial-value: 0;
}

.rd-sns-over__flood {
  position: fixed;
  inset: 0;
  z-index: 0;
  background: var(--rd-ok);
  opacity: var(--rd-sns-wash-t, 0);
  pointer-events: none;
  animation: rd-sns-flood var(--rd-sns-wash-dur) var(--rd-sns-wash-ease) 1 both;
}

@keyframes rd-sns-flood {
  0% {
    --rd-sns-wash-t: 0;
  }

  15% {
    --rd-sns-wash-t: .9;
  }

  55% {
    --rd-sns-wash-t: .9;
  }

  100% {
    --rd-sns-wash-t: 0;
  }
}

/* ── The beat's INK ──────────────────────────────────────────────────────── */
/*
   The rule above is the wash's GROUND. This is its INK, and the two together
   are the drawn frame.

   `55 (Mobile) Shoot No Shoot` and `55 (Tablet) Shoot No Shoot` both draw the
   mid-wash as the receipt REDRAWN on the ok ground: near-black copy, the NEW
   BEST tag inverted, the stats card down to hairlines. `48 Kit Rules` words it
   *"the receipt punches through in ink and it settles."* Green stacked behind
   copy that kept its ordinary ink is that sentence read as z-order alone, and
   in the dark theme it renders near-white on green — the drawing backwards.

   ⚠️ A MODIFIER, NOT A SECOND LAYOUT. The drawings give the wash its own still
   and say why: *"the wash is a moment, not a layout"* — an authoring reason,
   not a ruling that the receipt loses its back row and its acts for a beat. So
   nothing here removes an element; every declaration below is a colour.

   ⚠️ IT REBINDS THE THEME ROLES FOR THE SUBTREE, AND THAT IS THE POINT. The
   receipt's ink is not all this part's to set: the figures inside the card are
   `RdStat`, which spends `--rd-fg`, `--rd-acc` and `--rd-fg3` in
   `_legacy-kit-components-and-screens.css`, and the card's hairline is the
   chamfer substrate's `--rd-line`. A page-side rule cannot reach any of them
   without restating kit geometry it does not own. Rebinding the ROLES on the
   root re-inks all of them at once and leaves every one of those colours spent
   in exactly the rule that already spends it.

   ⚠️ NO LITERAL. The ink is `--rd-on-ok`, the token `kit-ok.css` declares for
   this one question, and it inverts between themes for the reason the tag's
   rule below already records — a theme-invariant near-black would be
   unreadable on the light theme's deep `--rd-ok`.

   ⚠️ `--rd-ok` IS DELIBERATELY NOT REBOUND: `.rd-sns-over__flood` is a CHILD of
   this root and paints itself with it.

   ⚠️ `--rd-sns-beat-ink` IS THE GATE, AND IT IS ONE PROPERTY. Every declaration
   in this section reads it. The reduced-motion block at the foot of the part
   sets it to `initial`, which makes it *guaranteed-invalid*: a `var()` that
   carries a fallback takes the fallback, and a custom property whose whole
   value is that one `var()` becomes invalid at computed-value time and INHERITS
   the theme's own. One line at the foot returns the settled frame for the whole
   modifier, so the settled values are never written twice to drift apart.
*/
.rd-sns-over--flood {
  --rd-sns-beat-ink: var(--rd-on-ok);
  --rd-sns-beat-ground: var(--rd-ok);
  --rd-fg: var(--rd-sns-beat-ink);
  --rd-fg2: var(--rd-sns-beat-ink);
  --rd-fg3: var(--rd-sns-beat-ink);
  --rd-acc: var(--rd-sns-beat-ink);
  --rd-line: var(--rd-sns-beat-ink);
  --rd-surf: var(--rd-sns-beat-ground);
}

/* The hero is `--rd-ok` on a best, which for the beat would be green on green.
   It takes the ink instead. The drawings' larger mid-wash hero is NOT built: a
   size change on the number would reflow the receipt under the wash, and the
   wash is a moment.

   ⚠️ **AND IT CROSSFADES, ON THE GROUND'S OWN CLOCK.** James ruled 2026-08-29
   that the number must fade with the ground rather than step to it. So this
   rule runs the SAME `rd-sns-flood` off the SAME `--rd-sns-wash-*` tokens and
   spends the progress as a `color-mix` position: at t=0 the mix is 0% ink and
   the colour is `var(--rd-ok)` — the settled promoted hero, character for
   character with the rule below — and at the wash's .9 plateau it is 90% ink,
   which is the ground's own opacity at the same instant. The two are the same
   curve by construction, and the number is back at `--rd-ok` before the page
   removes the class, so nothing steps at either end.

   ⚠️ **THE `var()` IS UNCHANGED, FALLBACK INCLUDED, AND THAT IS DELIBERATE.**
   The obvious edit was to re-point the fallback at `--rd-on-ok`, on the reading
   that the mix's ink end should name the ink. It must not, for two reasons that
   agree.

   The first is that it would be unobservable. The fallback is reached in
   exactly one situation — `--rd-sns-beat-ink` guaranteed-invalid — and there is
   exactly one rule in the sheet that does that: the reduced-motion gate, which
   sets it to `initial`. That same gate stops the animation, so
   `--rd-sns-wash-t` is at its registered 0 and the ink end of this mix is
   weighted 0%. Whatever the fallback names cannot be seen.

   The second is the one that decides it. `--rd-on-ok` here would be a FOURTH
   consumer of that token in this part, and
   `the_new_best_tag_is_the_first_consumer_of_the_ok_ink` holds the census at
   three ENUMERATED consumers precisely so that a copy of the ink cannot spread
   by habit. A fourth spelling that changes nothing on screen is the worst kind
   to add: it costs the pin its meaning and buys no pixel. The gate property is
   already the single reach into the beat's ink, so the crossfade reads it and
   adds nothing. Ruled with the crossfade, 2026-08-29. */
.rd-sns-over--flood .rd-sns-over__hero {
  color: color-mix(in srgb, var(--rd-sns-beat-ink, var(--rd-ok)) calc(var(--rd-sns-wash-t, 0) * 100%), var(--rd-ok));
  animation: rd-sns-flood var(--rd-sns-wash-dur) var(--rd-sns-wash-ease) 1 both;
}

/* The tag INVERTS — both documents draw it ink-filled with the ok ground as its
   letters, the exact reverse of the settled face below. Two classes, so it
   beats `kit-ok.css`'s single-class `.rd-box--fill-ok` on specificity rather
   than on which part the assembler appends last. */
.rd-sns-over--flood .rd-sns-over__best-tag-face {
  background: var(--rd-sns-beat-ink, var(--rd-ok));
  color: var(--rd-sns-beat-ground, var(--rd-on-ok));
}

/* ── The NEW BEST tag ────────────────────────────────────────────────────── */
/*
   `Fill::Ok` under the page's `RdChamferBox` at chamfer 8 — the fill is
   `kit-ok.css`'s `.rd-box--fill-ok` and this rule only supplies the INK and the
   type role. `--rd-on-ok` inverts between themes (black on the dark theme's
   lighter green, white on the light theme's deep one), which is why the ink is
   a token rather than a `#000` that would be unreadable in one of them.
*/
.rd-sns-over__best-tag {
  align-self: flex-start;
  margin-top: var(--rd-sns-best-tag-gap);
}

.rd-sns-over__best-tag-face {
  padding: 5px 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .14em;
  font-size: 11px;
  color: var(--rd-on-ok);
}

.rd-sns-over__eyebrow {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .16em;
  font-size: 10px;
  color: var(--rd-fg3);
}

/* "The failure reason is the HEADLINE, not a foot line: the run ended because
   of it, and naming it plainly is the training." Drawn at font-stretch 64%;
   the 27 Aug design reply (§C-45.1) retires 64 into the 66 small-display role
   — see the head banner and `docs/design/kit/SNS-55.md` §5. */
.rd-sns-over__headline {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 900;
  font-stretch: 66%;
  letter-spacing: .01em;
  font-size: var(--rd-sns-headline);
  line-height: .95;
  color: var(--rd-fg);
  margin-top: 7px;
}

/* The hero is targets cleared — "the number the whole run was for". Accent
   normally; `--rd-ok` when the run promoted the best, which is the half of the
   celebration a reader with reduced motion still gets. */
.rd-sns-over__hero {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--rd-sns-hero);
  line-height: .85;
  letter-spacing: -.03em;
  color: var(--rd-acc);
  margin-top: var(--rd-sns-hero-gap);
}

.rd-sns-over__hero--best {
  color: var(--rd-ok);
}

.rd-sns-over__hero-label {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .14em;
  font-size: 11px;
  color: var(--rd-fg2);
  margin-top: var(--rd-sns-hero-label-gap);
}

.rd-sns-over__stats {
  padding: var(--rd-sns-card-pad);
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 12px;
  margin-top: var(--rd-sns-stats-gap);
}

/* ── The one commerce block ──────────────────────────────────────────────── */
/*
   A DEVIATION FROM `ScreenSnsOver`, filed for the designer. The drawing puts a
   "Last target" attribute chip on the receipt with a text act beside it naming
   an in-app catalogue. Both are gone: the shooter wants to see the target they
   got wrong, so this block draws that photograph as a small card, with one link
   under it that says where it actually goes — out to the website.

   The card is the run screen's own 4:5 frame at a smaller measure: rd-sns__photo
   is the full-bleed stage and cannot simply be reused, because this one is a
   thumbnail beside body copy rather than the subject of the screen. Its width is
   the one value the tablet redraws, so it is a custom property and the `--tab`
   root re-declares it — ONE rule paints it on both devices, which is the idiom
   the eight properties above it already use. No width media query: the receipt
   picks its arm in Rust by device class, never in CSS.

   The photograph is absolutely positioned inside the chamfer's own
   `position: relative` so `object-fit: cover` fills the 4:5 whatever the
   catalogue's aspect ratio is — the same argument rd-sns__photo-img makes, and
   the same `user-select` latch, because a framed photograph is one gesture from
   being dragged out.

   The link is `--rd-acc` and NOT underlined. `55` draws its act undecorated and
   the house default is already `a { text-decoration: none }` in the base sheet,
   so an underline here would be a double opt-out. The accent is what marks the
   one word on the receipt that leaves the app. It keeps `display: inline-block`
   for one reason: a tap target that wrapped as an inline box would split into
   two hit rectangles with dead space between them, a phone defect the drawings
   cannot show.

   The wrapper exists to SHRINK THE FRAME. The kit's chamfer box paints its
   stroke on an outer element the page cannot reach, and a width set on the
   inner face would leave a full-width stroked box around a narrow photograph.
   A column flex with `align-items: flex-start` sizes that outer box to its
   content instead, which is the width the inner face declares. Both receipts
   stack left-aligned, so the alignment is the one they already have.
*/
.rd-sns-over__failed {
  margin-top: var(--rd-sns-commerce-gap);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.rd-sns-over__failed-card {
  width: var(--rd-sns-failed-card);
  height: auto;
  aspect-ratio: 4 / 5;
  position: relative;
  overflow: hidden;
}

.rd-sns-over__failed-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  user-select: none;
  -webkit-user-select: none;
}

.rd-sns-over__failed-link {
  display: inline-block;
  margin-top: var(--rd-sns-failed-link-gap);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: var(--rd-sns-failed-link);
  color: var(--rd-acc);
}

.rd-sns-over__note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: var(--rd-sns-note);
  line-height: 1.5;
  color: var(--rd-fg3);
  margin-top: var(--rd-sns-note-gap);
  text-wrap: pretty;
}

/* Below the acts since 2026-08-31 (James: a tall viewport must not strand the
   acts at the foot) — it eats the leftover height AND carries the
   home-indicator clearance, which used to be the acts' padding: whatever
   remains at the foot owns the safe area. */
.rd-sns-over__spacer {
  flex: 1;
  min-height: max(12px, env(safe-area-inset-bottom));
}

/* "Acts are full-width like ScreenDryDone's; the floating bottom-right act is
   a Lists rule (51) and this is a receipt." In the content flow at the
   receipt's own note gap since 2026-08-31 — see the spacer above. */
.rd-sns-over__acts {
  flex: none;
  display: grid;
  gap: 8px;
  margin-top: var(--rd-sns-note-gap);
}

/* ---------------------------------------------------------------------------
   §T · THE TABLET RENDERING

   Drawn: **`55 (Tablet) Shoot No Shoot`** — screen `ScreenTabSns`, leaf
   `TabSnsThumb`; doc rows 01 "The Front Door Keeps The Rail", 02 "Thumbs On The
   Rails", 03 "The Receipt, At Rest". Rust: `pages/shoot_no_shoot.rs`, the
   `DeviceClass::Tablet` arm of each of the three screens.

   ⚠️ STILL NO WIDTH `@media`, AND §T IS NOT AN EXCEPTION TO THE HEAD BANNER —
   it is what the head banner describes. The device is resolved in Rust by
   `use_device_class()` and the arm emits a `--tab` modifier; every rule below
   hangs off one of those, so this section is unreachable from a phone without
   a page-side branch having chosen it.

   §T carries ONE `@media`, row 02's desktop cluster, and it is the head
   banner's second allowed prelude rather than a third: `(hover: hover) and
   (pointer: fine)`, an input capability, and every selector under it is still
   `.rd-sns-run--tab`-prefixed. It splits the tablet rendering by what the
   reader is HOLDING — a gripped iPad keeps the rails, a desk gets the two acts
   together — which is a question the Rust device class does not ask and a
   width could not answer.

   ⚠️ THE MODIFIERS SIT ON THE THREE ROOTS AND THE ELEMENTS TAKE A PREFIX —
   `s10-par.css` §T's idiom. `.rd-sns--tab` / `.rd-sns-run--tab` /
   `.rd-sns-over--tab` are the SAME three roots the phone declares, so the
   reduced-motion blanket at the foot and the flood's `z-index` stacking both
   still reach the tablet subtree without a line being rewritten. New elements
   take `.rd-sns-tab__*`, disjoint from `.rd-sns__*`, for the reason s10's own
   §T states: one screen on two devices, one owner, and where a thing is
   painted has one answer.

   ⚠️ WHAT IS A TOKEN AND WHAT IS A RULE. Where the tablet only resizes the
   phone's own element — the thumb face, the drain, the START tile, the hero,
   the failure headline — the value became a custom property on the shared
   token block above and the `--tab` root re-declares it. That keeps ONE rule
   per element for both devices, which is what stops the two renderings drifting
   apart in a property nobody meant to change. Where the tablet is a different
   LAYOUT — three columns instead of a stack — it gets its own rules.

   ⚠️ 250px PAINTED PER THUMB, and the face is 247 inside the 1.5px stroke
   either side, exactly as the phone's 96/93 pair is built. The doc: the same
   half-second-window logic as `48`'s 96, at tablet scale.

   ⚠️ THE THUMB COLUMNS CLEAR 56px OF THE BOTTOM EDGE and do NOT spend
   `--rd-sns-safe-foot`. That token is `48`'s home-indicator clearance for a
   BOTTOM-SPANNING split; here the controls sit in the lower third of each side
   rail, where the drawn 56 is doing a different job — it is where a gripping
   thumb rests, not where the system's gesture strip is. Two numbers, two
   reasons, so the tablet does not borrow the phone's.

   ⚠️ TYPE FLOORS. `01 Foundations` §05: 11px mixed-case, 9.5px tracked
   uppercase — iPad pixels run larger than the phone's 13px physical floor.
   TWO of the tablet's drawn values sit under a floor and are raised for that
   reason and nothing else: the 10.5px best-note goes to 11 (mixed-case) and the
   9px START note to 9.5 (tracked uppercase). Every other value below already
   clears both floors — including the nine the tablet redraws and this section
   now re-declares as tokens.

   ⚠️ THE TWO FLAGGED FONT-STRETCH WIDTHS ARE RESOLVED, 2026-08-27.
   `TabSnsThumb` draws its word at 66% and the failure headline at 64%; both
   share the phone's `.rd-sns__thumb-word` / `.rd-sns-over__headline` rules
   (the tablet root only re-declares sizes as tokens, not font-stretch), so
   both now render at 66% — the named small-display role — with 64 retired
   into it. See the head banner and `docs/design/kit/SNS-55.md` §5.
   --------------------------------------------------------------------------- */

/* ── Row 01 · the front door keeps the rail ─────────────────────────────── */
/*
   One centred 520px column. The width is the RULING, not a measure preference:
   "a start screen stretched across 986px of pane is a form with nothing to fill
   in". The lede and the title are the SHELL's header on this device, so the
   column starts at the strip or the card rather than at a heading.
*/
.rd-sns--tab {
  --rd-sns-start-h: 128px;
  --rd-sns-start-word: 48px;
  --rd-sns-card-pad: 15px 17px;
  --rd-sns-best-note: 11px;
  --rd-sns-best-note-gap: 11px;
  --rd-sns-ready: 11px;
  --rd-sns-loading-count: 12px;
  --rd-sns-start-note: 9.5px;
  padding-top: 0;
  align-items: center;
  justify-content: center;
}

.rd-sns-tab__col {
  width: 520px;
  max-width: 100%;
  display: flex;
  flex-direction: column;
}

/* The saved-batch strip. `RdTabletShell` has no banner slot — that is
   `RdShell`'s — and the drawing puts the strip inside the column anyway, with
   16px under it. */
.rd-sns-tab__strip {
  margin-bottom: 16px;
}

.rd-sns-tab__foot {
  flex: none;
  margin-top: 20px;
}

/* ── Row 02 · thumbs on the rails ───────────────────────────────────────── */
/*
   Three columns: HOLD under the left thumb, the target in the centre, SHOOT
   under the right thumb on the trigger side. There is no rail and no header on
   this screen, so `.rd-sns-tab__cols` fills the whole content region.
*/
.rd-sns-run--tab {
  --rd-sns-clock: 19px;
  --rd-sns-thumb-h: 250px;
  --rd-sns-thumb-face-h: 247px;
  --rd-sns-thumb-word: 30px;
  --rd-sns-drain-h: 5px;
  padding-top: 0;
}

.rd-sns-tab__cols {
  flex: 1;
  min-height: 0;
  display: flex;
  gap: 30px;
  padding: 0 30px 30px;
}

/* Bottom-aligned: the thumbs sit in the lower third of each edge, where they
   rest on a gripped landscape iPad. */
.rd-sns-tab__thumb-col {
  width: 168px;
  flex: none;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding-bottom: 56px;
}

/* The phone's thumb is a flex ROW item and grows; in a column it would grow to
   the full height and lose the drawn 250. It is a fixed block here. */
.rd-sns-tab__thumb-col .rd-sns__thumb {
  flex: none;
  width: 100%;
}

.rd-sns-tab__stage {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* The sequence label left, the mono window value right — "for whoever wants the
   figure". */
.rd-sns-tab__head {
  flex: none;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 8px 4px 0;
}

.rd-sns-tab__card-wrap {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

/* ⚠️ 420px, NOT THE PANE. The 4:5 card at pane width would put the
   photograph's far corner outside the thumb-anchored gaze line; 420 keeps the
   whole photograph inside one fixation at arm's length. */
.rd-sns-tab__card {
  width: 420px;
  max-width: 100%;
}

/* ── Row 02 · desktop (fine pointer) · the two acts come together ───────── */
/*
   ⚠️ RULED BY JAMES, 2026-08-28 (rehearsal): "Shoot / No-Shoot on a DESKTOP
   browser spreads HOLD and SHOOT to the far bottom corners — at 1934px wide the
   buttons sit at x≈30 and x≈1400 with the image centred. On desktop the two
   acts sit TOGETHER, centred under the active image: HOLD left, SHOOT right,
   one gap between, same size, same F/J key captions."

   The corner grip above is not a mistake — it is `55 (Tablet)` row 02 built
   exactly as drawn, and it stays for the device it was drawn for. A gripped
   landscape iPad rests a thumb on each edge; a desk has no edges to grip and
   the spread just makes the reader cross 1400px of screen with a mouse.

   ⚠️ THE PRELUDE IS A CAPABILITY QUERY, NOT A BREAKPOINT — same reasoning the
   key-cap gate carries, and the reason it is spelled this way rather than as a
   width. This part's head banner refuses width, `max-width` and `orientation`
   by name, because the DEVICE is chosen once in Rust by `use_device_class()`
   and a width here would be a second, invisible answer to that question. The
   ruling itself says so: "detection = input capability, not width". So a touch
   tablet keeps the corner grip at any width, and a small desktop window gets
   the cluster — which is the split James ruled, not a size.

   ⚠️ AND IT MOVES NOTHING THE PHONE CAN SEE. Every selector is prefixed
   `.rd-sns-run--tab`, the modifier the `DeviceClass::Tablet` arm emits, so this
   is unreachable without the Rust branch having chosen the tablet rendering
   first. The corner rules above are untouched: the desktop reading overrides
   the two properties that place them and nothing else.

   ⚠️ GRID, AND FOUR TRACKS RATHER THAN TWO. The act row wants two centred
   columns; the stage wants the WHOLE width, because the card is 420px and two
   168px acts plus the 30px gap are 366 — a stage confined to the acts' tracks
   would shrink the photograph to fit its own buttons. The `1fr` outsides are
   the centring: equal, so the pair sits centred, and the stage spans `1 / -1`
   over all four. No new element and no DOM change; source order is still
   HOLD, stage, SHOOT, which is still left-to-right.

   The sizes and the gap are the ones already drawn — the 168px column width and
   the container's own 30px `gap` are inherited from the rules above, so the two
   acts stay equal and exactly one gap apart without this block naming a number.
*/
@media (hover: hover) and (pointer: fine) {
  .rd-sns-run--tab .rd-sns-tab__cols {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto minmax(0, 1fr);
    grid-template-rows: 1fr auto;
  }

  .rd-sns-run--tab .rd-sns-tab__stage {
    grid-row: 1;
    grid-column: 1 / -1;
  }

  /* The 56px is the gripping thumb's rest against a held edge. Under the
     cluster the acts are not at an edge, so the clearance has no job and the
     row sits on the container's own bottom padding. */
  .rd-sns-run--tab .rd-sns-tab__thumb-col {
    grid-row: 2;
    padding-bottom: 0;
  }

  .rd-sns-run--tab .rd-sns-tab__thumb-col:first-child {
    grid-column: 2;
  }

  .rd-sns-run--tab .rd-sns-tab__thumb-col:last-child {
    grid-column: 3;
  }
}

/* ── Row 03 · the receipt, at rest ──────────────────────────────────────── */
/*
   The rail returns — after a run, leaving is allowed — so this screen is a
   normal `RdTabletShell` with a header. The receipt is one 560px column.

   ⚠️ THE FLOOD NEEDS NOTHING HERE. `.rd-sns-over__flood` is already
   `position: fixed; inset: 0`, and `kit-tablet-shell.css` declares no
   `transform`, `filter` or `contain` between the shell root and the content —
   so it resolves against the VIEWPORT and covers the rail, which is the ruling:
   "a wash that stops at the rail edge reads as a pane state, not a moment".
   `.rd-sns-over > *` still stacks the receipt above it in ink.
*/
.rd-sns-over--tab {
  --rd-sns-hero: 96px;
  --rd-sns-headline: 32px;
  --rd-sns-card-pad: 15px 17px;
  --rd-sns-best-tag-gap: 13px;
  --rd-sns-hero-gap: 20px;
  --rd-sns-hero-label-gap: 8px;
  --rd-sns-stats-gap: 18px;
  --rd-sns-commerce-gap: 16px;
  --rd-sns-failed-card: 104px;
  --rd-sns-failed-link: 11px;
  --rd-sns-failed-link-gap: 9px;
  --rd-sns-note: 11px;
  --rd-sns-note-gap: 14px;
  padding-top: 0;
  align-items: center;
  justify-content: center;
}

.rd-sns-tab__receipt {
  width: 560px;
  max-width: 100%;
  display: flex;
  flex-direction: column;
}

/* Side by side, where the phone stacks them: 560px is wide enough for two, and
   the phone's stack is a measure decision rather than a hierarchy one. */
.rd-sns-tab__acts {
  flex: none;
  display: flex;
  gap: 10px;
  margin-top: 22px;
}

.rd-sns-tab__act {
  flex: 1;
  min-width: 0;
}

/* ── Reduced motion ──────────────────────────────────────────────────────── */
/*
   ⚠️ THIS BLOCK GATES MOTION THIS PART DOES NOT HAVE YET, ON PURPOSE.

   Today nothing here declares `animation` or `transition`. The one thing that
   moves — the drain bar — moves because `pages/shoot_no_shoot.rs` rewrites its
   inline `width` on a repaint tick, and that is the CLOCK rather than a
   decoration: a reader with motion off still has to see how much window is
   left, so CSS neither can nor should stop it.

   ⚠️ A BLANKET, WHERE `kit-loading.css` AND `adm-evd-mob.css` NAME CLASSES, and
   the divergence is the whole point rather than laziness. Those two parts were
   written AFTER their animations and could name them; this one is written
   BEFORE. `43 Loading States`'s ruling — "the shapes carry the meaning without
   the motion" — has to hold for motion the band has ALREADY RULED and not yet
   built. Three named pieces are coming, and every one of them is decoration:

     THE GREEN FLOOD.  ✅ LANDED WITH M2, and the blanket predicted it exactly.
     `48`'s Games band: "A new personal best washes the screen t.ok … for a beat;
     the receipt punches through in ink and it settles. Visual-only; REDUCED
     MOTION GETS THE SETTLED FRAME (green tag, green hero) WITH NO WASH." The
     animation is `rd-sns-flood` on `.rd-sns-over__flood`, which sits under
     `.rd-sns-over` and is therefore already covered by the blanket below —
     nothing here had to be rewritten for it. It is ALSO named explicitly, in the
     one rule after the blanket, and that rule is documented where it stands.

     THE MUZZLE GLOW AND THE SCREEN KICK.  Still coming, same gate. 55 adds them
     to the same sentence — "The muzzle glow and screen kick from the prototype
     stay, same gate." Neither is built; when either is, the blanket reaches it
     for free provided it sits under one of the three roots, and
     `reduced_motion_stops_every_animation_this_part_declares` fails by NAME if
     it does not.

     THE TARGET'S SLIDE-IN.  ✅ LANDED WITH M1b, and it is the first thing the
     blanket actually covers. 55 draws the entry as a still — the card offset
     right and slightly transparent — and rules only that targets enter from a
     side, with the direction carrying no meaning yet. The classes are
     `.rd-sns__slide` and `.rd-sns__slide--entering`; both sit under
     `.rd-sns-run`, so the blanket reaches them without being rewritten, which
     is exactly what it was written blanket-shaped for. A card that appears
     instead of sliding is the same card.

   Naming a class that did not exist yet would have been a rule the dead-class
   checker is right to reject; naming nothing would have left the block empty. So
   the selector is the part's three roots and their subtrees, which is the one
   form that was true before M1b and is still true after it. The prediction paid
   off once already: the slide landed and this block needed no edit to gate it.

   ⚠️ THE ENTERING OFFSET IS NEUTRALISED TOO, and stopping the `transition`
   alone would not be enough. With motion off, a card left at `translateX(34%)`
   for a repaint tick and then snapped to rest is a jump — a worse artefact than
   the tween it replaced. So the second rule below resets the offset itself. It
   is a separate rule because it changes VALUES rather than turning motion off,
   and it wins on source order for the same reason the blanket does.

   ⚠️ `transition` IS STOPPED TOO, not only `animation` — `adm-evd-mob.css`'s
   reduced-motion block gates a `transition` and nothing else, so both halves of
   the vocabulary are covered rather than the half this part happens to expect.

   ⚠️ NO `!important`. Same reading `kit-loading.css` records: `@media` adds no
   specificity, and this block is LAST in the part, so it already wins every tie
   against the rules above it on source order alone. Carrying `!important` in
   would put an un-overridable declaration in the sheet for a fight nothing is
   having.
*/
@media (prefers-reduced-motion: reduce) {
  .rd-sns,
  .rd-sns *,
  .rd-sns-run,
  .rd-sns-run *,
  .rd-sns-over,
  .rd-sns-over * {
    animation: none;
    transition: none;
  }

  .rd-sns__slide--entering {
    transform: none;
    opacity: 1;
  }

  /* ⚠️ THE GREEN FLOOD, NAMED. `rd-sns-flood` is the one animation this part
     declares, and the blanket above already stops it — this rule is not what
     makes the gate work, it is what makes the gate READABLE, and it is the only
     place in the sheet where the settled frame is described in one place.

     The element's resting state is `opacity: 0`, so a stopped `rd-sns-flood`
     leaves an invisible full-bleed div rather than a green screen that never
     lifts. Restating the 0 here means the settled frame does not depend on
     reading the rule 200 lines up to know it is safe.

     WHAT THE READER STILL GETS is `48`'s own settled frame and all of it: the
     green NEW BEST tag and the green hero, neither of which animates, plus every
     figure on the receipt. Nothing about the achievement is carried by the wash. */
  .rd-sns-over__flood {
    animation: none;
    opacity: 0;
  }

  /* ⚠️ AND THE BEAT'S INK GOES WITH THE GROUND IT WAS FOR. With the wash
     stopped at `opacity: 0` the ground is the page's own again, so near-black
     copy on it would be this gate's own defect in the other direction — a
     receipt no one can read, arrived at by turning motion OFF.

     `initial` makes `--rd-sns-beat-ink` guaranteed-invalid. Every rule in the
     ink section reads it: the two that carry a fallback take the fallback, and
     the role rebinds — whose whole value is that one `var()` — become invalid
     at computed-value time and inherit the theme's own. One line, the whole
     modifier off, and no settled colour restated here to fall out of step with
     the rule it came from. */
  .rd-sns-over--flood {
    --rd-sns-beat-ink: initial;
    --rd-sns-beat-ground: initial;
  }
}
/* ==== S56-LOC · 56 (Mobile) Events Lab - Shooter frames 8–10 — the shooter's saved locations · owner: 3-track5 ==== */

/* ---------------------------------------------------------------------------
   The Location field on `Schedule A Range Day`, the picker sheet it opens, the
   location sheet at that sheet's foot, and the Saved Locations page under
   Profile that mounts the same sheet (`pages/saved_locations.rs`).

   Drawn: `56 (Mobile) Events Lab - Shooter.html` frames 7–10 —
   `ScreenPlanDayV2`'s `Location` field, `ScreenLocPick`, `ScreenLocAdd`,
   `ScreenLocManage` (`components/rd-events-lab-shooter.jsx:126-252`). Ruled
   C-56.9.1 (`uploads/design-reply-2026-08-26.md:46-56`).

   TWO BLOCKS, `.rd-sloc__*` and `.rd-sloc-pg__*` — the sheets and the page.
   They share exactly one rule, `.rd-sloc__add-row`, because the drawn foot is
   the same foot on both surfaces.

   NAME SPACE: `.rd-sloc__*`, one block, declared by no other part
   (`grep -c 'rd-sloc' apps/web/styles/*.css` is 0 everywhere else). It is NOT
   `.rd-loc*` — `adm-loc.css` owns that name for `38 Pro Locations`, the org
   console's twin surface, and two blocks one letter apart on two consoles is
   how a rule lands on the wrong screen. Nothing here contends with anything, so
   this part's position in `order.txt` re-orders no existing pair.

   ⚠️ NOT A BLOCK INSIDE `s13-plan.css`. That part scopes itself to
   `13 Community`'s range-day form and the four shapes the kit has no vocabulary
   for on it; these three shapes belong to `56`, and two of them (the picker
   sheet, the add sheet) are ALSO the Saved Locations page's — a surface that
   does not live on `13` at all. A part whose contents sit outside its own
   stated scope on the day it merges is how a scope note stops being read.

   ⚠️ NO WIDTH `@media`. The app decides phone-vs-tablet in Rust
   (`device::use_device_class`), never in CSS, and this block is mounted from
   both arms of `range_day_form.rs`. The one geometry that has to survive the
   translation is the card's cap — see `.rd-sloc__sheet-card`.

   The chamfered boxes, the labels, the text fields, the heading and the primary
   button are all kit. What is page CSS is the field's inner face, the sheet's
   scrim and card, the radio rows, and the two quiet acts (the dashed add row
   and the disclosure).
   --------------------------------------------------------------------------- */

/* ── The field on the form ──────────────────────────────────────────── */

.rd-sloc__field {
  display: grid;
  gap: 5px;
}

/* The kit box's inner face. Padding and min-height live on the inner box at
   every RdChamferBox site in the app; the outer carries geometry only. */
.rd-sloc__trigger-box {
  padding: 0;
}

/* The whole face is the control — a reset button filling the box, so the tap
   target is the drawn field rather than the words inside it. */
.rd-sloc__trigger {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 42px;
  padding: 10px 13px;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.rd-sloc__trigger:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

.rd-sloc__trigger-name {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg);
}

/* Nothing picked. The em-dash and the quiet colour are the same pair the
   drawn `Time — optional` field one row above uses for its own empty state
   (`rd-events-lab-shooter.jsx:143-145`, `dim`), so the form says one thing
   about emptiness rather than two. */
.rd-sloc__trigger-name--empty {
  color: var(--rd-fg3);
}

/* `YOURS ▾` — the source word and the affordance in one small-caps note, as
   drawn (`rd-events-lab-shooter.jsx:147`). */
.rd-sloc__trigger-note {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8px;
  color: var(--rd-fg3);
}

/* ── The sheets ─────────────────────────────────────────────────────── */

/* `inset: 0` against the nearest positioned ancestor, which is deliberate and
   is what lets one block serve both arms: on the phone that is `.rd-plan`
   inside `.rd-shell__content`, and on the tablet it is `.rd-tshell`, whose
   `modal` slot exists precisely so a stacked surface resolves `inset: 0`
   against the shell and *"dims everything, rail included"*
   (`components/kit/tablet_shell.rs`, §"The modal is a child of the SHELL"). */
.rd-sloc__sheet {
  position: absolute;
  inset: 0;
  z-index: 2;
}

/* The way out. Frames 8 and 9 draw no close control on either sheet, so the
   scrim IS the control and is a real button with an accessible name — the same
   call `.rd-plan__scrim` makes about the add-drill sheet two fields down. */
.rd-sloc__scrim {
  position: absolute;
  inset: 0;
  width: 100%;
  padding: 0;
  border: 0;
  background: rgba(0, 0, 0, .72);
  cursor: pointer;
}

/* ⚠️ THE CAP IS THE KIT'S 460, AND IT IS WHAT SURVIVES THE PHONE→TABLET
   TRANSLATION. Under 460 the `max-width` does nothing at all, so the phone gets
   exactly the drawn full-bleed bottom sheet; on a tablet, where this same sheet
   hangs off `.rd-tshell`, an uncapped card would stretch a phone sheet across
   the whole console. 460 is the kit's own translated sheet width
   (`rd-tablet-kit.jsx:376`, quoted in `tablet_shell.rs`), not a number chosen
   here — the tablet grammar for THESE two sheets is undrawn (gap G-9) and this
   cap is the smallest thing that keeps the drawn one legible until it is.

   `bottom: 12px` is `.rd-plan__sheet-card`'s, and for its reason: the home
   clearance is already spent by `.rd-shell__home-clear`, so taking
   `--rd-home-clear` again here would double-count it. */
.rd-sloc__sheet-card {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 12px;
  max-width: 460px;
  margin: 0 auto;
}

.rd-sloc__sheet-in {
  padding: 15px 15px 14px;
}

.rd-sloc__sheet-sub {
  margin: 7px 0 10px;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.45;
  text-wrap: pretty;
}

/* UNDRAWN — the picker's empty and loading states are gap G-3. Same shape as
   the drill sheet's own empty line, so the two sheets on one form do not read
   as two products. */
.rd-sloc__none {
  padding: 10px 2px 2px;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.45;
  text-wrap: pretty;
}

.rd-sloc__rows {
  display: grid;
  max-height: 244px;
  overflow-y: auto;
}

/* One offered location. Drawn as a 46px rule-separated row, not the drill
   sheet's boxed toggle — because it is a radio: one location is one choice, so
   the tap picks and closes (the per-selector principle, ruled 26 Aug). */
.rd-sloc__row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 46px;
  padding: 8px 4px;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.rd-sloc__row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* The drawn 13px ring. A circle, and the ONE place in this block that is round
   on purpose: it is a radio mark, and the chamfer rule is about boxes. */
.rd-sloc__mark {
  flex: none;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  border: 1.5px solid var(--rd-ctrl);
  background: transparent;
}

.rd-sloc__mark--on {
  border-color: var(--rd-acc);
  background: var(--rd-acc);
}

.rd-sloc__row-name {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
}

/* `Yours` / `Public` — the only thing on the row distinguishing a range
   somebody else published from a spot of your own, which is why it is drawn on
   every row rather than only on the public ones. */
.rd-sloc__row-src {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8px;
  color: var(--rd-fg3);
}

/* The dashed foot row, drawn on both the picker sheet and the Saved Locations
   page — creation exists in two places on purpose (`38 Pro Locations:98`). */
.rd-sloc__add-row {
  display: block;
  width: 100%;
  margin: 10px 0 0;
  padding: 11px 13px;
  border: 1px dashed var(--rd-ctrl);
  background: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg2);
  text-align: left;
  cursor: pointer;
}

.rd-sloc__add-row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* ── The add sheet's disclosure ─────────────────────────────────────── */

.rd-sloc__disclose-row {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 12px;
}

/* An accent word, not a button box: the five fields behind it are optional and
   a bordered control would ask to be pressed. */
.rd-sloc__disclose {
  padding: 0;
  border: 0;
  background: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-acc);
  cursor: pointer;
}

.rd-sloc__disclose:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* The drawn second half of the line — it is the sentence that says the five
   fields may be left alone, so it is beside the act rather than under it. */
.rd-sloc__disclose-note {
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  line-height: 1.45;
  text-wrap: pretty;
}

.rd-sloc__address {
  display: grid;
  gap: 9px;
  margin-top: 10px;
}

/* ── The add sheet's foot ───────────────────────────────────────────── */

.rd-sloc__foot {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 14px;
}

.rd-sloc__foot > :first-child {
  flex: 1;
}

/* Cancel is the quiet word beside the commit, as drawn — a reset button, never
   a second filled control. */
.rd-sloc__cancel {
  flex: none;
  padding: 0;
  border: 0;
  background: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
  cursor: pointer;
}

.rd-sloc__cancel:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* ── The Saved Locations page ───────────────────────────────────────── */

/* `/profile/locations`, frame 10 (`ScreenLocManage`). Its own `-pg` block
   rather than more `__` elements on the sheet's: the page and the sheets are
   two surfaces that happen to share a subject, and one of them opens inside
   the other. What they DO share is the dashed `.rd-sloc__add-row`, mounted on
   both — creation exists in two places on purpose (`38 …:98`), and one rule is
   what keeps the two feet identical. */
.rd-sloc-pg {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* The drawn intro, verbatim. Quiet, and it carries the fact the list itself
   cannot: public ranges are in the picker and are not here. */
.rd-sloc-pg__intro {
  margin: 8px 0 0;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.5;
  text-wrap: pretty;
}

.rd-sloc-pg__wait {
  display: grid;
  gap: 8px;
  margin-top: 14px;
}

/* UNDRAWN — frame 10 draws two rows and no empty frame. */
.rd-sloc-pg__none {
  margin: 14px 0 0;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.45;
}

.rd-sloc-pg__rows {
  margin-top: 14px;
}

/* The drawn 52px rule-separated row: two lines of copy and one quiet act. */
.rd-sloc-pg__row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 52px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-sloc-pg__row-copy {
  flex: 1;
  min-width: 0;
}

.rd-sloc-pg__row-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
}

/* The address, or `No address — just a name, which is fine`. The drawn
   sentence is why this line never collapses: a row with nothing under the name
   would read as a row still loading. */
.rd-sloc-pg__row-sub {
  margin-top: 2px;
  color: var(--rd-fg3);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.4;
}

/* `Edit` — an accent word, and the one act frame 10 draws on a row. A reset
   button, never a bordered control: a box here would compete with the dashed
   add row for the eye, and the add row is the page's only real affordance. */
.rd-sloc-pg__edit {
  flex: none;
  padding: 4px 2px;
  border: 0;
  background: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-acc);
  cursor: pointer;
}

.rd-sloc-pg__edit:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}
/* ==== S58-QUAL · 58 (Mobile) Qualifications — the verdict face on a score · owner: lane-drills ==== */
/*
   `58 (Mobile) Qualifications` heads itself **"Pass Is The Headline"**, and
   that sentence is the whole of this part. Frames 2/3/5 (`ScreenQualResult`)
   put the declared verdict at display size with the run's number *under* it;
   frame 4 (`ScreenQualHistory`) leads the row's right slot with the same word
   and keeps one row shape — *"The verdict leads on qualifier rows; ordinary
   drills keep their number. Same row, different headline."*

   The rules live here rather than in the pages' own parts because ONE face is
   the point: `score_detail.rs` (the hero) and `score_history.rs` (the row)
   would otherwise declare the same ink twice, in two files, and drift.
   `apps/web/src/verdict.rs` is the Rust half and carries the reasoning.

   ⚠️ **FAIL IS WARN, NOT DANGER, AND THAT IS A RULING RATHER THAN A
   TRANSCRIPTION.** 58's own fixture hard-codes `#FF5A3C` — `TT.dark.danger` —
   and does not switch it in the light theme its frame 5 re-draws. Two ratified
   sources say otherwise and the shipped build is one of them:

     · `08 (Mobile) Assessments` (`rd-assess.jsx`, `ScreenAssessResult`):
       *"FAIL in fg, not danger. Danger stays reserved for things that are
       broken — a refused sync, a run that will not save."*
     · `admin/progress.rs:1013` paints the ruled `Failed` chip on
       the `rd-prog-chip--warn` rule (undotted here on purpose: `progress.rs`
       pins that ink by slicing the assembled sheet on the dotted literal),
       and `ProTone` deliberately has no danger arm.

   So `--rd-warn` here, the same token, and 58's literal is read as fixture
   drift. Whichever way a designer finally rules it, it must be ONE vocabulary
   across 08, 58, `adm-prog.css` and this file — which is why there are three
   selectors and not thirty.

   ⚠️ **NO PASS LINE IS PAINTED.** Frame 2 prints `of 500 — passing is 400`
   under the number. No drill carries a threshold at any layer (the 6 Aug 2026
   ruling, `libs/core/src/engine.rs:1194-1211`: *"the standard stays prose; the
   shooter declares; the app records"*), so there is nothing to set. There is
   deliberately no `.rd-qualface__line` waiting empty for it.
*/

/* ── The detail hero (`score_detail.rs`) ─────────────────────────────────── */

/* The verdict and its number, as one block under the demoted run name. The
   name stays the `<h1>`: the document outline answers "which run is this",
   which is the name, and 58's inversion is a SIZE decision, not an outline
   one. */
.rd-qualface {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* ⚠️ **NOT `RdHeading`, and the kit says so itself.** `components/kit/heading.rs`
   spends a paragraph on it: *"No colour prop, and that is load-bearing … State
   never recolours the heading. If a screen needs a red heading, that is a
   finding to route, not a prop to add."* — and its `size` is documented as a
   fit control over 17–30, which 44 is not. The kit's own escape is the one
   taken here: a value rendered large is not a heading, so this element carries
   the display TYPE ROLE (Archivo 900 / `wdth` 62% / zero tracking /
   `line-height: .92`, transcribed from `.rd-h1`) and adds the one thing the
   role refuses — the ink. The heading component is untouched. */
.rd-qualface__verdict {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 900;
  font-stretch: 62%;
  letter-spacing: 0;
  line-height: .92;
  font-size: 44px;
  text-transform: uppercase;
  color: var(--rd-fg);
}

.rd-qualface__verdict--pass { color: var(--rd-ok); }

.rd-qualface__verdict--fail { color: var(--rd-warn); }

/* Asked, and nothing was said. It is not a verdict, so it does not take the
   verdict's size or a state colour — the quiet treatment
   `.rd-prog-qual__none` already gives the same fact on the Pro side. */
.rd-qualface__verdict--none {
  font-size: 17px;
  text-transform: none;
  color: var(--rd-fg3);
}

/* The number, under the headline — 58's `431` on its own baseline row. It is
   the run's figure and nothing is derived from it. */
.rd-qualface__figure {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 20px;
  color: var(--rd-fg2);
}

/* ── The history row's right slot (`score_history.rs`) ───────────────────── */

/* Two owners on one BEM block, the arrangement `s03-splits.css` already
   records for `.rd-scorein__*`: `.rd-history__row-result` is declared in
   adm0-picker.css (owner: lane-3) and is UNTOUCHED — these add only the
   verdict ink, and win on source order because this part sits after it in
   order.txt. The element is the same one; frame 4's whole argument is that the
   row shape does not change. */
.rd-history__row-result--pass { color: var(--rd-ok); }

.rd-history__row-result--fail { color: var(--rd-warn); }

/* ── The RO's driving verdict face (`pages/driving_score.rs`, WP5) ───────── */
/*
   ⚠️ **THIS PART, NOT A NEW ONE, AND NOT `s09-board.css`.** The block mounts
   inside the squad board's expanded row, so `s09-board.css` looks like the
   home — but this file is *"the verdict face on a score"* and that is exactly
   what the block IS: the same declared Pass/Fail/DQ, drawn for the instructor
   instead of the shooter. Putting it here keeps ONE vocabulary for the verdict
   ink across 08, 58, `adm-prog.css` and this face, which is the argument the
   header above already makes about three selectors rather than thirty. It also
   costs `apps/web/styles/order.txt` no new line, so the 109-file pin in
   `cli/src/css.rs` does not move.

   ⚠️ **FAIL IS WARN HERE TOO.** Same ruling, same token, same reason: danger
   stays reserved for things that are BROKEN — a refused sync, a run that will
   not save — which is what `.rd-dvf__error` is and what a declared Fail is not.

   UNDRAWN. There is no frame for a driving face in the corpus; the six
   drawings slice 5.8 commissions had not landed when this shipped. So this
   borrows shapes that are already ruled rather than inventing a look: the
   verdict pair's geometry is `.rd-scorein__toggle-*`'s, the field/label pair is
   `.rd-drillf__field`/`__label`'s, and the tones are this file's own. A
   designer ruling on the real frame replaces the layout and keeps the ink.
*/
.rd-dvf {
  display: grid;
  gap: 16px;
}

.rd-dvf__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
}

.rd-dvf__who {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 17px;
  font-weight: 600;
  color: var(--rd-fg);
}

.rd-dvf__what {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 14px;
  color: var(--rd-fg2);
}

/* Vehicle class and the par GATE. Quiet, because neither is an act — they are
   facts about the exercise the RO needs while marking, not controls. */
.rd-dvf__meta {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
}

/* `Attempt 3 of 5 · 1 of 4 clean`. The one line that tells the RO where the
   driver stands, so it sits at label weight rather than at note weight. */
.rd-dvf__attempt {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  font-weight: 600;
  color: var(--rd-fg2);
}

.rd-dvf__grid {
  display: grid;
  gap: 8px;
}

.rd-dvf__crit {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.rd-dvf__crit-label {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 14px;
  color: var(--rd-fg);
}

/* The terminating flag, said in words on the row it belongs to. It is the whole
   of the zero-fault gate the org authored (decision 10 — the grid has one
   spelling), so the instructor must be able to see WHICH row ends the run
   without opening the drill. */
.rd-dvf__crit-ends {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--rd-warn);
}

.rd-dvf__crit-marks {
  display: flex;
  flex: none;
  gap: 8px;
}

/* ⚠️ **44px TALL, AND THAT IS THE FLOOR RATHER THAN A CHOICE.** These are
   pressed with a clipboard in one hand at a cone course. The kit's own tappable
   floor applies (`kit-hit.css`); the width is generous for the same reason. */
.rd-dvf__mark {
  min-height: 44px;
  min-width: 76px;
  padding: 0 14px;
  background: none;
  border: 1.5px solid var(--rd-ctrl);
  border-radius: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  font-weight: 600;
  color: var(--rd-fg2);
  cursor: pointer;
}

.rd-dvf__mark:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* Selected = filled, and only for INPUT — the corpus rule the drill form's own
   segmented control states in full. */
.rd-dvf__mark--ok-on {
  border-color: var(--rd-ok);
  color: var(--rd-ok);
}

.rd-dvf__mark--fault-on {
  border-color: var(--rd-warn);
  color: var(--rd-warn);
}

.rd-dvf__field { display: grid; gap: 6px; }

.rd-dvf__label {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--rd-fg2);
}

.rd-dvf__control {
  min-height: 44px;
  padding: 10px 12px;
  background: var(--rd-field);
  border: 1.5px solid var(--rd-ctrl);
  border-radius: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 14px;
  color: var(--rd-fg);
  appearance: none;
  -webkit-appearance: none;
}

.rd-dvf__control:focus { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

.rd-dvf__verdict { display: flex; gap: 10px; }

.rd-dvf__call {
  flex: 1 1 0;
  min-height: 52px;
  background: none;
  border: 1.5px solid var(--rd-ctrl);
  border-radius: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 15px;
  font-weight: 700;
  color: var(--rd-fg2);
  cursor: pointer;
}

.rd-dvf__call:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* ⚠️ ONE LIT TREATMENT FOR ALL THREE CALLS, and it is deliberate. Pass in
   `--rd-ok` and Fail in `--rd-warn` would colour the RO's own press as though
   the app had an opinion about it; what is lit is WHICH call was made. The
   verdict's tone belongs on the RECORD (`.rd-qualface__verdict`, above), where
   it describes an outcome rather than a button. */
.rd-dvf__call--on {
  border-color: var(--rd-acc);
  color: var(--rd-fg);
}

.rd-dvf__note {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
}

/* A DQ with no reason is not yet a DQ — a caution about an unfinished entry,
   never `--rd-danger`, which this file reserves for a broken thing. */
.rd-dvf__warn {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-warn);
}

/* The write failed. THIS is the broken thing danger is reserved for. */
.rd-dvf__error {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  color: var(--rd-danger);
}

/* The set's headline, straight from core's `score_summary` — never assembled
   here (decision 12). */
.rd-dvf__standing {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 15px;
  font-weight: 700;
  color: var(--rd-fg);
}

/* Up to three acts sit here while the set is open — Save Run, Abort run and
   End set — so the row is spaced rather than butt-joined. `.rd-dvf__act`'s
   `flex: 1 1 auto` shares the width between whatever is drawn. */
.rd-dvf__acts { display: flex; gap: 10px; }

.rd-dvf__act {
  flex: 1 1 auto;
  min-height: 52px;
  border-radius: 0;
  border: 1.5px solid var(--rd-ctrl);
  background: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 15px;
  font-weight: 700;
  color: var(--rd-fg);
  cursor: pointer;
}

.rd-dvf__act--primary {
  border-color: var(--rd-acc);
  color: var(--rd-acc);
}

.rd-dvf__act:disabled { color: var(--rd-fg3); border-color: var(--rd-ctrl); cursor: default; }
.rd-dvf__act:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* The refusal when the drill's judgement arm cannot be read on this build.
   Deliberately not an empty grid — see `driving_score.rs`. */
.rd-dvf__none {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 14px;
  color: var(--rd-fg2);
}
/* ==== SX-NOTFOUND · the catch-all recovery screen (UNDRAWN) · owner: lane-track8 ==== */

/* ---------------------------------------------------------------------------
   `pages/not_found.rs`, route `/:..segments`. The screen a shooter lands on
   after a typo, a stale bookmark, or a native deep link (`/scores`) opened in a
   browser.

   ⚠️ **UNDRAWN, AND THAT IS THE FIRST THING TO KNOW ABOUT THIS PART.** The
   corpus draws no 404 screen — swept 2026-08-25 across every `.html`, `.jsx`
   and `.md` in the corpus for `404 | page not found | unmatched | catch-all |
   dead link | bookmark`, and the only hits are (a) an unrelated org-visibility
   ruling (`33 (Desktop) Pro Org Settings.html:55`, "The public page 404s rather
   than rendering as closed") and (b) two build-side INVENTORY entries that
   record what this page already does rather than deciding anything
   (`uploads/design-feature-inventory.md:167`,
   `uploads/implementation-ground-truth/our-inventory.md:160`).

   So every value below is the minimal move the brief allows — fixed greys and
   Tailwind-lookalike utilities become the `--rd-*` roles, geometry unchanged —
   and nothing here is a design decision dressed up as a transcription. Same
   posture `sx-shared.css` takes for its four undrawn components: **flagged
   UNDRAWN on the part rather than designed here.**

   ⚠️ **NO GROUND RULE IN THIS FILE, DELIBERATELY.** The bug this part closes is
   that the page had no app ground behind it at all — it rendered on the
   browser's default white because it is declared after `#[end_layout]` and so
   has no `ShellLayout` above it. The fix is NOT a background declaration here.
   `--rd-app-bg` is painted by exactly two rules in this stylesheet, `.rd-shell`
   (kit-shell.css §1) and `.rd-tcard` (s02-tcard.css), and the page now renders
   its own `RdShell` — which is what `pages/account_gone.rs` does at
   `/account-deleted` for the identical reason its module doc states: *"It
   renders its own `RdShell` … outside `ShellLayout` there is no shell above
   it."* A third rule painting the ground would be a third place for the
   texture layer to have to arrive, and kit-shell.css §1 rules that the token is
   *"the one place the texture arrives."*

   ⚠️ **AND NO CARD RULE EITHER.** The card is `RdChamferBox`
   (`Fill::Surface` + `Stroke::Line`, `chamfer: 8.0`) — a #1044-era conversion
   this part must not re-declare. `.rd-nf__card` below is the chamfer
   substrate's `inner_class`, so it carries PADDING and the column's rhythm and
   nothing else; the fill, the edge and the cut are all the primitive's. */

/* The centred column inside the shell's content region. `.rd-gate`'s
   composition (AU1-gate-screens block) and `.rd-gone`'s, for the reason both
   state: a screen the app FORCES on the reader has no scroll and no back row,
   so its column takes the full height it is given and centres in it. Not a
   borrow of either name — a 404 is neither a boot gate nor the farewell, and a
   shared block name would say the three are one screen. */
.rd-nf {
  min-height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* The card's inside. `padding: 26px` is `.rd-ccard__card`'s number
   (kit-centered-card.css, from `rd-tablet-identity.jsx:243`), taken because
   this is the same object — one chamfered card holding a short column — and 26
   is the only padding the corpus states for it. The retired `p-8` was 32px from
   the Tailwind backfill, chosen by nobody.

   `gap`, not margins on the children: the old markup spaced itself with
   `mb-2`/`mb-4` utilities, which put the rhythm on the content instead of on
   the container and left a trailing margin under the last child. */
.rd-nf__card {
  display: flex;
  flex-direction: column;
  gap: 13px;
  padding: 26px;
  text-align: center;
}

/* The attempted path — the one fact this screen exists to show, so it takes
   PRIMARY ink and not the meta grey. That is the emphasis the retired markup
   already had (`text-gray-900` on the path against `text-gray-500` on its
   caption) and it is preserved rather than re-decided: the caption steps DOWN
   to `RdLabel`'s `--rd-fg3` and the value stays up here.

   ⚠️ `overflow-wrap: anywhere` IS LOAD-BEARING, not tidiness. A URL path has no
   spaces, so a long unmatched one is a single unbreakable word: without this it
   overflows the card and the chamfer clips it (`clip-path` clips content, per
   01-chamfer-box.css's own header), leaving the address — the whole point of
   the screen — cut off with no scroll to reach it.

   Not `RdText`: that role is `--rd-fg2` at 18 of 18 sites by design and exposes
   no colour prop, and it takes a `String` that could not carry this wrapping
   either. The same call `.rd-gone__note` records one drawing over, in the
   other direction. */
.rd-nf__path {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13px;
  line-height: 1.5;
  color: var(--rd-fg);
  margin: 0;
  overflow-wrap: anywhere;
}

/* The back door — `RDLink`, the accent as text. Values copied verbatim from
   `.rd-auth__link` (s02-auth.css), which is this role's canonical instance;
   page-scoped rather than shared because the role has no kit primitive and the
   corpus declined to give it one (`48 Kit Rules.html:91`: *"Component taxonomy
   belongs to the build, not the corpus."*). `.rd-chal-d__link`,
   `.rd-adm-lib__link` and `.rd-ev-danger__link` are the same clone.

   ⚠️ `--rd-acc`, NEVER `--rd-volt` — substrate §1.1: volt fills, `acc` strokes
   and marks text, and `--rd-volt` on a light-theme ground is an unreadable tan.
   This replaces `text-blue-600 hover:underline`, which was fixed-light AND a
   colour this system does not contain. No underline, ever; hover steps the
   colour to `--rd-fg`, exactly as `.rd-auth__link` does. */
.rd-nf__link {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  font-size: 11.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--rd-acc);
  text-decoration: none;
}

.rd-nf__link:hover { color: var(--rd-fg); }

/* The house focus ring — `.rd-lf__link` / `.rd-door__link` / `.rd-adm-lib__link`
   all spell it this way. This link is the ONLY control on the screen, so a
   keyboard reader who cannot see it has no way out at all. */
.rd-nf__link:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}
/* ==== SX-VERIFY · the email-verification refusal strip (UNDRAWN) · owner: lane-track8 ==== */

/* ---------------------------------------------------------------------------
   `verification.rs`'s `VerificationBlockedBanner` — the strip a gated screen
   mounts when the signed-in user's email is not verified, or when we could not
   ask. Six call sites (`drill_form`, `range_day_form`, `admin/org_settings`,
   `admin/org_transfer`, `admin/org_close`, `admin/members`), so it is a shared
   refusal surface rather than any one page's furniture.

   ⚠️ **UNDRAWN AS A COMPOSITION, AND ONLY AS A COMPOSITION.** The corpus draws
   no strip that carries controls inside it — swept 2026-08-26 across every
   `.html` and `.jsx` in the corpus. What the corpus DOES rule is the two
   things this part changes, and neither is decided here:

     - **The tone.** `19 (Mobile) Profile Pages:158` — *"Not verified is amber
       with 'Send a new link' beside it"* — and `:176` — *"The banner is grey,
       not red. Being offline is a machine state, not an error, and the
       four-rank system spends red on failures only."* The retired markup drew
       BOTH states in red, including the offline one whose copy is 19's own
       pinned sentence (`rd-profile-kit.jsx:120`, verbatim). So the strip is
       `RdBanner`'s Warning tone when the server said no and its Neutral tone
       when we could not ask, and the ground/edge/ink are `p1.css`'s, not this
       part's. Nothing below declares a banner colour.
     - **The corner.** `48 Kit Rules.html:33` — *"The kit is chamfered; radius
       is not a style option … A new rounded control is a defect, not a
       variant."* The retired strip and both of its buttons carried Tailwind's
       `rounded`, in no allowed round exception.

   ⚠️ **WHY THE ACTS SIT BELOW THE STRIP AND NOT INSIDE IT.** `RdBanner` takes a
   `message` and nothing else, deliberately — *"the component renders no copy of
   its own"* — and `p1.css`'s own banner block records that three page-local
   grey strips once grew against that absence and were retired the day the
   Neutral tone landed. Re-declaring the banner's ground here to get the buttons
   inside it would be the fourth. So the strip is the primitive, unmodified, and
   the acts are a row under it; the geometry that IS preserved from the retired
   markup is the one that had a caller depending on it — the 16px shelf below
   the whole block (`mb-4`).

   ⚠️ **NO GROUND, NO EDGE, NO COLOUR ON THE STRIP ITSELF.** Everything visual
   about the message belongs to `.rd-banner` + its tone modifier. This file owns
   stacking, the two acts' text treatment, and the sub-line under the resend.
   --------------------------------------------------------------------------- */

/* The block. A column because the strip is full-bleed in its caller's flow and
   the acts are inline-level: a row would leave them hanging off the strip's
   left edge with no relationship to it. `align-items: flex-start` keeps each
   act the width of its own label — an accent-text act stretched to the
   container is a button pretending to be a fill.

   `margin-bottom` was the retired `mb-4`, and it is `--rd-banner-gap` now: six
   callers place this strip above their form and none of them spaces it, so the
   shelf is this block's, and the number it spells is the kit's one block gap
   rather than a second 16.

   ⚠️ THE PRIMITIVE'S OWN GAP IS CANCELLED INSIDE THIS BLOCK, at the bottom of
   this file. `.rd-banner` carries `--rd-banner-gap` now (THE SLOT RULING,
   `p1.css` 2026-08-28) — which is right wherever the strip IS the notice, and
   wrong here, where the strip is the first row of a notice whose acts sit under
   it on an 8px gap. Left alone the strip would push its own acts 24px away and
   the block would read as two things. The block is the slot; the strip is a row
   in it. */
.rd-vblk {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  margin-bottom: var(--rd-banner-gap);
}

/* The resend act and its receipt line, kept together so the sub-line tracks the
   button rather than the block. */
.rd-vblk__resend {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}

/* The inline acts — accent as text, per `48 Kit Rules`' action tiers: *"quiet
   accent text (inline act)"*. Values verbatim from `.rd-auth__link`
   (s02-auth.css), this role's canonical instance, for the reason
   `sx-notfound.css` states when it makes the same clone: the role has no kit
   primitive and the corpus declined to give it one (`48 Kit Rules.html:91` —
   *"Component taxonomy belongs to the build, not the corpus."*).

   ⚠️ `--rd-acc`, NEVER `--rd-volt` — substrate §1.1. This replaces
   `bg-red-100 hover:bg-red-200 text-red-700 rounded`, which was a fixed-light
   filled pill: a volt-or-red FILL on a refusal act would also take the screen's
   one filled role away from the act the screen is actually for
   (`48 Kit Rules`: *"One volt-filled role per screen"*), which is the second
   reason this is text and not a button face.

   The button reset (`background: none; border: 0; padding: 0`) is
   `.rd-adm-lib__act`'s, the same shape every inline-act clone in the pool
   carries. */
.rd-vblk__act {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-decoration: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 700;
  font-stretch: 78%;
  font-size: 11.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--rd-acc);
}

.rd-vblk__act:hover:not(:disabled) { color: var(--rd-fg); }

/* Disabled is the grey rank and it means *you can't* — which is what both of
   this act's off states are: the throttle's wait, and a request in flight.
   ⚠️ `43 Loading States`' in-flight rule (*"In-flight controls keep their
   label"*) is NOT satisfied here and is not pretended to be: the copy still
   swaps to `Sending...`. Retiring that swap needs the sweep primitive, which
   this app has no component for yet — flagged, not smoothed over. */
.rd-vblk__act:disabled { color: var(--rd-fg3); cursor: default; }

.rd-vblk__act:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* The resend's receipt — *"Sent — check your inbox."* and the failure message.
   Meta rank (`--rd-fg2`), because it is about the act above it and not about
   the refusal above that. It was `text-xs` inheriting `text-red-700` inside the
   red box: a success sentence rendered in the failure colour. */
.rd-vblk__note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.45;
  color: var(--rd-fg2);
  margin: 0;
}

/* THE STRIP IS A ROW IN THIS BLOCK, NOT THE BLOCK. See `.rd-vblk`'s own note
   above: the primitive's `--rd-banner-gap` is the shelf under a notice, and
   here the notice is `.rd-vblk` — which carries that same token as its own
   bottom margin. Inside it the strip is spaced from the acts by the block's
   `gap: 8px`, drawn from the retired markup, so the strip's own shelf would be
   an extra 16 between a sentence and the button that answers it.

   ⚠️ CHILD COMBINATOR, AND ONLY THIS BLOCK. A bare `.rd-banner { margin: 0 }`
   anywhere in the pool is the reversal THE SLOT RULING was written against.
   This cancels the gap for one composition, at that composition's own edge, in
   that composition's own part. */
.rd-vblk > .rd-banner { margin-bottom: 0; }
/* ==== ADM1-WIZ · 52 (Desktop/Mobile) Events Lab, the wizard shell · owner: 1-track3 ==== */
/* The Pro event-creation wizard's own chrome — the 190/44 step rail, the phone's
   nine-segment strip and step line, the pinned footer and its three states
   (saved / offline / save-failed) — sections 1-4, slice S13 — plus the first
   three steps' own bodies: Basics in sections 3b and 5 (S14), its Fee block in
   3c and 5b (S14b, once S7 landed the wire), When beside each of those (S15),
   and Where in the two sections APPENDED AT THE END of this file (S16). Every
   remaining step's body is a later slice (S17-S23) and each brings its own
   rules to this part.

   ⚠️ WHERE'S TWO SECTIONS SIT AT THE END RATHER THAN IN STEP ORDER, and it is
   a rebase call rather than an oversight: S14b was in flight over sections 3b
   and 5 when S16 was written, and a step's worth of rules inserted between them
   would have put the two slices in the same hunks for no reader's benefit.
   Every section carries its step number in its own header, so the file reads in
   step order whatever order it is written in.

   Page-local, deliberately NOT a kit primitive, and the ban is written down.
   `apps/web/src/components/kit/hub_rail.rs:654`
   (`ruling_the_wizard_step_column_is_not_this_component`) refuses `step`,
   `ordinal`, `progress`, `done`, `complete` and `300px` on `RdHubRail`, and its
   reasoning is *"Forcing one component to serve two drawings is the second-copy
   hazard run in reverse."* C-52.3 (`design-reply-2026-08-26.md`) then ruled the
   Pro rail a deliberate SCALE against the shooter's: *"190/44 is the Pro
   console's density against the shooter's 300/62 — same reasoning as the
   standing ruling against forcing one rail component onto two drawings."*

   The precedent for where it lives is the shooter wizard's own rail, which is
   also page-local CSS: `.rd-af-tab__rail` in `s04-author.css` (`width: 300px`),
   mounted from `pages/drill_form.rs`. Same shape here, one console over.

   ⚠️ NO `@media` IN THIS FILE. The breakpoint is asked ONCE, in Rust
   (`device::use_device_class`, one call in `pages/admin/event_wizard.rs`, pinned
   by `exactly_one_use_device_class_call_in_this_file`), and these rules paint
   what that call mounted. The same call `adm0-mobile.css` and `adm-mob.css`
   make, and their order notes say why: a second statement of the threshold in
   CSS drifts against the Rust one and the two disagree at exactly the boundary
   width.

   ⚠️ ONE BORROW, NAMED: the footer's primary wears `.rd-pro-top__btn`
   (`_legacy-events-list.css`) for its volt fill and its 7px chamfer, and this
   part re-sizes it only. The drawing's primary is
   `{ background: t.volt, color: t.onVolt, padding: '10px 22px', …tcham(7) }`
   (`rd-events-lab.jsx:52` ("...tcham(7)")) — the same fill and the same chamfer
   the console's primary already carries, at a different padding. A second
   `clip-path` spelling for one corner is how the two drift apart; `adm1-4.css`
   makes the same argument from the other side (exactly ONE rule in the Pro
   family carries `clip-path`, and it is the primary action). */

/* ---------------------------------------------------------------------------
   1. DESKTOP — the frame. `rd-events-lab.jsx:20`
   ("flex: 1, minHeight: 0, display: 'flex'").
   --------------------------------------------------------------------------- */
.rd-wiz {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
}

/* ---------------------------------------------------------------------------
   2. DESKTOP — the step rail. `rd-events-lab.jsx:23`
   ("width: 190") — `borderRight: 1px t.line, padding: '20px 0', display: 'grid',
   gap: 2, alignContent: 'start'`.
   --------------------------------------------------------------------------- */
.rd-wiz__rail {
  width: 190px;
  flex: none;
  border-right: 1px solid var(--rd-line);
  padding: 20px 0;
  display: grid;
  gap: 2px;
  align-content: start;
}

/* One row. `rd-events-lab.jsx:25` ("minHeight: 44") — `display: flex,
   alignItems: center, gap: 12, padding: '0 20px', borderLeft: 2px`.

   A `button`, not a div, because it navigates — `drill_form.rs`'s rail says the
   ruling in words: *"The wizard stays a wizard. The rail makes it navigable,
   not optional."* The transparent left border is declared on the base rather
   than only on the current row: a border that appears with the state would move
   the label 2px sideways every time the step changed. */
.rd-wiz__step {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  padding: 0 20px;
  border: 0;
  border-left: 2px solid transparent;
  background: none;
  text-align: left;
  width: 100%;
  font-family: 'Archivo', system-ui, sans-serif;
  cursor: pointer;
}

.rd-wiz__step:disabled { cursor: default; }
.rd-wiz__step:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: -2px; }

/* The current row: accent edge + `surf2` ground (`rd-events-lab.jsx:25`). */
.rd-wiz__step--on {
  border-left-color: var(--rd-acc);
  background: var(--rd-surf2);
}

/* The ordinal. `rd-events-lab.jsx:26` ("fontSize: 10.5") — mono, `width: 12`,
   `t.acc` on the current row and `t.fg3` everywhere else. */
.rd-wiz__step-n {
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 10.5px;
  color: var(--rd-fg3);
  width: 12px;
  flex: none;
}

.rd-wiz__step--on .rd-wiz__step-n { color: var(--rd-acc); }

.rd-wiz__step-body {
  flex: 1;
  min-width: 0;
}

/* The step's name. 12.5px; weight 600 and `t.fg` on the current row, `t.fg2`
   once passed, `t.fg3` while still ahead (`rd-events-lab.jsx:28`). */
.rd-wiz__step-name {
  display: block;
  font-size: 12.5px;
  font-weight: 400;
  color: var(--rd-fg3);
}

.rd-wiz__step--on .rd-wiz__step-name { font-weight: 600; color: var(--rd-fg); }
.rd-wiz__step--done .rd-wiz__step-name { color: var(--rd-fg2); }

/* `--ahead` states its own grey rather than inheriting the base's, and that is
   deliberate: the three rail states are one machine (`RailRowState`), and a
   state with no rule of its own is a state the next reader has to go and prove
   is intentional. `t.fg3` is what the drawing paints an unreached step
   (`rd-events-lab.jsx:28`, the `i < step ? t.fg2 : t.fg3` tail). */
.rd-wiz__step--ahead .rd-wiz__step-name { color: var(--rd-fg3); }

/* The rail's honesty. `rd-events-lab.jsx:29` ("Optional") — five of the nine
   steps print it, and the source's own comment says why: *"only Basics, When,
   Where are demands — the rail says so, so the nine steps read as three
   requirements and six offers."* */
.rd-wiz__step-opt {
  display: block;
  font-size: 9.5px;
  color: var(--rd-fg3);
  margin-top: 1px;
}

/* The tick a passed step earns. `rd-events-lab.jsx:31` ("&#10003;"). */
.rd-wiz__step-tick {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
  flex: none;
}

/* ---------------------------------------------------------------------------
   3. DESKTOP — the pane, the body and the footer.
   --------------------------------------------------------------------------- */
.rd-wiz__pane {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* `rd-events-lab.jsx:36` ("padding: '24px 32px'") — `maxWidth: 700`. */
.rd-wiz__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 24px 32px;
  max-width: 700px;
  display: flex;
  flex-direction: column;
}

/* The quiet block quote each step opens with — `borderLeft: 2px t.line,
   paddingLeft: 14, maxWidth: '58ch'` (`rd-events-lab.jsx:229`
   ("borderLeft: `2px solid ${t.line}`")). */
.rd-wiz__intro {
  border-left: 2px solid var(--rd-line);
  padding-left: 14px;
  max-width: 58ch;
  margin: 0;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   3b. DESKTOP — STEP 1, Basics. `rd-events-lab.jsx:66` ("function WizBasics"),
   the step's own grid at `rd-events-lab.jsx:69` ("gap: 18").

   ⚠️ THE DRAWN BOXES ARE ALL IN THEIR *FILLED* STATE AND THE RESTING RULE HERE
   IS THE EMPTY ONE. `wb(t, on)` (`rd-events-lab.jsx:61` ("const wb")) has two
   arms — `on` takes `t.surf2` under a `t.fg` border, `off` takes `t.field`
   under `t.ctrl` — and every box in `WizBasics` is drawn `on`, because a mock
   with sample content in it has nothing to show in the other state. A real
   field is empty most of the time, so the resting face is the `off` arm and the
   `on` arm is `:focus`. The same call `RdTextField` makes for the field beside
   this one, and the same reasoning `text_field.rs` gives for not reproducing
   the drawn ten-bullet mask: a drawing convention is not a state machine.

   ⚠️ NO CHAMFER ON EITHER BOX. `adm-drillf.css` states the family rule this
   part already follows for `Back` and `Keep As Draft` — the chamfer is the Pro
   family's mark for *"this is THE action"*. The drawing agrees on its own
   terms: `tcham` appears six times in `rd-events-lab.jsx` and not once inside
   `WizBasics`. `RdTextField` brings its own 12, which is the kit primitive's
   business and not this part's.
   --------------------------------------------------------------------------- */
.rd-wiz__basics {
  display: grid;
  gap: 18px;
}

/* The copy-a-past-event door — `rd-events-lab.jsx:72` ("1px dashed"),
   `padding: '11px 14px'`, `gap: 12`.

   ⚠️ THE ROW IS NOT THE CONTROL — the WORD is. S22 gave the act its
   destination (frame 1b), so `.rd-wiz__copydoor-act` below is a `button` and
   carries the pointer; this container stays a `div` with no cursor and no
   hover, exactly as `.rd-wiz__gearrow` does for the same reason. A dashed
   rectangle that lights up under the mouse claims a hit area four times the
   size of the thing that answers. */
.rd-wiz__copydoor {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  border: 1px dashed var(--rd-ctrl);
}

.rd-wiz__copydoor-txt {
  flex: 1;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
}

/* `pdisp(700, 78, '.08em')` at 9px in `t.acc` (`rd-events-lab.jsx:74`
   ("Copy a past event")).

   ⚠️ THIS ACT TAPS — it opens frame 1b. So it takes the button reset,
   `cursor: pointer` and a non-visual hit-area extension, which is
   `.rd-wiz__gearact`'s shape verbatim one step down the file and the kit's own
   ruling (`components/kit/button.rs`, `hit_target_height_px`): *"platform
   minimums are met by non-visual hit-area extension; the painted geometry stays
   the designer's."* The extension is padding plus a negative margin, so the
   drawn 11px row height is unchanged. */
.rd-wiz__copydoor-act {
  flex: none;
  border: 0;
  background: none;
  cursor: pointer;
  padding: 11px 0 11px 12px;
  margin: -11px 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9px;
  color: var(--rd-acc);
}

.rd-wiz__field {
  display: block;
}

/* `wl(t)` — `pdisp(700, 78, '.11em')`, 9px, `t.fg3`, `marginBottom: 8`
   (`rd-events-lab.jsx:60` ("const wl")). */
.rd-wiz__label {
  display: block;
  margin-bottom: 8px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .11em;
  font-size: 9px;
  color: var(--rd-fg3);
}

/* The `— optional` tail, drawn as its own span at a looser tracking
   (`rd-events-lab.jsx:83` ("— optional")). Same colour as the label it sits in,
   which the drawing restates explicitly rather than inheriting. */
.rd-wiz__label-opt {
  letter-spacing: .06em;
  color: var(--rd-fg3);
}

/* The description box — `wb` plus `minHeight: 74, lineHeight: 1.5`
   (`rd-events-lab.jsx:84` ("minHeight: 74")).

   A native `textarea` by ruling: `docs/design/kit/AU5-text-field.md:218-220`
   puts a multiline variant out of `RdTextField`'s scope, so the element is
   native and the face is this part's. `org_settings.rs` and `event_form.rs`
   make the same departure. */
.rd-wiz__area {
  display: block;
  width: 100%;
  box-sizing: border-box;
  min-height: 74px;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  padding: 11px 13px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--rd-fg);
  resize: vertical;
}

.rd-wiz__area::placeholder { color: var(--rd-fg3); }

/* The `on` arm of `wb`, spent where a real field actually changes state — see
   the section head. `caret-color` is the kit's own translation of the drawn 2px
   accent caret bar (`text_field.rs` states the argument in full). */
.rd-wiz__area:focus {
  outline: none;
  background: var(--rd-surf2);
  border-color: var(--rd-fg);
  caret-color: var(--rd-acc);
}

/* The hint under Description — `fontSize: 10.5, marginTop: 6, lineHeight: 1.45`
   (`rd-events-lab.jsx:85` ("Shown on the event page")). DESKTOP ONLY: the phone
   drawing has the label and the box and stops. */
.rd-wiz__hint {
  margin: 6px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.45;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   3c. DESKTOP — STEP 1, the Fee block (S14b). `rd-events-lab.jsx:88` (">Fee<")
   through `:98` ("Collected outside the app").

   ⚠️ THESE CELLS ARE `wb()` BOXES, NOT THE KIT'S SEGMENTED CONTROL. The drawn
   cell is the same two-arm box the Name and Description fields wear — a
   hairline over `t.field`, `t.surf2` under a `t.fg` border when it is the one
   chosen — with NO chamfer and NO volt fill. `RdSegmented` paints a volt slab at
   chamfer 9, which is a different control; section 3b already states that
   `tcham` appears six times in `rd-events-lab.jsx` and not once inside
   `WizBasics`, and the Rust side gives the other two reasons (the kit's own doc
   rules the primitive out in front of a save, and the Pro console substitutes
   its own segmented idiom for the shooter kit's).

   The block's foot reuses `.rd-wiz__hint` — the drawing gives it the same
   10.5/1.45/`marginTop: 6` face as the Description hint (`:98` against `:85`),
   so it is one rule with two callers rather than a second copy of five values.
   --------------------------------------------------------------------------- */

/* The Free/Paid pair — `rd-events-lab.jsx:89` ("maxWidth: 360"), `gap: 8`. */
.rd-wiz__feeseg {
  display: flex;
  gap: 8px;
  max-width: 360px;
}

/* The collection row inside the amount row, which is `flex: 1` there and has no
   cap of its own (`rd-events-lab.jsx:94` ("gap: 8, flex: 1")). One modifier on
   the same row, so the two segmented rows stay one control with one stated
   variation. */
.rd-wiz__feeseg--wide {
  flex: 1;
  max-width: none;
}

/* One cell. `wb(t, 0)` plus the drawn cell overrides `flex: 1, textAlign:
   'center', fontSize: 12.5` (`rd-events-lab.jsx:90` ("Free Event")). */
.rd-wiz__feeopt {
  flex: 1;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  padding: 11px 13px;
  cursor: pointer;
  text-align: center;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg3);
}

/* `wb(t, 1)` — the chosen cell. The modifier carries only what changes, so the
   two states cannot drift apart in three independent declarations. */
.rd-wiz__feeopt--on {
  background: var(--rd-surf2);
  border-color: var(--rd-fg);
  color: var(--rd-fg);
}

/* No hover branch: the drawing gives these cells none, and an invented one would
   compete with the chosen cell for "which of these is in force". Focus is the
   rail's, so a keyboard operator sees the same ring on both controls. */
.rd-wiz__feeopt:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* The amount + methods row — `rd-events-lab.jsx:92` ("gap: 16"),
   `alignItems: 'flex-start', marginTop: 10`. */
.rd-wiz__feerow {
  display: flex;
  gap: 16px;
  align-items: flex-start;
  margin-top: 10px;
}

/* The amount box. `wb(t, 1)` at `width: 110, flex: 'none'`
   (`rd-events-lab.jsx:93` ("width: 110")).

   ⚠️ NO `box-sizing` HERE, DELIBERATELY. The drawing is React inline styles with
   no reset, so its `width: 110` is the CONTENT width and the 11/13 padding sits
   outside it. Declaring `border-box` would reproduce the number and not the box.
   `.rd-wiz__area` above sets `border-box` because it is `width: 100%`, which is
   the opposite need.

   The box wears the border and the input inside it wears none — one drawn box,
   one owner, and the `$` sits inside it as a glyph rather than as typed text. */
.rd-wiz__amount {
  flex: none;
  display: flex;
  align-items: center;
  gap: 4px;
  width: 110px;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  padding: 11px 13px;
}

.rd-wiz__amount-cur {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg3);
}

/* ⚠️ A TWO-CLASS COMPOUND, AND THAT IS THE DEFENCE RATHER THAN A NESTING HABIT.
   The pre-kit backfill styles bare inputs BY ATTRIBUTE — `_legacy-base.css`
   gives `input[type="number"]` a `#d1d5db` border and `input:focus` a `#3b82f6`
   ring, both (0,1,1) — so an undefended field here would paint the one hue the
   kit refuses to name. This compound is (0,2,0) and outranks it. The idiom, and
   the reason it is not fixed in the backfill itself, are `adm-drillf.css`'s
   `.rd-drillf__control` and issue #617. */
.rd-wiz__amount .rd-wiz__amount-input {
  width: 100%;
  min-width: 0;
  border: 0;
  background: none;
  box-shadow: none;
  padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
  /* The drawn box holds `$25` and nothing else — the browser's stepper arrows
     would be OS chrome inside a box the design already draws. Same call
     `adm-drillf.css` makes for its four selects. */
  appearance: none;
  -moz-appearance: textfield;
}

.rd-wiz__amount .rd-wiz__amount-input::-webkit-outer-spin-button,
.rd-wiz__amount .rd-wiz__amount-input::-webkit-inner-spin-button {
  appearance: none;
  -webkit-appearance: none;
  margin: 0;
}

.rd-wiz__amount .rd-wiz__amount-input::placeholder { color: var(--rd-fg3); }

/* The `on` arm again, spent where the field actually changes state — the same
   call section 3b makes for the description box, and it lands on the WRAPPER
   because the wrapper is the box the drawing draws. */
.rd-wiz__amount:focus-within {
  background: var(--rd-surf2);
  border-color: var(--rd-fg);
}

.rd-wiz__amount .rd-wiz__amount-input:focus {
  outline: none;
  box-shadow: none;
  caret-color: var(--rd-acc);
}

/* ---------------------------------------------------------------------------
   STEP 2 · WHEN — `WizWhen` (`rd-events-lab.jsx:145`).

   Two boxes and a block quote. There is NO third control and there must not be
   one: the `Days` segmented toggle an earlier revision drew was deleted 26 Aug
   (James, C-52.1), which reaffirms the 17 Aug one-shape ruling — Ends empty IS
   the single-day case. `event_wizard.rs` pins the absence.
   --------------------------------------------------------------------------- */
.rd-wiz__when {
  display: grid;
  gap: 18px;
}

/* `display: flex, gap: 16, maxWidth: 500` (`rd-events-lab.jsx:147`
   ("maxWidth: 500")), the two fields sharing it equally. */
.rd-wiz__daterow {
  display: flex;
  gap: 16px;
  max-width: 500px;
}

.rd-wiz__daterow .rd-wiz__field { flex: 1; }

/* `wb(t, 0)` — the EMPTY arm's ground (`rd-events-lab.jsx:61` ("const wb")):
   `t.field` behind a `t.ctrl` border, `padding: '11px 13px'`, 13.5px.

   ⚠️ **TWO CLASSES, NOT ONE, AND IT IS THE `input:focus` RULE THAT MAKES IT
   NECESSARY.** `_legacy-pre-kit.css` carries a bare
   `input:focus { box-shadow: 0 0 0 2px #3b82f6; border-color: #3b82f6 }` — a
   deliberate pin, kept for the unmigrated pages — at specificity (0,1,1). A
   single-class `.rd-wiz__date:focus` is (0,1,1) too and would win only on
   source order, which is exactly the fragility `adm1-4.css` wrote its own
   two-class note about after a box silently opted out of the design system in
   both themes with both screenshots looking fine. `.rd-wiz .rd-wiz__date` is
   (0,2,0) / (0,2,1) and wins on specificity instead.

   (`input[type="date"]` is NOT in the legacy attribute list — only text, email,
   password, number, textarea and select are — so the base rule is safe either
   way. The pair is written the same way regardless, because the reader should
   not have to check which types are on that list.)

   `appearance: none` on the box, never on the indicator: the calendar button is
   an OS-owned control and 48's geometry rule carves the exception for exactly
   it. */
.rd-wiz .rd-wiz__date {
  display: block;
  width: 100%;
  box-sizing: border-box;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  border-radius: 0;
  padding: 11px 13px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 400;
  font-size: 13.5px;
  line-height: 1.2;
  color: var(--rd-fg3);
  outline: none;
  appearance: none;
  -webkit-appearance: none;
}

/* The `on` arm of `wb` — a box with a date in it. The Rust side picks the arm,
   because a native date input cannot report its own emptiness to CSS. */
.rd-wiz .rd-wiz__date--on {
  background: var(--rd-surf2);
  border-color: var(--rd-fg);
  color: var(--rd-fg);
}

.rd-wiz .rd-wiz__date:focus {
  box-shadow: none;
  border-color: var(--rd-acc);
  caret-color: var(--rd-acc);
}

/* The quiet block quote — `borderLeft: 2px solid t.line, paddingLeft: 14,
   maxWidth: '58ch'` (`rd-events-lab.jsx:151` ("borderLeft")). A rule the eye
   skips past, which is the point: the two sentences answer a question the
   organizer may not have asked. */
.rd-wiz__quote {
  border-left: 2px solid var(--rd-line);
  padding-left: 14px;
  max-width: 58ch;
}

/* `fontSize: 12.5, color: t.fg, lineHeight: 1.55`
   (`rd-events-lab.jsx:152` ("Just the date for now.")). */
.rd-wiz__quote-lead {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--rd-fg);
}

/* `fontSize: 11.5, color: t.fg3, lineHeight: 1.55, marginTop: 5`
   (`rd-events-lab.jsx:153` ("Leave Ends empty for a single day.")). */
.rd-wiz__quote-body {
  margin: 5px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--rd-fg3);
}

/* The save-failure strip, ABOVE the footer and never inside it —
   `rd-events-lab.jsx:37` ("border: '1px solid #FF5A3C'"), `margin: '0 32px',
   padding: '10px 14px'`. The drawn hex IS `--rd-danger` in the dark theme
   (`_legacy-tokens.css` declares the token as that value), so the token is what
   goes here — and it is the token rather than the hex precisely because the
   light theme resolves it to a different, darker red that the drawing, which is
   drawn dark, never shows. */
.rd-wiz__err {
  flex: none;
  margin: 0 32px;
  padding: 10px 14px;
  border: 1px solid var(--rd-danger);
  display: flex;
  align-items: center;
  gap: 14px;
}

.rd-wiz__err-msg {
  font-size: 11.5px;
  color: var(--rd-danger);
  flex: 1;
}

/* `Retry`, accent, display face (`rd-events-lab.jsx:37`). */
.rd-wiz__retry {
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9.5px;
  color: var(--rd-acc);
}

/* The footer. `rd-events-lab.jsx:38` ("padding: '14px 32px 22px'") — one row,
   `gap: 12`, hairline over. */
.rd-wiz__foot {
  flex: none;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 32px 22px;
  border-top: 1px solid var(--rd-line);
}

.rd-wiz__spacer { flex: 1; }

/* `Back` and `Keep As Draft` — outlined, `t.ctrl`, no fill
   (`rd-events-lab.jsx:39` ("Back"), `:51`). NOT chamfered: `adm-drillf.css`
   states the family rule this follows — the chamfer is the Pro family's mark
   for *"this is THE action"*, and spending it on a second act spends the signal
   that makes the primary findable. */
.rd-wiz__act {
  flex: none;
  display: inline-block;
  border: 1.5px solid var(--rd-ctrl);
  background: none;
  color: var(--rd-fg);
  padding: 10px 18px;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 11px;
}

.rd-wiz__act:hover { border-color: var(--rd-fg3); }
.rd-wiz__act:disabled { opacity: .5; cursor: default; }

/* `Close`, plain text, present on every step (`rd-events-lab.jsx:42`
   ("Close")). A button because it acts; drawn as a word, so it wears no box. */
.rd-wiz__close {
  flex: none;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
}

.rd-wiz__close:hover { color: var(--rd-fg2); }

/* `Saved as draft` (`rd-events-lab.jsx:45`) and the offline sentence
   (`:46`) — the second is a step brighter, `t.fg2` against `t.fg3`, because it
   is the one the reader has to act on. */
.rd-wiz__saved { flex: none; font-size: 11px; color: var(--rd-fg3); }
.rd-wiz__offline { flex: none; font-size: 11px; color: var(--rd-fg2); }

/* The step's own grey line, right of the footer (`rd-events-lab.jsx:48`). */
.rd-wiz__stepfoot {
  font-size: 11.5px;
  color: var(--rd-fg3);
  text-align: right;
}

/* The primary. Wears `.rd-pro-top__btn` for the fill and the chamfer (see the
   borrow note at the head of this file); this re-sizes it to the drawn
   `padding: '10px 22px'` / 11px and carries the offline dim,
   `opacity: state === 'offline' ? .45 : 1` (`rd-events-lab.jsx:52`). */
.rd-wiz__next {
  flex: none;
  border: 0;
  cursor: pointer;
  padding: 10px 22px;
  font-size: 11px;
}

.rd-wiz__next--dim { opacity: .45; }
.rd-wiz__next:disabled { cursor: default; }

/* ---------------------------------------------------------------------------
   3d. DESKTOP — STEP 4, What. `rd-events-lab.jsx:194` ("function WizWhat"),
   the step's head row at `rd-events-lab.jsx:198` ("alignItems: 'baseline'").

   ONE LIST, and the drawing says why in its own comment: *"Drills added singly
   and stages loaded from a course land in the same list; the course tag says
   where a row came from."* The tag is a client-side label — `event_wizard.rs`'s
   `FROM_TAG_CAPTION` carries the ruling and the caption the screen prints.
   --------------------------------------------------------------------------- */
/* The step's children stack the way `WizShell`'s body stacks them — a flex
   column with no gap (`rd-events-lab.jsx:36` ("padding: '24px 32px'")). The
   drawn spacing is on the parts: the label's own `marginBottom: 8`, the empty
   arm's `paddingTop: 26`, the quote's `marginTop: 18`. */
.rd-wiz__what {
  display: flex;
  flex-direction: column;
}

/* `display: flex, alignItems: 'baseline', justifyContent: 'space-between'`
   (`rd-events-lab.jsx:198` ("alignItems: 'baseline'")) — the label and the two
   acts on one line, sitting on the same baseline rather than centred, which is
   what keeps a 9px act aligned to a 9px label. */
.rd-wiz__whathead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

/* `display: flex, gap: 16` (`rd-events-lab.jsx:201` ("gap: 16")). */
.rd-wiz__whatacts {
  display: flex;
  gap: 16px;
}

/* `pdisp(700, 78, '.08em')` at 9px in `t.acc` (`rd-events-lab.jsx:202`
   ("Add a drill")) — the same face `.rd-wiz__copydoor-act` carries, and the
   same 9px this part already ships for `.rd-wiz__label` rather than the 9.5
   floor. A button because it opens something; drawn as a word, so it wears no
   box. */
.rd-wiz__whatact {
  flex: none;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9px;
  color: var(--rd-acc);
}

.rd-wiz__whatact:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* Frame 4 — `maxWidth: '54ch', paddingTop: 26` (`rd-events-lab.jsx:208`
   ("maxWidth: '54ch'")). NOT `RdEmptyState`: that primitive centres on both
   axes at a 420 bound and has no action slot by ruling, and this arm is a
   left-aligned block whose whole point is the two doors under the sentence.
   `event_wizard.rs`'s `ADD_DRILL_DOOR` states it in full. */
.rd-wiz__whatempty {
  max-width: 54ch;
  padding-top: 26px;
}

/* `pdisp(800, 75, '.02em')` at 17px, `t.fg` (`rd-events-lab.jsx:209`
   ("Nothing on the schedule yet")). Uppercase by construction — the transform
   is in the display helper, which is why the source string is a sentence. */
.rd-wiz__whatempty-head {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 75%;
  letter-spacing: .02em;
  font-size: 17px;
  color: var(--rd-fg);
}

/* `fontSize: 12.5, color: t.fg3, marginTop: 10, lineHeight: 1.6`
   (`rd-events-lab.jsx:210` ("Add drills one at a time")). */
.rd-wiz__whatempty-body {
  margin: 10px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--rd-fg3);
}

/* `display: flex, gap: 10, marginTop: 18` (`rd-events-lab.jsx:211`
   ("gap: 10")). */
.rd-wiz__whatempty-acts {
  display: flex;
  gap: 10px;
  margin-top: 18px;
}

/* The volt door — `padding: '10px 18px'`, 11px, `tcham(7)`
   (`rd-events-lab.jsx:212` ("Add A Drill")). Wears `.rd-pro-top__btn` for the
   fill and the chamfer, exactly as `.rd-wiz__next` does; this re-sizes it to
   the drawn 18 where the footer's primary takes 22. TWO acts, ONE chamfer: the
   second door is `.rd-wiz__act`, whose own note says the chamfer is the Pro
   family's mark for *"this is THE action"*.

   The picker's commit wears this same rule — same drawn padding, same 11px,
   same fill (`rd-events-lab.jsx:267` ("Add 2 Drills")). One door, two mounts,
   rather than two rules that must stay equal. */
.rd-wiz__door {
  flex: none;
  border: 0;
  cursor: pointer;
  padding: 10px 18px;
  font-size: 11px;
}

.rd-wiz__door:disabled { cursor: default; opacity: .5; }

/* Frame 4a — `display: grid, gap: 6` (`rd-events-lab.jsx:217` ("gap: 6")). */
.rd-wiz__picked {
  display: grid;
  gap: 6px;
}

/* One picked row — `padding: '12px 16px', background: t.field,
   border: 1px solid t.ctrl, gap: 14` (`rd-events-lab.jsx:219`
   ("padding: '12px 16px'")).

   ⚠️ NOT `RdRow`, AND NOT A HAND-ROLLED TABLE ROW EITHER. `RdRow` is the PHONE
   fold of a desktop table — `--rd-surf` ground, chamfer 10, `12px 14px`, a
   name/sub stack with a verdict edge — and this row is drawn at BOTH widths on
   the `field` ground behind a square `ctrl` hairline, which is the drawn FIELD
   family rather than the row family. The name avoids the `__row` / `__head`
   taxonomy `components/kit/pro_table.rs` sweeps admin pages for, so that pin
   keeps measuring what it is about. */
.rd-wiz__pickedrow {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 16px;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
}

/* `flex: 1, minWidth: 0` (`rd-events-lab.jsx:220`) — the name gives way and
   `Remove` never compresses. */
.rd-wiz__pickedmain {
  flex: 1;
  min-width: 0;
}

/* `pbody` at 13.5, `t.fg` (`rd-events-lab.jsx:221`). */
.rd-wiz__pickedname {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
}

/* `From <course>` — `fontSize: 10.5, color: t.fg3, marginTop: 3`
   (`rd-events-lab.jsx:222` ("From {from}")). The provenance line, and the
   caption under the list says it is this browser's note. */
.rd-wiz__pickedfrom {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
}

/* `pbody` at 12, `t.fg3` (`rd-events-lab.jsx:224` ("Remove")). A button
   because it acts; drawn as a word, so no box. */
.rd-wiz__remove {
  flex: none;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
}

.rd-wiz__remove:hover { color: var(--rd-fg2); }
.rd-wiz__remove:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* The step's quote and the gap above it — a two-class compound so the shared
   `.rd-wiz__quote` rule, which is this same block quote for the steps that
   print it alone, keeps its own geometry.

   ⚠️ **THE COMPOUND NAMED `.rd-wiz__intro` AND MATCHED NOTHING** — the desktop
   half of the defect the phone twin records at §7 (`.rd-wizm__what`). This step
   mounts `.rd-wiz__quote`; `.rd-wiz__intro` is the catch-all paragraph for a
   step with no body of its own, mounted in the fallthrough arm and never inside
   `.rd-wiz__what`. So the drawn 18 sat on a selector the step does not produce
   and the quote printed flush against the act row above it — the empty arm's
   `Add a drill` / `Load a course` doors (`.rd-wiz__whatempty-acts`) at zero
   gap.

   ⚠️ **AND THE GAP IS THE KIT'S BLOCK GAP, NOT THE DRAWN 18** (James,
   rehearsal item 19: *"give it the kit block gap … the same token the banner
   slot uses"*). `--rd-banner-gap` is declared in `p1.css` (`:root` 16px, 20px
   from the 1024 breakpoint up) and its own THE SLOT RULING is the argument for
   spending it here: a block gap every caller respells is a gap no caller owns —
   that part counted twenty-seven spellings of one gap before the token existed.
   18 and 20 are the same gap drawn and tokenised; the token is the one a reader
   can find.

   Declared on the QUOTE's top rather than on the act row's bottom, which is
   what the ruling describes visually: the act row is the neighbour only in the
   EMPTY arm. In the filled arm the two acts sit in `.rd-wiz__whathead` at the
   TOP of the step and the quote follows the picked list, so a bottom margin on
   the act row would space the head from the list and leave the quote flush —
   the gap belongs to the block that must never touch what precedes it. */
.rd-wiz__what .rd-wiz__quote {
  margin-top: var(--rd-banner-gap);
}

/* ---------------------------------------------------------------------------
   3e. DESKTOP — FRAMES 4b / 4c / 11d, the picker. `rd-events-lab.jsx:240`
   ("function WizWhatPick").

   ⚠️ PAGE-LOCAL, NOT A KIT PRIMITIVE, and the reasoning is the one the corpus
   already accepted for the step rail: `hub_rail.rs:647-649` — *"Forcing one
   component to serve two drawings is the second-copy hazard run in reverse."*
   Two drawings want a picker (52 and 56), at different widths, in different
   apps. If the kit owner wants it extracted, `kit-implementation-contract.md`
   rules that spec-doc-first and it is its own slice.

   ONE MODAL, TWO POPULATIONS. The only differences are the mark's shape and
   what a press does to the selection — square + add for drills, round +
   replace for a course (James, 26 Aug: *"selector shape follows the selector's
   nature"*). Both live in Rust, in `pick_box_class` and `toggle_staged`, so the
   look and the behaviour cannot drift apart.

   ⚠️ SQUARE PANEL, AND THAT IS DRAWN. `48 Kit Rules.html:33` chamfers the kit
   and this panel takes none, because the drawing gives it a plain
   `border: 1px solid t.line` (`rd-events-lab.jsx:248` ("width: 520")) — exactly
   what the shipped Pro confirm already is (`.rd-confirm__panel`, adm1-5.1). The
   one chamfer in this modal rides the volt commit through `.rd-pro-top__btn`,
   which is the family's mark for the action.
   --------------------------------------------------------------------------- */
/* FIXED, not absolute — the same ruling `adm1-5-1.css` records at length for
   `.rd-confirm`: an absolutely-positioned scrim resolves against whichever
   ancestor happens to be positioned, and in this console that is a scroller
   that starts a third of the way down the glass. A modal fences the whole
   surface, so it centres on the surface. Same `z-index: 40` as that scrim; the
   two never coexist (the wizard mounts no confirm — `ruling_closing_the_wizard_never_asks`). */
.rd-wiz__pickwrap {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* `background: 'rgba(6,6,6,.82)'` (`rd-events-lab.jsx:247` ("rgba(6,6,6,.82)"))
   — the value `.rd-confirm` already carries, so the console dims to one depth.

   A real `button` rather than a tappable `div`: tapping out is the drawn
   dismissal (James, 26 Aug) and a dismissal no keyboard can reach is a trap.
   `range_day_form.rs`'s sheet made the same call. */
.rd-wiz__pick-scrim {
  position: absolute;
  inset: 0;
  border: 0;
  padding: 0;
  background: rgba(6, 6, 6, .82);
  cursor: pointer;
}

/* `width: 520, background: t.surf, border: 1px solid t.line`
   (`rd-events-lab.jsx:248` ("width: 520")). The two bounds are ours: a drawn
   frame has a fixed height and a real library does not.

   ⚠️ **THE MODAL IS BOUNDED TO THE VIEWPORT AND ITS LIST IS THE ONLY SCROLLER**
   (James, rehearsal item 20). It used to be one scroller — `overflow-y: auto`
   on this panel — which was true to the cap and wrong about the FOOT: the org
   library plus the public catalogue (the 28 Aug ruling) makes a list far longer
   than the drawn four rows, and a panel that scrolls as a whole carries `Add N
   Drills` off the bottom edge with it. The reader then scrolls a list to reach
   the button that commits the list.

   So: a flex column, capped here; the head and the search stay pinned at the
   top, `.rd-wiz__pick-list` takes the slack and scrolls, and
   `.rd-wiz__modal-acts` is pinned visible at the bottom whatever the list
   length. The foot pattern is `.rd-runsh__foot`'s (adm-run-sheet.css §4-5) —
   *"the head and the foot are `flex: none`, so a taller device gives its extra
   pixels [to the pad] and the foot stays on the foot"* — and `RdConfirmModal`'s.

   ⚠️ THE INSET IS OURS AND THE DRAWING SAYS SO. `rd-events-lab.jsx:247` centres
   the panel in a `position: absolute; inset: 0` scrim with `alignItems:
   'center'` and no padding, and `:248` gives it a WIDTH and no height — the
   drawn frame is fixed-height, so there is no drawn maximum to transcribe. 64
   is this rule's own bound, unchanged, and the twin of the 32 above it.

   ⚠️ `100dvh` OVER `100vh`, WITH `100vh` LEFT ABOVE IT AS THE FALLBACK — the
   pair and the argument `adm0-chrome.css` §1 and `kit-tablet-shell.css` state
   at length: `100vh` is the URL-bar-collapsed height, so a modal capped on it
   is taller than the glass on a phone-sized window and puts its own foot under
   the browser chrome — the defect this change exists to remove. Note that a
   HORIZONTAL viewport unit has no such twin: `100vw` above stays as it is. */
.rd-wiz__modal {
  position: relative;
  width: 520px;
  max-width: calc(100vw - 32px);
  max-height: calc(100vh - 64px);
  max-height: calc(100dvh - 64px);
  display: flex;
  flex-direction: column;
  background: var(--rd-surf);
  border: 1px solid var(--rd-line);
}

/* `padding: '20px 22px 14px', borderBottom: 1px t.line`
   (`rd-events-lab.jsx:249` ("padding: '20px 22px 14px'")). */
.rd-wiz__modal-head {
  flex: none;
  padding: 20px 22px 14px;
  border-bottom: 1px solid var(--rd-line);
}

/* `pdisp(800, 75, '.02em')` at 17, `t.fg` (`rd-events-lab.jsx:250`
   ("Load a course")). Sentence case in the source, uppercase in the helper. */
.rd-wiz__modal-title {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 75%;
  letter-spacing: .02em;
  font-size: 17px;
  color: var(--rd-fg);
}

/* `fontSize: 12, color: t.fg3, marginTop: 8` (`rd-events-lab.jsx:251`
   ("From your org library")). */
.rd-wiz__modal-sub {
  margin-top: 8px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
}

/* `padding: '12px 22px', borderBottom: 1px t.line` (`rd-events-lab.jsx:253`
   ("Search courses")). The drawing paints one line of grey placeholder there;
   what mounts inside is the kit's own `RdSearchField`, at its own chamfer,
   because the console searches with one control everywhere. */
.rd-wiz__modal-search {
  flex: none;
  padding: 12px 22px;
  border-bottom: 1px solid var(--rd-line);
}

/* THE ONE SCROLLER IN THE MODAL — rehearsal item 20; `.rd-wiz__modal` above
   carries the whole ruling.

   `flex: 1 1 auto` so the list is the block that absorbs a long library and a
   short viewport, and `min-height: 0` beside the `overflow` because without it
   a flex child's automatic minimum is its CONTENT height: the list would refuse
   to shrink, the panel would grow past its own cap, and the foot would leave
   the screen with the modal still nominally bounded. The pair is `pro/shell.rs`'s
   own sentence for a bounded region — *"a region inside `.rd-pro-page` scrolls
   by being bounded — `flex: 1; min-height: 0; overflow: auto`"* — and this
   selector is named in its `SCROLL_ALLOWLIST` with that reason.

   It is a DIRECT child of `.rd-wiz__modal` in all three mounts the panel serves
   (the drill/course picker, the itinerary's Add-entry form, the copy-a-past-event
   picker), which is what lets one rule pin every wizard picker's foot. */
.rd-wiz__pick-list {
  display: block;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

/* One pickable row — `padding: '12px 22px', gap: 12,
   borderBottom: 1px t.line` (`rd-events-lab.jsx:260` ("padding: '12px 22px'")).
   A full-width `button`, because a row that toggles is an act. */
.rd-wiz__pick {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 22px;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  background: none;
  text-align: left;
  cursor: pointer;
}

/* `background: on ? t.surf2 : 'transparent'` (`rd-events-lab.jsx:260`) — the
   picked row is the only one tinted. */
.rd-wiz__pick--on {
  background: var(--rd-surf2);
}

.rd-wiz__pick:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* The mark — `width: 14, height: 14, border: 1.5px solid`, accent-filled when
   on (`rd-events-lab.jsx:261` ("borderRadius: course")).

   Presentational only: what a screen reader is told is the row button's
   `aria-pressed`, which is why the span is empty and unlabelled. The shipped
   shooter picker's `.rd-plan__pick-box` is the same element one app over, and
   `rd-cb__pick-box` a third; each part owns its own rule at its own drawn size
   rather than reaching across for another part's class. */
.rd-wiz__pickbox {
  flex: none;
  width: 14px;
  height: 14px;
  border: 1.5px solid var(--rd-ctrl);
  background: transparent;
}

/* ⚠️ THE ONE ROUND CONTROL IN THIS PART, AND IT IS DRAWN AND RULED.
   `48 Kit Rules.html:33` ends *"A new rounded control is a defect, not a
   variant"* — and this is not new: the drawing draws
   `borderRadius: course ? '50%' : 0` on this element at both widths, and James
   ruled the pair in words on 26 Aug (drills multi-toggle with square boxes, the
   course single-pick with round radios). A radio is the control whose shape IS
   its meaning. The modifier exists so the exception is one line with a reason
   beside it rather than a `border-radius` loose in the file. */
.rd-wiz__pickbox--round {
  border-radius: 50%;
}

.rd-wiz__pickbox--on {
  border-color: var(--rd-acc);
  background: var(--rd-acc);
}

/* UNDRAWN, and it is the 28 Aug ruling's shape. The drawn picker row is one
   line, because the drawn picker held one population — the org's own drills.
   James ruled the two Pro pickers list the org's OWN drills PLUS the
   public/published catalogue, so every row now names its source underneath, and
   the name needs a block to sit in. Geometry taken from `.rd-wiz__copytxt`
   beside it, which is the same name-over-sub row this file already draws. */
.rd-wiz__picktxt {
  flex: 1;
  min-width: 0;
  display: block;
}

/* `Made by` — the row's source. `.rd-wiz__copysub`'s face exactly: 10.5 in
   `t.fg3`. One face for one job, at one width. */
.rd-wiz__pick-made {
  display: block;
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* `flex: 1, pbody, fontSize: 13.5, color: t.fg` (`rd-events-lab.jsx:262`).
   ⚠️ `flex: 1` is kept though the element now sits inside `.rd-wiz__picktxt`,
   which carries the flex child role: it is inert on a block child and removing
   it would be a second edit to a drawn rule for no drawn reason. */
.rd-wiz__pick-name {
  display: block;
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The shelf tag — `pdisp(700, 78, '.1em')` at 8.5, `t.fg3`
   (`rd-events-lab.jsx:263`). `Drills` for the drill population, `5 stages` for
   a course. Drawn size kept, as this part keeps `.rd-wiz__label`'s 9. */
.rd-wiz__pick-tag {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

/* UNDRAWN — a picker whose population is empty. In the row band's own padding
   so the modal does not change shape when the library is. */
.rd-wiz__pick-none {
  padding: 17px 22px;
  border-bottom: 1px solid var(--rd-line);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg3);
}

/* FRAME 11d — three skeleton rows IN THE SHEET'S OWN GEOMETRY, no spinner.
   `padding: '17px 22px', gap: 12`, a 14px mark and a 10px bar at 64/44/56%
   (`rd-events-lab.jsx:254` ("64, 44, 56")).

   ⚠️ NOT `RdSkeleton`, and its own doc is the reason: its shapes are *"a closed
   set of drawn shapes, and deliberately not a geometry API"* — `Line` is 82% ×
   9 and there is no width prop, by design. The kit primitive that DOES apply is
   mounted beside these rows: `RdLoading`, the one visually-hidden live region
   that names what is on its way.

   Flat `--rd-surf2` blocks rather than the kit's pulsing ink, because the
   drawing paints them flat — `background: t.surf2` and nothing else. */
.rd-wiz__pickskel {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 17px 22px;
  border-bottom: 1px solid var(--rd-line);
}

.rd-wiz__pickskel-mark {
  flex: none;
  width: 14px;
  height: 14px;
  background: var(--rd-surf2);
}

.rd-wiz__pickskel-bar {
  height: 10px;
  background: var(--rd-surf2);
}

/* The three drawn widths. Named rather than inlined as a style attribute, and
   shared with the phone sheet's rows the way `s13-plan.css` shares
   `.rd-plan__pick-box` across its two arms: *"one page, one part, one shape"* —
   the widths are one figure from one drawing, and a `-m__` twin would be two
   rules that must stay identical to keep a ruling whose content is "these
   three". */
.rd-wiz__pickskel-bar--w64 { width: 64%; }
.rd-wiz__pickskel-bar--w44 { width: 44%; }
.rd-wiz__pickskel-bar--w56 { width: 56%; }

/* `padding: '16px 22px 20px', display: flex, gap: 14, alignItems: 'center'`
   (`rd-events-lab.jsx:266` ("16px 22px 20px")). */
.rd-wiz__modal-acts {
  flex: none;
  padding: 16px 22px 20px;
  display: flex;
  gap: 14px;
  align-items: center;
}

/* `pbody` at 11.5, `t.fg3` (`rd-events-lab.jsx:268` ("Cancel")). A word, not a
   box — the drawing gives the second act no outline here, unlike the footer's
   `Back`. */
.rd-wiz__pick-cancel {
  flex: none;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
}

.rd-wiz__pick-cancel:hover { color: var(--rd-fg2); }

/* ---------------------------------------------------------------------------
   4. PHONE — 52 (Mobile) Events Lab, `MWizShell`.

   The shell's own padding and heading are NOT here. `rd-events-lab-mobile.jsx:24`
   ("pad={18}") and `:30` ("size={27}") are the standard Pro mobile screen, which
   `adm0-mobile.css` already paints for every destination in this console
   (`.rd-prom` padding `… 18px 0`, `.rd-prom .rd-pro-top__title` 27px). C-52.5
   ruled the phone wizard's earlier 16/20 *"drift, not a decision"*; mounting the
   standard chrome is what closes it, so this part adds nothing for it.
   --------------------------------------------------------------------------- */
.rd-wizm {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* The nine-segment strip that stands in for the 190px rail.
   `rd-events-lab-mobile.jsx:31` ("gap: 3") — `margin: '9px 0 6px'`, each segment
   `flex: 1, height: 3` (`:32`). */
.rd-wizm__strip {
  flex: none;
  display: flex;
  gap: 3px;
  margin: 9px 0 6px;
}

.rd-wizm__seg {
  flex: 1;
  height: 3px;
  background: var(--rd-line);
}

.rd-wizm__seg--on { background: var(--rd-acc); }
.rd-wizm__seg--done { background: var(--rd-ctrl); }

/* The step line. `rd-events-lab-mobile.jsx:34` ("alignItems: 'baseline'") — the
   line carries the event's name, the ordinal, the step's name and the rail's
   `· Optional`, with `Close` on its right (`:35`, `:38`). */
.rd-wizm__line {
  flex: none;
  display: flex;
  align-items: baseline;
  margin-bottom: 11px;
}

.rd-wizm__line-txt {
  flex: 1;
  min-width: 0;
  font-size: 10.5px;
  color: var(--rd-fg3);
}

.rd-wizm__line-name { color: var(--rd-fg2); font-weight: 600; }

/* `Close` on the step line (`rd-events-lab-mobile.jsx:38` ("Close")).

   ⚠️ The PAINTED word stays the drawn 10.5px and the TARGET does not. The kit's
   own ruling on this (`components/kit/button.rs`, `hit_target_height_px`):
   *"platform minimums are met by non-visual hit-area extension; the painted
   geometry stays the designer's."* The padding below is that extension — it
   buys a 36px-tall target without moving a painted pixel, which is the same
   number `macw` grew to on 26 Aug for the header acts
   (`rd-events-lab-mobile.jsx:17` ("minHeight: 36")). */
.rd-wizm__close {
  flex: none;
  border: 0;
  background: none;
  cursor: pointer;
  padding: 11px 0 11px 14px;
  margin: -11px 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
}

/* `rd-events-lab-mobile.jsx:40` ("overflow: 'hidden'") — the drawing clips
   because it is a still frame at a fixed height; a real screen scrolls, and the
   footer below is what must not. */
.rd-wizm__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

/* The footer PINS. `rd-events-lab-mobile.jsx:41` ("paddingTop: 9") — the file's
   own translation rule says why: *"The footer pins: Back + Next always
   reachable"*.

   ⚠️ `--rd-home-clear`, and it is the kit's token rather than a number.
   `48 Kit Rules.html:47`: *"Nothing tappable sits in the home-indicator zone …
   The kit owns the clearance so no screen has to remember it."* This is a
   nav-less phone screen with acts on its bottom edge, so it takes the 28px
   floor (`kit-shell.css` §0), not `--rd-sheet-clear` — that one is for a
   viewport-anchored bottom SHEET, and the wizard's footer is not one. */
.rd-wizm__foot {
  flex: none;
  padding-top: 9px;
  padding-bottom: var(--rd-home-clear);
  border-top: 1px solid var(--rd-line);
}

/* The same strip as the desktop's, inside the footer block above the acts
   (`rd-events-lab-mobile.jsx:42`). */
.rd-wizm__err {
  padding: 8px 10px;
  border: 1px solid var(--rd-danger);
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
}

.rd-wizm__err-msg {
  flex: 1;
  font-size: 10px;
  line-height: 1.4;
  color: var(--rd-danger);
}

.rd-wizm__retry {
  flex: none;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 8.5px;
  color: var(--rd-acc);
}

/* The step's own line, on its own row above the acts
   (`rd-events-lab-mobile.jsx:43`). */
.rd-wizm__stepfoot {
  font-size: 9.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
  margin-bottom: 8px;
}

/* One act row, `gap: 7` (`rd-events-lab-mobile.jsx:44`). */
.rd-wizm__acts {
  display: flex;
  gap: 7px;
}

/* `Back` is `flex: 'none'` and `Keep As Draft` is `flex: 1`
   (`rd-events-lab-mobile.jsx:45`, `:46`) — the modifier is what carries that
   difference, so the two are one control with one stated variation. */
.rd-wizm__act {
  flex: none;
  border: 1.5px solid var(--rd-ctrl);
  background: none;
  color: var(--rd-fg);
  padding: 10px 13px;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 10.5px;
}

.rd-wizm__act--wide { flex: 1; padding: 10px 4px; text-align: center; }
.rd-wizm__act:disabled { opacity: .5; cursor: default; }

/* The phone primary — `flex: 1`, centred, same fill and chamfer
   (`rd-events-lab-mobile.jsx:47` ("flex: 1")). Borrows `.rd-pro-top__btn` like
   its desktop sibling. */
.rd-wizm__next {
  flex: 1;
  border: 0;
  cursor: pointer;
  justify-content: center;
  padding: 10px 4px;
  font-size: 10.5px;
}

.rd-wizm__next--dim { opacity: .45; }
.rd-wizm__next:disabled { cursor: default; }

/* Centred under the acts, from step 2 on (`rd-events-lab-mobile.jsx:49`), and
   the offline sentence in its place (`:50`). */
.rd-wizm__saved {
  font-size: 9.5px;
  color: var(--rd-fg3);
  text-align: center;
  margin-top: 6px;
}

.rd-wizm__offline {
  font-size: 9.5px;
  color: var(--rd-fg2);
  text-align: center;
  margin-top: 6px;
}

/* The step body's quiet block quote at phone width — `paddingLeft: 11`,
   10.5px (`rd-events-lab-mobile.jsx:212` ("paddingLeft: 11")). */
.rd-wizm__intro {
  border-left: 2px solid var(--rd-line);
  padding-left: 11px;
  margin: 0;
  font-size: 10.5px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   5. PHONE — STEP 1, Basics. `rd-events-lab-mobile.jsx:95`
   ("function MWizBasics"), the step's grid at `:98` ("gap: 11").

   Same three decisions as the desktop section above and they are not restated:
   the drawn boxes are all in their filled state and the resting rule here is
   the empty one; no chamfer; the description is a native `textarea` by ruling.
   Only the geometry moves — `msl`/`msb` (`rd-events-lab-mobile.jsx:15`
   ("const msl"), `:16` ("const msb")) are `wl`/`wb` one step tighter.
   --------------------------------------------------------------------------- */
.rd-wizm__basics {
  display: grid;
  gap: 11px;
}

/* `rd-events-lab-mobile.jsx:99` ("1px dashed") — `gap: 8`, `padding: '8px 10px'`. */
.rd-wizm__copydoor {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border: 1px dashed var(--rd-ctrl);
}

.rd-wizm__copydoor-txt {
  flex: 1;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.35;
  color: var(--rd-fg3);
}

/* `macw(t)` (`rd-events-lab-mobile.jsx:17` ("const macw")) — the phone's act
   WORD is a bordered chip rather than the desktop's bare accent caps, and the
   1.5px `t.acc` stroke plus the 36px minimum are what make it legible at 9.5px.

   ⚠️ 36px is DRAWN, and as of S22 the chip DOES tap — it opens frame 1b. The
   kit's 48dp floor is about things that respond to a thumb, and this one now
   does, so the drawn 36 is extended to it by the same non-visual padding the
   desktop act takes rather than by repainting the chip taller: the painted
   geometry stays the designer's. */
.rd-wizm__copydoor-act {
  flex: none;
  position: relative;
  border-radius: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0 11px;
  border: 1.5px solid var(--rd-acc);
  background: none;
  color: var(--rd-acc);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9.5px;
}

/* The 48dp floor, met without repainting the drawn 36px chip: a transparent
   12px band above and below, pulled back out of flow. `kit-shell.css`'s own
   idiom for the same problem. */
.rd-wizm__copydoor-act::after {
  content: '';
  position: absolute;
  inset: -6px 0;
}

.rd-wizm__field {
  display: block;
}

/* `msl(t)` — 8.5px, `marginBottom: 5` (`rd-events-lab-mobile.jsx:15`
   ("const msl")). */
.rd-wizm__label {
  display: block;
  margin-bottom: 5px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .11em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

/* `rd-events-lab-mobile.jsx:105` ("— optional"). The phone drawing sets the
   tracking on this span and NOT the colour — it inherits the label's. */
.rd-wizm__label-opt {
  letter-spacing: .06em;
}

/* `msb` at `fontSize: 11, lineHeight: 1.4`
   (`rd-events-lab-mobile.jsx:106` ("lineHeight: 1.4")), `padding: '9px 11px'`.

   ⚠️ `min-height` IS DERIVED, NOT DRAWN, AND HERE IS THE ARITHMETIC. The phone
   drawing puts no `minHeight` on this box — it is a `div` around the drawn
   sample, which wraps to three lines at this width. A `textarea` with no floor
   defaults to two rows and would open shorter than the frame. So the floor is
   the drawn box's own height: 3 lines x (11px x 1.4) = 46.2, plus 9 + 9 of
   padding, rounded to 64. Labelled rather than presented as a drawn number. */
.rd-wizm__area {
  display: block;
  width: 100%;
  box-sizing: border-box;
  min-height: 64px;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  padding: 9px 11px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.4;
  color: var(--rd-fg);
  resize: vertical;
}

.rd-wizm__area::placeholder { color: var(--rd-fg3); }

.rd-wizm__area:focus {
  outline: none;
  background: var(--rd-surf2);
  border-color: var(--rd-fg);
  caret-color: var(--rd-acc);
}

/* ---------------------------------------------------------------------------
   5b. PHONE — STEP 1, the Fee block (S14b). `rd-events-lab-mobile.jsx:109`
   (">Fee<") through `:117` ("Collected outside the app").

   The three decisions section 3c states are not restated: `msb()` boxes rather
   than the kit's segmented control, no chamfer, one owner for the amount box's
   border. Only the geometry moves — and one thing genuinely differs: the amount
   and the methods share ONE row at this width (`:113`), where the desktop gives
   them a row of their own.

   ⚠️ THE CELLS ARE UNDER THE KIT'S 48dp TAP FLOOR, AND THE DRAWING IS WHY.
   `msb` is `padding: '9px 11px'` and the drawn cells override it to `'8px 3px'`
   at 10.5px, which lands near 33px. This part already takes the drawn geometry
   for the footer acts (`.rd-wizm__act`, `padding: 10px 13px`) rather than
   inventing a floor the drawing does not draw, so the fee cells follow it.
   Flagged for the designer in the PR body rather than silently corrected here —
   a floor added in one part and not the other is worse than either answer.
   --------------------------------------------------------------------------- */

/* The Free/Paid pair — `rd-events-lab-mobile.jsx:110` ("gap: 5"). No cap: the
   phone row is the screen's width. */
.rd-wizm__feeseg {
  display: flex;
  gap: 5px;
}

/* The collection cells beside the amount box (`rd-events-lab-mobile.jsx:115`
   ("In person")) — they are the rest of the row. */
.rd-wizm__feeseg--wide {
  flex: 1;
  gap: 5px;
}

/* One cell. `msb(t, 0)` plus the drawn overrides `flex: 1, textAlign: 'center',
   padding: '8px 3px'` (`rd-events-lab-mobile.jsx:111` ("Free Event")). The
   collection cells are drawn a step smaller again (9.5px, `'8px 2px'`, `:115`);
   the difference is 1px of type and 1px of padding, and one cell rule with the
   pair's numbers is what keeps the row from being two near-identical classes. */
.rd-wizm__feeopt {
  flex: 1;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  padding: 8px 3px;
  cursor: pointer;
  text-align: center;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
}

.rd-wizm__feeopt--on {
  background: var(--rd-surf2);
  border-color: var(--rd-fg);
  color: var(--rd-fg);
}

.rd-wizm__feeopt:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* The amount + methods row — `rd-events-lab-mobile.jsx:113` ("marginTop: 5"),
   `gap: 5`. */
.rd-wizm__feerow {
  display: flex;
  gap: 5px;
  align-items: flex-start;
  margin-top: 5px;
}

/* The amount box. `msb(t, 1)` at `width: 52, flex: 'none'`, centred, `'8px 3px'`
   (`rd-events-lab-mobile.jsx:114` ("width: 52")). Content width, for the reason
   section 3c states.

   ⚠️ **THE FACE IS `msb`'s OWN 12px, AND THE BOX OVERRIDES NO SIZE.** The drawn
   `$25` cell spreads `msb(t, 1)` and then overrides only `width`, `flex`,
   `textAlign` and `padding` — `fontSize` is left at the shared field face's 12.
   The three method cells beside it DO override it, down to 9.5 (`:115`), so the
   amount is drawn as the largest thing in its row on purpose. Both halves of
   the box carry it: the `$` is a glyph the box wears, not a second face. */
.rd-wizm__amount {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
  width: 52px;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  padding: 8px 3px;
}

.rd-wizm__amount-cur {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
}

/* The same two-class defence as the desktop's — see section 3c and #617. */
.rd-wizm__amount .rd-wizm__amount-input {
  width: 100%;
  min-width: 0;
  border: 0;
  background: none;
  box-shadow: none;
  padding: 0;
  text-align: center;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg);
  appearance: none;
  -moz-appearance: textfield;
}

.rd-wizm__amount .rd-wizm__amount-input::-webkit-outer-spin-button,
.rd-wizm__amount .rd-wizm__amount-input::-webkit-inner-spin-button {
  appearance: none;
  -webkit-appearance: none;
  margin: 0;
}

.rd-wizm__amount .rd-wizm__amount-input::placeholder { color: var(--rd-fg3); }

.rd-wizm__amount:focus-within {
  background: var(--rd-surf2);
  border-color: var(--rd-fg);
}

.rd-wizm__amount .rd-wizm__amount-input:focus {
  outline: none;
  box-shadow: none;
  caret-color: var(--rd-acc);
}

/* The block's foot at phone width — `fontSize: 9.5, marginTop: 4,
   lineHeight: 1.35` (`rd-events-lab-mobile.jsx:117`
   ("Collected outside the app")).

   ⚠️ A CLASS OF ITS OWN, AND IT IS NEITHER OF THE TWO HINTS BESIDE IT. Not
   `.rd-wiz__hint` — that is the desktop's 10.5/1.45, which the desktop fee foot
   and the desktop `Same day` both genuinely draw. And not S15's
   `.rd-wizm__hint` below, which is the phone's `Same day` at 10/1.4: this foot
   is drawn 9.5/1.35, half a pixel and half a step of leading under it. Three
   drawn faces, so three rules; folding this into either neighbour would print a
   number the frame does not draw. */
.rd-wizm__feefoot {
  margin: 4px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.35;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   PHONE · STEP 2 · WHEN — `MWizWhen` (`rd-events-lab-mobile.jsx:141`).

   The same two boxes and the same two sentences, at the phone's scale. No
   toggle here either — the phone drawing reads its step list and its words from
   the desktop one, so the two cannot disagree, and neither draws one.
   --------------------------------------------------------------------------- */
.rd-wizm__when {
  display: grid;
  gap: 14px;
}

/* `display: flex, gap: 10` (`rd-events-lab-mobile.jsx:143` ("gap: 10")) — no
   `maxWidth` at this width, where the screen is the bound. */
.rd-wizm__daterow {
  display: flex;
  gap: 10px;
}

.rd-wizm__daterow .rd-wizm__field { flex: 1; min-width: 0; }

/* `msb(t, 0)` — `padding: '9px 11px'`, 12px
   (`rd-events-lab-mobile.jsx:16` ("const msb")).

   Two classes for the reason the desktop box states in full: the legacy bare
   `input:focus` rule is (0,1,1) and a single class would tie with it. */
.rd-wizm .rd-wizm__date {
  display: block;
  width: 100%;
  box-sizing: border-box;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  border-radius: 0;
  padding: 9px 11px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 400;
  font-size: 12px;
  line-height: 1.2;
  color: var(--rd-fg3);
  outline: none;
  appearance: none;
  -webkit-appearance: none;
}

/* The `on` arm of `msb`, picked on the Rust side — see the desktop box. */
.rd-wizm .rd-wizm__date--on {
  background: var(--rd-surf2);
  border-color: var(--rd-fg);
  color: var(--rd-fg);
}

.rd-wizm .rd-wizm__date:focus {
  box-shadow: none;
  border-color: var(--rd-acc);
  caret-color: var(--rd-acc);
}

/* The `Same day` words, under the empty `Ends` box rather than inside it — a
   native date input ignores `placeholder` and paints its own format text.
   `event_wizard.rs`'s `SAME_DAY_HINT` states the reasoning; the face is the
   phone's own quiet one, one step under the box's 12px. */
.rd-wizm__hint {
  margin: 4px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  line-height: 1.4;
  color: var(--rd-fg3);
}

/* `borderLeft: 2px solid t.line, paddingLeft: 11`
   (`rd-events-lab-mobile.jsx:147` ("borderLeft")). No `maxWidth`: the phone
   drawing drops it, because the screen already is one. */
.rd-wizm__quote {
  border-left: 2px solid var(--rd-line);
  padding-left: 11px;
}

/* `fontSize: 11.5, color: t.fg, lineHeight: 1.5`
   (`rd-events-lab-mobile.jsx:148` ("Just the date for now.")). */
.rd-wizm__quote-lead {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--rd-fg);
}

/* `fontSize: 10.5, color: t.fg3, lineHeight: 1.5, marginTop: 4`
   (`rd-events-lab-mobile.jsx:149` ("Leave Ends empty for a single day.")). */
.rd-wizm__quote-body {
  margin: 4px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   DESKTOP · STEP 3 · WHERE — `WizWhere` (`rd-events-lab.jsx:162`), plus frame
   11c's empty arm `WizWhereEmpty` (`:525`).

   ⚠️ APPENDED AT THE END RATHER THAN FILED AFTER THE `When` BLOCK, on purpose.
   A sibling slice (S14b, the Basics fee block) is in flight over sections 3b
   and 5 of this file; a step's worth of rules inserted between them would put
   this slice and that one in the same hunks for no reader's benefit. The
   section headers say which step each block is.

   ⚠️ THE ROWS ARE A RADIO GROUP, NOT A TABLE. `kit/row.rs` refuses row-level
   activation in its own words — "a `div` with a click handler is a link that a
   keyboard cannot reach" — and a pick row is a radio, whose whole surface is
   the target. So these are `button`s with `role="radio"`, page-local, named
   `.rd-wiz__loc*`: a control's classes, which is why `pro_table.rs`'s
   `__row` / `__head` sweep neither fires nor needs an allow-list entry.

   ⚠️ NO CHAMFER, same as the two date boxes above and for the same reason: the
   drawing's rows are plain 1px strokes, and `tcham` appears nowhere inside
   `WizWhere`.
   --------------------------------------------------------------------------- */
.rd-wiz__where {
  display: block;
}

/* `display: grid, gap: 7` (`rd-events-lab.jsx:166` ("gap: 7")) — the records
   AND the dashed row below them are one rhythm in the drawing, so they are one
   grid here. */
.rd-wiz__locblock {
  display: grid;
  gap: 7px;
}

.rd-wiz__locs {
  display: grid;
  gap: 7px;
}

/* One record — `gap: 14, padding: '13px 16px'`, the `wb` off-arm ground
   (`rd-events-lab.jsx:168` ("padding: '13px 16px'")). */
.rd-wiz__loc {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  box-sizing: border-box;
  padding: 13px 16px;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  border-radius: 0;
  text-align: left;
  cursor: pointer;
}

/* The picked record — `t.surf2` under a `t.fg` border (`:168`, `i === 0`). */
.rd-wiz__loc--on {
  background: var(--rd-surf2);
  border-color: var(--rd-fg);
}

.rd-wiz__loc:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* The radio mark — `14 x 14`, round, `1.5px t.ctrl`, filled `t.acc` when picked
   (`rd-events-lab.jsx:169` ("borderRadius: '50%'")). */
.rd-wiz__loc-mark {
  width: 14px;
  height: 14px;
  flex: none;
  border-radius: 50%;
  border: 1.5px solid var(--rd-ctrl);
  background: transparent;
}

.rd-wiz__loc-mark--on {
  border-color: var(--rd-acc);
  background: var(--rd-acc);
}

/* The range's name — 13.5px `t.fg` (`rd-events-lab.jsx:170` ("fontSize: 13.5")). */
.rd-wiz__loc-name {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
}

/* The source word — `pdisp(700, 78, '.1em')` at 8.5px in `t.fg3`
   (`rd-events-lab.jsx:171` ("fontSize: 8.5")). `This org` or `Public`; the
   mapping is the domain's, in `locations::location_source_word`. */
.rd-wiz__loc-tag {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

/* The dashed `Add new location` row (`rd-events-lab.jsx:174` ("1px dashed"),
   `:534` in the empty frame — same row, both frames).

   ⚠️ THIS ONE IS A CONTROL AND THE COPY DOOR ABOVE IS NOT, and the difference
   is that this one has somewhere to go: the add form under it, in this slice.
   So it takes `cursor: pointer` and a focus ring, which `.rd-wiz__copydoor`
   deliberately refuses. */
.rd-wiz__addloc {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  box-sizing: border-box;
  padding: 13px 16px;
  background: none;
  border: 1px dashed var(--rd-ctrl);
  border-radius: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg2);
  text-align: left;
  cursor: pointer;
}

.rd-wiz__addloc:hover { border-color: var(--rd-fg3); }

.rd-wiz__addloc:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* Frame 11c's copy block — `maxWidth: '54ch', paddingTop: 8`
   (`rd-events-lab.jsx:529` ("54ch")).

   ⚠️ `margin-bottom` IS DERIVED, AND HERE IS THE ARITHMETIC. The drawing puts
   `marginTop: 16` on the dashed row in the EMPTY frame (`:533`) and `gap: 7`
   between the rows in the filled one (`:166`). The block and the dashed row are
   one grid here — one Rust arm, one rhythm — so the empty block carries the
   9px difference and the pair lands on the drawn 16. Labelled rather than
   presented as a drawn number. */
.rd-wiz__locempty {
  max-width: 54ch;
  padding-top: 8px;
  margin-bottom: 9px;
}

/* `pdisp(800, 75, '.02em')` at 17px in `t.fg`
   (`rd-events-lab.jsx:530` ("No saved locations yet.")). */
.rd-wiz__locempty-head {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 75%;
  letter-spacing: .02em;
  font-size: 17px;
  color: var(--rd-fg);
}

/* `fontSize: 12.5, color: t.fg3, marginTop: 10, lineHeight: 1.6`
   (`rd-events-lab.jsx:531` ("Add the range you use")). */
.rd-wiz__locempty-body {
  margin-top: 10px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--rd-fg3);
}

/* The inline add-new form. UNDRAWN on 52 — the lab draws the dashed row and
   stops — so the geometry is this step's own: the drawn `gap: 18` the step
   bodies here already use, and the kit's own fields inside it. The SHAPE is
   ruled (C-56.9.1: name, then the five address parts behind a disclosure). */
.rd-wiz__newloc {
  display: grid;
  gap: 14px;
  margin-top: 14px;
  padding: 16px;
  border: 1px solid var(--rd-line);
  background: var(--rd-surf);
}

.rd-wiz__disclose-line {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}

/* `Add address` — the accent display word this console gives a quiet act. */
.rd-wiz__disclose {
  flex: none;
  padding: 0;
  background: none;
  border: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9px;
  color: var(--rd-acc);
  cursor: pointer;
}

.rd-wiz__disclose:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

.rd-wiz__disclose-note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.45;
  color: var(--rd-fg3);
}

.rd-wiz__address {
  display: grid;
  gap: 12px;
}

.rd-wiz__newloc-acts {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* The free-text half — `marginTop: 18, maxWidth: 360`
   (`rd-events-lab.jsx:180` ("maxWidth: 360")). */
.rd-wiz__wherebox {
  margin-top: 18px;
  max-width: 360px;
}

/* `wb(t, 0)` for a TEXT box — the same two arms and the same two-class
   specificity answer the date boxes above carry, and here the legacy bare
   `input:focus` rule genuinely applies: `text` IS on `_legacy-pre-kit.css`'s
   attribute list where `date` is not. `.rd-wiz .rd-wiz__text` is (0,2,0) and
   wins on specificity rather than on source order. */
.rd-wiz .rd-wiz__text {
  display: block;
  width: 100%;
  box-sizing: border-box;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  border-radius: 0;
  padding: 11px 13px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 400;
  font-size: 13.5px;
  line-height: 1.2;
  color: var(--rd-fg);
  outline: none;
  appearance: none;
  -webkit-appearance: none;
}

.rd-wiz .rd-wiz__text::placeholder { color: var(--rd-fg3); }

/* The `on` arm — a box with a bay line in it. Picked on the Rust side, beside
   the two date boxes, so the two halves of the step answer emptiness the same
   way. */
.rd-wiz .rd-wiz__text--on {
  background: var(--rd-surf2);
  border-color: var(--rd-fg);
  color: var(--rd-fg);
}

.rd-wiz .rd-wiz__text:focus {
  box-shadow: none;
  border-color: var(--rd-acc);
  caret-color: var(--rd-acc);
}

/* ---------------------------------------------------------------------------
   PHONE · STEP 3 · WHERE — `MWizWhere` (`rd-events-lab-mobile.jsx:158`), plus
   `MWizWhereEmpty` (`:417`).

   The same control at the phone's scale — `MPickRow`'s inline arm
   (`rd-events-lab-mobile.jsx:80` ("function MPickRow")) is `gap: 10`,
   `padding: '10px 12px'`, a 13px mark and a 12.5px name. The decisions above
   are not restated: same radio grammar, same absence of chamfer, same
   `location_source_word` mapping.
   --------------------------------------------------------------------------- */
.rd-wizm__where {
  display: block;
}

/* `display: grid, gap: 5` (`rd-events-lab-mobile.jsx:162` ("gap: 5")). */
.rd-wizm__locblock {
  display: grid;
  gap: 5px;
}

.rd-wizm__locs {
  display: grid;
  gap: 5px;
}

/* `MPickRow`'s inline arm (`rd-events-lab-mobile.jsx:82` ("padding: sheet ?")).

   ⚠️ `min-height: 48px` IS THE KIT'S FLOOR, NOT THE DRAWING'S NUMBER. The drawn
   row computes to about 42 (10 + 12.5 x 1.2 + 10 + 2 of border) and this row is
   a real tap target, which the 48 Kit Rules floor is about; the copy-door chip
   above takes the drawn height instead precisely because it does NOT tap. */
.rd-wizm__loc {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  box-sizing: border-box;
  min-height: 48px;
  padding: 10px 12px;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  border-radius: 0;
  text-align: left;
  cursor: pointer;
}

.rd-wizm__loc--on {
  background: var(--rd-surf2);
  border-color: var(--rd-fg);
}

.rd-wizm__loc:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* `13 x 13`, round, `1.5px` (`rd-events-lab-mobile.jsx:83` ("width: 13")). */
.rd-wizm__loc-mark {
  width: 13px;
  height: 13px;
  flex: none;
  border-radius: 50%;
  border: 1.5px solid var(--rd-ctrl);
  background: transparent;
}

.rd-wizm__loc-mark--on {
  border-color: var(--rd-acc);
  background: var(--rd-acc);
}

/* 12.5px `t.fg` (`rd-events-lab-mobile.jsx:85` ("fontSize: 12.5")). */
.rd-wizm__loc-name {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
}

/* 8.5px `pdisp(700, 78, '.1em')` (`rd-events-lab-mobile.jsx:88` ("fontSize: 8.5")). */
.rd-wizm__loc-tag {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

/* The dashed row — `padding: '10px 12px'`, 12.5px
   (`rd-events-lab-mobile.jsx:164` ("1px dashed")), at the same 48 floor as the
   pick rows above and for the same reason. */
.rd-wizm__addloc {
  display: flex;
  align-items: center;
  width: 100%;
  box-sizing: border-box;
  min-height: 48px;
  padding: 10px 12px;
  background: none;
  border: 1px dashed var(--rd-ctrl);
  border-radius: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg2);
  text-align: left;
  cursor: pointer;
}

.rd-wizm__addloc:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* Frame 11c at phone width — `paddingTop: 6` (`rd-events-lab-mobile.jsx:421`).

   The same derived tail as the desktop block: the drawing puts `marginTop: 12`
   on the dashed row here (`:425`) against a `gap: 5` list, so the block carries
   the 7px difference. */
.rd-wizm__locempty {
  padding-top: 6px;
  margin-bottom: 7px;
}

/* `pdisp(800, 75, '.02em')` at 15px (`rd-events-lab-mobile.jsx:422`). */
.rd-wizm__locempty-head {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 75%;
  letter-spacing: .02em;
  font-size: 15px;
  color: var(--rd-fg);
}

/* `fontSize: 11, marginTop: 7, lineHeight: 1.5`
   (`rd-events-lab-mobile.jsx:423`). */
.rd-wizm__locempty-body {
  margin-top: 7px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

/* The inline add-new at phone scale — undrawn, see the desktop block. */
.rd-wizm__newloc {
  display: grid;
  gap: 11px;
  margin-top: 11px;
  padding: 12px;
  border: 1px solid var(--rd-line);
  background: var(--rd-surf);
}

.rd-wizm__disclose-line {
  display: flex;
  align-items: baseline;
  gap: 7px;
  flex-wrap: wrap;
}

.rd-wizm__disclose {
  flex: none;
  padding: 0;
  background: none;
  border: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9px;
  color: var(--rd-acc);
  cursor: pointer;
}

.rd-wizm__disclose:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

.rd-wizm__disclose-note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.35;
  color: var(--rd-fg3);
}

.rd-wizm__address {
  display: grid;
  gap: 10px;
}

.rd-wizm__newloc-acts {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* `marginTop: 14` (`rd-events-lab-mobile.jsx:166`). No `maxWidth` — see the
   Rust side, which states why the drawn 150 is a sample's shrink-to-fit rather
   than a box a phone should type into. */
.rd-wizm__wherebox {
  margin-top: 14px;
}

/* The note under the box — `fontSize: 9.5, marginTop: 4, lineHeight: 1.35`
   (`rd-events-lab-mobile.jsx:169` ("The spot on the range")).

   ⚠️ **NOT `.rd-wizm__hint`, WHICH IS `Same day`'s 10/1.4.** This line used to
   borrow that class and printed half a pixel and half a step of leading too
   large. The face it actually shares is Basics' `.rd-wizm__feefoot` — same
   9.5/1.35, drawn from the same `pbody` spread — and that rule's own comment is
   the reason this is a second rule rather than a borrow across two steps: three
   drawn faces, three rules, and a fold would tie one step's foot to another's
   the next time either number moves. */
.rd-wizm__wherenote {
  margin: 4px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.35;
  color: var(--rd-fg3);
}

/* `msb(t, 0)` for a TEXT box — the pair, for the desktop box's specificity
   reason, which bites harder here: `text` IS on the legacy attribute list. */
.rd-wizm .rd-wizm__text {
  display: block;
  width: 100%;
  box-sizing: border-box;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  border-radius: 0;
  padding: 9px 11px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 400;
  font-size: 12px;
  line-height: 1.2;
  color: var(--rd-fg);
  outline: none;
  appearance: none;
  -webkit-appearance: none;
}

.rd-wizm .rd-wizm__text::placeholder { color: var(--rd-fg3); }

.rd-wizm .rd-wizm__text--on {
  background: var(--rd-surf2);
  border-color: var(--rd-fg);
  color: var(--rd-fg);
}

.rd-wizm .rd-wizm__text:focus {
  box-shadow: none;
  border-color: var(--rd-acc);
  caret-color: var(--rd-acc);
}

/* ---------------------------------------------------------------------------
   7. PHONE — STEP 4, What. `rd-events-lab-mobile.jsx:177` ("function MWizWhat").
   (There is no numbered 6: S16's two STEP 3 · WHERE blocks above carry unnumbered
   headers, and renumbering another slice's sections is not this one's to do.)

   The same list, the same words, the phone's geometry. The two header acts are
   `macw` — a bordered 36px accent target since 26 Aug, not the 8.5px bare word
   they were before it (`rd-events-lab-mobile.jsx:17` ("const macw")).
   --------------------------------------------------------------------------- */
.rd-wizm__what {
  display: flex;
  flex-direction: column;
}

/* `alignItems: 'center', justifyContent: 'space-between', gap: 8`
   (`rd-events-lab-mobile.jsx:181` ("justifyContent: 'space-between'")) —
   CENTRED here where the desktop is baseline-aligned, because the acts are
   36px boxes at this width and a baseline would hang them off the label. */
.rd-wizm__whathead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

/* `display: flex, gap: 12, flex: 'none'` (`rd-events-lab-mobile.jsx:184`
   ("gap: 12")). */
.rd-wizm__whatacts {
  display: flex;
  gap: 12px;
  flex: none;
}

/* `macw` — `pdisp(700, 78, '.08em')` at 9.5, `t.acc`, `minHeight: 36`,
   `padding: '0 11px'`, `border: 1.5px solid t.acc`
   (`rd-events-lab-mobile.jsx:17` ("const macw")). The 1.5px accent stroke and
   the 36px minimum are what make it a target at 9.5px. */
.rd-wizm__whatact {
  flex: none;
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0 11px;
  border: 1.5px solid var(--rd-acc);
  background: none;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9.5px;
  color: var(--rd-acc);
}

/* Frame 4 at phone width — `paddingTop: 18`
   (`rd-events-lab-mobile.jsx:191` ("paddingTop: 18")). No `maxWidth`: the
   screen already is one. */
.rd-wizm__whatempty {
  padding-top: 18px;
}

/* `pdisp(800, 75, '.02em')` at 15, `t.fg` (`rd-events-lab-mobile.jsx:192`
   ("Nothing on the schedule yet")). */
.rd-wizm__whatempty-head {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 75%;
  letter-spacing: .02em;
  font-size: 15px;
  color: var(--rd-fg);
}

/* `fontSize: 11.5, color: t.fg3, marginTop: 8, lineHeight: 1.5`
   (`rd-events-lab-mobile.jsx:193` ("Add drills one at a time")). */
.rd-wizm__whatempty-body {
  margin: 8px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

/* `display: grid, gap: 7, marginTop: 14` (`rd-events-lab-mobile.jsx:194`
   ("gap: 7")) — STACKED here where the desktop puts the two doors side by
   side, which is 46's one-act-per-screen rule applied to a pair. */
.rd-wizm__whatempty-acts {
  display: grid;
  gap: 7px;
  margin-top: 14px;
}

/* The volt door — `padding: '10px 4px'`, centred, 10.5px, `tcham(7)`
   (`rd-events-lab-mobile.jsx:195` ("Add A Drill")). Wears `.rd-pro-top__btn`
   for the fill and the chamfer, like `.rd-wizm__next`. The sheet's commit act
   wears this same rule (`rd-events-lab-mobile.jsx:71` ("flex: 1")); the outline
   door beside it is `.rd-wizm__act--wide`, which is already the drawn
   `1.5px t.ctrl` box at this padding. */
.rd-wizm__door {
  flex: 1;
  border: 0;
  cursor: pointer;
  justify-content: center;
  text-align: center;
  padding: 10px 4px;
  font-size: 10.5px;
}

.rd-wizm__door:disabled { cursor: default; opacity: .5; }

/* `display: grid, gap: 5` (`rd-events-lab-mobile.jsx:203` ("gap: 5")). */
.rd-wizm__picked {
  display: grid;
  gap: 5px;
}

/* `padding: '9px 12px', gap: 10, background: t.field,
   border: 1px solid t.ctrl` (`rd-events-lab-mobile.jsx:204`
   ("padding: '9px 12px'")). The desktop row's twin — see that rule for why
   this is neither `RdRow` nor a `__row`. */
.rd-wizm__pickedrow {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
}

.rd-wizm__pickedmain {
  flex: 1;
  min-width: 0;
}

/* `fontSize: 12, color: t.fg` (`rd-events-lab-mobile.jsx:205`). */
.rd-wizm__pickedname {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg);
}

/* `fontSize: 9.5, color: t.fg3, marginTop: 2`
   (`rd-events-lab-mobile.jsx:205` ("From {from}")). */
.rd-wizm__pickedfrom {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* `fontSize: 10.5, color: t.fg3, flex: 'none'`
   (`rd-events-lab-mobile.jsx:207` ("Remove")). */
.rd-wizm__remove {
  flex: none;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
}

/* The step's quote takes the KIT'S BLOCK GAP where the drawing draws
   `marginTop: 14` (`rd-events-lab-mobile.jsx:210` ("marginTop: 14")), and its
   one sentence spends no second-line margin — the second half of the pair Who
   (`marginTop: 12`), Gear (`13`) and Documents (`14`) already declare.

   ⚠️ **THE TOKEN IS THE DESKTOP TWIN'S, AND BOTH WIDTHS TOOK IT TOGETHER**
   (James, rehearsal item 19). `--rd-banner-gap` — `p1.css`, 16px here and 20px
   from 1024 up — is the kit's block gap, and its THE SLOT RULING is the
   argument: a gap every caller respells is a gap no caller owns. 14 and 16 are
   the same gap drawn and tokenised. The other three steps keep their drawn
   numbers; converging them is a ruling of its own and not this item's.

   ⚠️ **THE COMPOUND USED TO NAME `.rd-wizm__intro` AND MATCHED NOTHING.**
   `.rd-wizm__intro` is the catch-all paragraph for a step with no body of its
   own; it is mounted in the fallthrough arm, never inside `.rd-wizm__what`. So
   the drawn 14 was stranded on a selector the step does not produce and the
   quote printed flush against the list above it. */
.rd-wizm__what .rd-wizm__quote { margin-top: var(--rd-banner-gap); }
.rd-wizm__what .rd-wizm__quote-body { margin-top: 0; }

/* ---------------------------------------------------------------------------
   8. PHONE — FRAMES 4b / 4c, the picker as a bottom sheet. `MSheet`,
   `rd-events-lab-mobile.jsx:59` ("function MSheet").

   The desktop's centred modal becomes the phone's modal grammar, KEEPING THE
   DESKTOP MODAL'S WORDS VERBATIM — the drawing's own rule for the move
   (`rd-events-lab-mobile.jsx:57-58`). Only the geometry is here; every string
   comes from `event_wizard.rs`'s `PickerKind`, shared with the desktop arm.

   ⚠️ 11d IS DESKTOP-ONLY IN THE DRAWING and the skeleton rows still mount
   here. The phone file drops that frame (it draws 22 of the desktop's 24), not
   the state: a sheet that opens onto nothing while a read is in flight and then
   pops four rows is the flash the loading system exists to prevent. The rows
   take this sheet's own row padding and the three drawn widths, which is what
   "the sheet's own geometry" means at either width.
   --------------------------------------------------------------------------- */
/* `position: absolute, inset: 0` over a `fixed` wrapper — the same shape
   `.rd-prom__overlay` / `.rd-prom__sheet` already ship for the org sheet, which
   is this console's only other bottom sheet. */
.rd-wizm__pickwrap {
  position: fixed;
  inset: 0;
  z-index: 40;
}

/* `background: 'rgba(0,0,0,.55)'` (`rd-events-lab-mobile.jsx:63`
   ("rgba(0,0,0,.55)")) — the phone's own scrim value, lighter than the
   desktop's because it dims a screen rather than a console. A real button: the
   scrim is the dismissal. */
.rd-wizm__pick-scrim {
  position: absolute;
  inset: 0;
  border: 0;
  padding: 0;
  background: rgba(0, 0, 0, .55);
  cursor: pointer;
}

/* `left/right 0, bottom 0, ground #141414 / t.surf, borderTop: 1px t.line`
   (`rd-events-lab-mobile.jsx:64` ("borderTop")). `--rd-surf` for the ground —
   the token the drawing's own non-texture branch names beside the hex.

   ⚠️ **THIS IS NOT `adm0-mobile.css` §6's CASE, AND TAKING ITS ANSWER INVERTED
   THE SHEET.** There, a drawer ground and a sheet ground are BOTH drawn as
   `t.surf`, two different grounds that overlap, so the sheet had to step up to
   `--rd-surf2` to stay distinguishable. Here the sheet overlaps nothing: it
   sits under a `rgba(0,0,0,.55)` scrim of its own, which is what separates it
   from the screen, and its chosen row is drawn a step ABOVE it (`t.surf2`,
   `MPickRow`'s `on` arm). With two surface tokens on the ladder, spending
   `--rd-surf2` on the panel leaves the chosen row nowhere lighter to go, and
   the row shipped DARKER than the panel — the reverse of the drawing.

   `max-height` is ours — a drawn frame has a fixed height and a real library
   does not.

   ⚠️ **AND THE SHEET IS BOUNDED WITH ITS LIST AS THE ONLY SCROLLER**, the
   phone half of rehearsal item 20; `.rd-wiz__modal` (§3e) carries the ruling
   in full. The sheet scrolled as a whole, so a library longer than the drawn
   four rows carried `Add N Drills` — and the home-indicator clearance under it
   — off the bottom of the panel. A flex column instead: the head and the
   search pinned, `.rd-wizm__pick-list` taking the slack, `.rd-wizm__sheet-acts`
   pinned at the foot however long the list runs.

   The cap stays `max-height: 80%` and stays a PERCENTAGE, which is why no
   `dvh` twin appears here where the desktop grew one: the percentage resolves
   against `.rd-wizm__pickwrap`, which is `position: fixed; inset: 0` and is
   already the visual viewport the URL bar leaves behind. */
.rd-wizm__sheet {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  max-height: 80%;
  display: flex;
  flex-direction: column;
  background: var(--rd-surf);
  border-top: 1px solid var(--rd-line);
}

/* `padding: '15px 16px 11px', borderBottom: 1px t.line`
   (`rd-events-lab-mobile.jsx:65` ("15px 16px 11px")). */
.rd-wizm__sheet-head {
  flex: none;
  padding: 15px 16px 11px;
  border-bottom: 1px solid var(--rd-line);
}

/* `pdisp(800, 75, '.02em')` at 15, `t.fg` (`rd-events-lab-mobile.jsx:66`). */
.rd-wizm__sheet-title {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 75%;
  letter-spacing: .02em;
  font-size: 15px;
  color: var(--rd-fg);
}

/* `fontSize: 10.5, color: t.fg3, marginTop: 5, lineHeight: 1.45`
   (`rd-events-lab-mobile.jsx:67`). */
.rd-wizm__sheet-sub {
  margin-top: 5px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.45;
  color: var(--rd-fg3);
}

/* `padding: '10px 16px', borderBottom: 1px t.line`
   (`rd-events-lab-mobile.jsx:228` ("padding: '10px 16px'")). */
.rd-wizm__sheet-search {
  flex: none;
  padding: 10px 16px;
  border-bottom: 1px solid var(--rd-line);
}

/* THE ONE SCROLLER IN THE SHEET — the twin of `.rd-wiz__pick-list` (§3e),
   declaration for declaration and for the same reasons; that rule carries them.
   `min-height: 0` is what keeps the list shrinkable so the acts stay on the
   foot, and this selector is named in `pro/shell.rs`'s `SCROLL_ALLOWLIST` with
   its reason. */
.rd-wizm__pick-list {
  display: block;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

/* `MPickRow` in its sheet arm — `padding: '10px 16px', gap: 10,
   borderBottom: 1px t.line` (`rd-events-lab-mobile.jsx:82`
   ("padding: sheet ?")).

   ⚠️ `min-height: 48px` IS THE KIT'S FLOOR, NOT THE DRAWING'S NUMBER — the same
   call `.rd-wizm__loc` states in full one step back, and the same drawn row:
   `MPickRow`'s two arms differ only in gutter and ground. This arm computes to
   about 38 on the drawn padding, so it was the shortest tap target on these
   screens while its twin already stood at 48. */
.rd-wizm__pick {
  width: 100%;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 48px;
  padding: 10px 16px;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  background: none;
  text-align: left;
  cursor: pointer;
}

/* `background: on ? t.surf2 : 'transparent'` (`rd-events-lab-mobile.jsx:82`) —
   one step UP from the sheet's own ground, and the same `--rd-surf2` every
   `MPickRow` row in this file takes (`.rd-wizm__loc--on`,
   `.rd-wizm__whorow--on`). One component draws all three arms, so one on-state
   ground is the drawing's own answer. */
.rd-wizm__pick--on {
  background: var(--rd-surf2);
}

.rd-wizm__pick:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* `width: 13, height: 13, border: 1.5px solid`, accent-filled when on
   (`rd-events-lab-mobile.jsx:83` ("borderRadius: box")) — 13 here against the
   desktop's 14, which is the one number that differs. */
.rd-wizm__pickbox {
  flex: none;
  width: 13px;
  height: 13px;
  border: 1.5px solid var(--rd-ctrl);
  background: transparent;
}

/* The single-pick radio — see the desktop twin, which carries the ruling. */
.rd-wizm__pickbox--round {
  border-radius: 50%;
}

.rd-wizm__pickbox--on {
  border-color: var(--rd-acc);
  background: var(--rd-acc);
}

/* `flex: 1, fontSize: 12.5, color: t.fg` (`rd-events-lab-mobile.jsx:86`). */
/* The phone's half of the 28 Aug `Made by` line — see `.rd-wiz__picktxt`.
   `.rd-wizm__copysub`'s face at this width: 9.5 in `t.fg3`. */
.rd-wizm__picktxt {
  flex: 1;
  min-width: 0;
  display: block;
}

.rd-wizm__pick-made {
  display: block;
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rd-wizm__pick-name {
  display: block;
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* `pdisp(700, 78, '.1em')` at 8.5, `t.fg3`, `flex: 'none'`
   (`rd-events-lab-mobile.jsx:88` ("{tag &&")). */
.rd-wizm__pick-tag {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

/* UNDRAWN — the empty population, in the row band's own padding. */
.rd-wizm__pick-none {
  padding: 14px 16px;
  border-bottom: 1px solid var(--rd-line);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
}

/* The skeleton row in THIS sheet's geometry — the sheet's own `10px 16px`,
   `gap: 10` and 48px floor, carrying the shared mark and the three shared
   widths declared in section 3e.

   ⚠️ **THE BOX IS `.rd-wizm__pick`'s, DECLARATION FOR DECLARATION, AND THAT IS
   THE WHOLE POINT OF THE STATE.** The rule shipped `14px 16px` while claiming
   the sheet's own `10px 16px` in this comment; the panel then shortened by
   about 8px per row the moment the real list landed — the flash a loading state
   exists to prevent. The floor above matters twice over here: it is what keeps
   the placeholder and the row it stands in for the same height. No corpus
   number moves — the phone page stops at frame 11c and draws no picker-loading
   state at this width (11d is desktop only), so the row it copies IS the
   reference. */
.rd-wizm__pickskel {
  display: flex;
  align-items: center;
  gap: 10px;
  box-sizing: border-box;
  min-height: 48px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--rd-line);
}

/* `padding: '12px 16px 40px', display: flex, gap: 12, alignItems: 'center'`
   (`rd-events-lab-mobile.jsx:70` ("12px 16px 40px")).

   ⚠️ THE 40 IS `--rd-sheet-clear` AND NOT A LITERAL. `48 Kit Rules.html:47`:
   *"Nothing tappable sits in the home-indicator zone … The kit owns the
   clearance so no screen has to remember it."* This sheet is VIEWPORT-anchored
   — `.rd-wizm__pickwrap` is `position: fixed; inset: 0` — which is the half of
   `kit-shell.css` §0 that decides it carries the whole 40 itself rather than
   inheriting a shell's 28. `.rd-plan__sheet-card` is the other kind and
   deliberately takes none. */
.rd-wizm__sheet-acts {
  flex: none;
  padding: 12px 16px var(--rd-sheet-clear);
  display: flex;
  gap: 12px;
  align-items: center;
}

/* `fontSize: 11, color: t.fg3, flex: 'none'`
   (`rd-events-lab-mobile.jsx:72` ("Cancel")). */
.rd-wizm__pick-cancel {
  flex: none;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}
/* ---------------------------------------------------------------------------
   DESKTOP · STEP 6 · WHO — `WizWho` (`rd-events-lab.jsx:313`).

   ⚠️ THE STEP INVITES; IT DOES NOT ENROL (James, 26 Aug: *"invite — members get
   asked and RSVP themselves"*), and the geometry says so: a checkbox is a
   question you are asking, and the count under it is `N invited so far`, never
   `N coming`. Nothing here paints a roster.

   ⚠️ AND THERE IS NO FRIENDS ARM. `WizWho` keeps a `friends` prop *"for the
   corpus record"* (`rd-events-lab.jsx:311`); no rule below has a second pool.

   The search box is `RdSearchField` and brings its own face — the two classes
   here are the wrapper's margin and the drawn type size, and nothing else.
   --------------------------------------------------------------------------- */

/* The step's own container. `WizWho`'s children are laid out by their own
   margins (`rd-events-lab.jsx:319` onward), so this is a plain block and the
   rule exists to be a declared class rather than an implied one. */
.rd-wiz__who { display: block; }

/* The heading row — `display: flex, alignItems: 'baseline', justifyContent:
   'space-between'` (`rd-events-lab.jsx:320` ("alignItems: 'baseline'")). */
.rd-wiz__whohead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

/* The org's headcount at the right — `fontSize: 10.5, color: t.fg3`
   (`rd-events-lab.jsx:322` ("318 members")). The NUMBER is the org's whole
   headcount and not the page's length; the Rust side states why. */
.rd-wiz__whocount {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
}

/* `marginBottom: 10` (`rd-events-lab.jsx:324` ("marginBottom: 10")). */
.rd-wiz__whosearch {
  margin-bottom: 10px;
}

/* `fontSize: 12.5` (`rd-events-lab.jsx:324` ("fontSize: 12.5")) — a step down
   from the kit field's 16, which is the console's own scale and the same step
   `.rd-mem-search` takes on the Members page. */
.rd-wiz__whosearch-input {
  font-size: 12.5px;
}

/* `display: 'grid', gap: 6` (`rd-events-lab.jsx:325`). */
.rd-wiz__wholist {
  display: grid;
  gap: 6px;
}

/* One member — `gap: 14, padding: '11px 16px'`, the `wb` off-arm ground
   (`rd-events-lab.jsx:327` ("padding: '11px 16px'")). A `button`, because it
   toggles. */
.rd-wiz__whorow {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  box-sizing: border-box;
  padding: 11px 16px;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  border-radius: 0;
  text-align: left;
  cursor: pointer;
}

/* Checked — `t.surf2` under a `t.fg` border (`rd-events-lab.jsx:327`, `on`). */
.rd-wiz__whorow--on {
  background: var(--rd-surf2);
  border-color: var(--rd-fg);
}

/* ⚠️ NOT DIMMED. A row already on the roster is inert because un-inviting is
   `RemoveRosterEntry` and lives on the event page — but it is a CHECKED row,
   not an unavailable one, so it keeps the checked face and loses only the
   pointer. Dimming it would say the person cannot be invited. */
.rd-wiz__whorow:disabled { cursor: default; }

.rd-wiz__whorow:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* The checkbox mark — `16 x 16`, SQUARE, `1.5px t.ctrl`, volt-filled under a
   `t.acc` edge when checked (`rd-events-lab.jsx:328` ("width: 16, height: 16")).
   Square is the whole difference from step 3's round radio: one asks several
   people, the other picks one range. */
.rd-wiz__whobox {
  width: 16px;
  height: 16px;
  flex: none;
  border: 1.5px solid var(--rd-ctrl);
  background: transparent;
}

.rd-wiz__whobox--on {
  border-color: var(--rd-acc);
  background: var(--rd-volt);
}

/* The member's name — 13.5px `t.fg` (`rd-events-lab.jsx:329` ("fontSize: 13.5")). */
.rd-wiz__whoname {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
}

/* The pool word — `pdisp(700, 78, '.1em')` at 8.5px in `t.fg3`
   (`rd-events-lab.jsx:330` ("fontSize: 8.5")). */
.rd-wiz__whotag {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

/* Nothing to draw. Undrawn on this step — the borrowed sentence is the Members
   page's, and it is flagged in the PR body. */
.rd-wiz__whoempty {
  padding: 13px 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
}

/* `marginTop: 16` (`rd-events-lab.jsx:334` ("marginTop: 16")) — the shared
   quote rule carries no top margin, because When's sits directly under a field
   row. Compounds rather than a modifier: the margin is a fact about where this
   quote SITS, not a second kind of quote. */
.rd-wiz__who .rd-wiz__quote { margin-top: 16px; }
/* One line, not two — the 5px that separates a lead from a body has nothing to
   separate here. */
.rd-wiz__who .rd-wiz__quote-body { margin-top: 0; }

/* ---------------------------------------------------------------------------
   PHONE · STEP 6 · WHO — `MWizWho` (`rd-events-lab-mobile.jsx:264`).

   `MPickRow` with `box` (`rd-events-lab-mobile.jsx:277` ("box />")) — the same
   row the phone's Where step draws, with a SQUARE mark instead of a round one.
   Scale moves; nothing else does.
   --------------------------------------------------------------------------- */

/* The step's own container — see the desktop rule. */
.rd-wizm__who { display: block; }

/* `alignItems: 'center'` here, against the desktop's baseline
   (`rd-events-lab-mobile.jsx:271` ("alignItems: 'center'")). */
.rd-wizm__whohead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

/* `fontSize: 9.5` (`rd-events-lab-mobile.jsx:273` ("318 members")). */
.rd-wizm__whocount {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* `marginBottom: 7` (`rd-events-lab-mobile.jsx:275` ("marginBottom: 7")). */
.rd-wizm__whosearch {
  margin-bottom: 7px;
}

/* `msb` at `fontSize: 12` (`rd-events-lab-mobile.jsx:16` ("const msb")). */
.rd-wizm__whosearch-input {
  font-size: 12px;
}

/* `display: 'grid', gap: 5` (`rd-events-lab-mobile.jsx:276`). */
.rd-wizm__wholist {
  display: grid;
  gap: 5px;
}

/* `MPickRow` — `gap: 10, padding: '10px 12px'`
   (`rd-events-lab-mobile.jsx:82` ("padding: sheet ? '10px 16px' : '10px 12px'")).

   ⚠️ `min-height: 48px` IS THE KIT'S FLOOR, NOT THE DRAWING'S NUMBER. This is
   the SAME drawn row as `.rd-wizm__loc`, which carries the floor and states the
   whole reasoning; Where's copy of it stood at 48 and Who's at the drawn ~42
   for no reason either drawing gives. */
.rd-wizm__whorow {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  box-sizing: border-box;
  min-height: 48px;
  padding: 10px 12px;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  border-radius: 0;
  text-align: left;
  cursor: pointer;
}

.rd-wizm__whorow--on {
  background: var(--rd-surf2);
  border-color: var(--rd-fg);
}

/* See the desktop rule: inert, never dimmed. */
.rd-wizm__whorow:disabled { cursor: default; }

.rd-wizm__whorow:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* `13 x 13`, square because `box` is set, `1.5px`, filled `t.acc` when checked
   (`rd-events-lab-mobile.jsx:83` ("width: 13, height: 13")). */
.rd-wizm__whobox {
  width: 13px;
  height: 13px;
  flex: none;
  border: 1.5px solid var(--rd-ctrl);
  background: transparent;
}

.rd-wizm__whobox--on {
  border-color: var(--rd-acc);
  background: var(--rd-acc);
}

/* `fontSize: 12.5, color: t.fg` (`rd-events-lab-mobile.jsx:85`). */
.rd-wizm__whoname {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
}

/* `pdisp(700, 78, '.1em')` at 8.5px (`rd-events-lab-mobile.jsx:88`). */
.rd-wizm__whotag {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

/* Undrawn — see the desktop rule. */
.rd-wizm__whoempty {
  padding: 11px 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* `marginTop: 12` (`rd-events-lab-mobile.jsx:279` ("marginTop: 12")). */
.rd-wizm__who .rd-wizm__quote { margin-top: 12px; }
.rd-wizm__who .rd-wizm__quote-body { margin-top: 0; }

/* ===========================================================================
   STEP 5 · GEAR (S19) — `WizGear` / `MWizGear`

   ⚠️ Cited by screen and component NAME, not by lab line number, for the reason
   the Rust side's S19 section header states: the corpus ledger records
   `rd-events-lab.jsx` at 491 lines against a live 621, so a line citation above
   491 fails `rangeday check citations` and one below it resolves GREEN against
   the wrong line; `rd-events-lab-mobile.jsx` is not in the ledger at all.

   The step is four blocks in flow, and the drawing spaces them with explicit
   `marginTop`s rather than a container gap — so this section does too, and the
   heading's own `margin-bottom: 8px` (`.rd-wiz__label`, unchanged) is the
   drawn `wl(t)` one. No `@media` rule: this part has none, by the file's own
   ruling, and the two widths are two class families the Rust side picks
   between.
   =========================================================================== */

.rd-wiz__gear {
  display: block;
}

/* `display: flex, alignItems: baseline, justifyContent: space-between` — the
   heading and its act on one line (`rd-events-lab.jsx`, `WizGear`). */
.rd-wiz__gearhead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

/* `pdisp(700, 78, '.08em')` at 9px in `t.acc` — the desktop's bare accent caps,
   the same shape `.rd-wiz__copydoor-act` wears.

   ⚠️ Unlike that one, this act TAPS: it appends a row. So it takes the button
   reset, `cursor: pointer` and a hit-area extension that the copy door
   deliberately refuses. The extension is padding plus a negative margin — the
   kit's own ruling (`components/kit/button.rs`, `hit_target_height_px`):
   *"platform minimums are met by non-visual hit-area extension; the painted
   geometry stays the designer's."* */
.rd-wiz__gearact {
  flex: none;
  border: 0;
  background: none;
  cursor: pointer;
  padding: 11px 0 11px 12px;
  margin: -11px 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9px;
  color: var(--rd-acc);
}

/* `display: grid, gap: 6` (`rd-events-lab.jsx`, `WizGear`). */
.rd-wiz__gearlist {
  display: grid;
  gap: 6px;
}

/* One item — `gap: 14, padding: '12px 16px'` on the `t.field` ground under a
   `t.ctrl` stroke. The row itself is NOT a control: the three things inside it
   are, which is why it stays a `div` and carries no cursor. */
.rd-wiz__gearrow {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 16px;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
}

/* The name cell — `flex: 1, minWidth: 0`, `pbody` at 13.5 in `t.fg`.

   ⚠️ The doubled selector is this part's standing guard for a text `input`:
   `text` is on the legacy attribute list, and one-class specificity loses to a
   UA rule. `.rd-wizm .rd-wizm__text` states it; the pair here is the same call.

   ⚠️ Transparent ground, NOT `t.field` again — the ROW paints the field, and a
   box that repainted it would draw a second border's worth of edge inside one
   that is already there. The drawing paints the row and lets the cells sit on
   it, and these are cells that happen to be typeable. */
.rd-wiz .rd-wiz__gearname {
  flex: 1;
  min-width: 0;
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 400;
  font-size: 13.5px;
  line-height: 1.2;
  color: var(--rd-fg);
  outline: none;
  appearance: none;
  -webkit-appearance: none;
}

/* The quantity cell — `mono` at 13 in `t.fg2`, and this file's spelling of
   `mono` is Archivo with tabular figures (`.rd-wiz__step-n` sets the precedent).
   Shrink-to-fit, because the drawn cell is: the name takes the slack.

   ⚠️ `1 each` and `150 rounds, 9mm` are quantities here, so this is `text` and
   never `number` — the Rust side says the same thing beside the box. */
.rd-wiz .rd-wiz__gearqty {
  flex: none;
  width: 11ch;
  text-align: right;
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 400;
  font-size: 13px;
  line-height: 1.2;
  color: var(--rd-fg2);
  outline: none;
  appearance: none;
  -webkit-appearance: none;
}

/* The drawn `—` arm: an empty quantity is painted `t.fg3` where a filled one is
   `t.fg2`. As a placeholder rather than a value — see the Rust side. */
.rd-wiz .rd-wiz__gearname::placeholder,
.rd-wiz .rd-wiz__gearqty::placeholder { color: var(--rd-fg3); }

.rd-wiz .rd-wiz__gearname:focus,
.rd-wiz .rd-wiz__gearqty:focus {
  box-shadow: none;
  caret-color: var(--rd-acc);
  color: var(--rd-fg);
}

/* `Remove` — `pbody` at 12 in `t.fg3`, and a real act, so the same hit-area
   extension `.rd-wiz__gearact` takes. */
.rd-wiz__gearrm {
  flex: none;
  border: 0;
  background: none;
  cursor: pointer;
  padding: 11px 0 11px 4px;
  margin: -11px 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
}

/* `marginTop: 14`, `display: flex, alignItems: baseline, gap: 12` — the act
   word and its caption on one line at this width. */
.rd-wiz__suggest {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-top: 14px;
}

/* `pdisp(700, 78, '.08em')` at 9px in `t.acc`.

   ⚠️ THIS ACT TAPS AS OF BATCH 29 — it was a `span` with no cursor and no
   hit area, and its Rust side is a `button` now: the press reveals the
   suggestion list (it adds nothing — see `GEAR_SUGGEST_NOTE`). So it takes the
   button reset, `cursor: pointer` and the non-visual hit-area extension
   `.rd-wiz__gearact` and `.rd-wiz__copydoor-act` already wear. The extension is
   VERTICAL ONLY here: the caption sits beside the word on this width, and a
   left/right pad would move the drawn 12px gap between them. */
.rd-wiz__suggest-act {
  flex: none;
  border: 0;
  background: none;
  cursor: pointer;
  padding: 11px 0;
  margin: -11px 0;
  text-align: left;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9px;
  color: var(--rd-acc);
}

/* `pbody` at 11 in `t.fg3`. */
.rd-wiz__suggest-note {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

/* ── the revealed suggestion list (batch 29) ───────────────────

   ⚠️ UNDRAWN FRAME. `52 Events Lab` draws no chooser for `Suggest from
   drills` at either width, so nothing here is a paint copied from a drawing:
   the list borrows `.rd-wiz__gearlist`'s 6px grid and `.rd-wiz__gearrow`'s
   geometry outright, and the modifier says the row is an OFFER rather than an
   item — dashed, the same word `.rd-wiz__copydoor` spends on a thing that is
   not yet anything. Flagged in the PR body for the designer. */
.rd-wiz__suggestlist {
  display: grid;
  gap: 6px;
  margin-top: 10px;
}

/* The offer, not the item: a dashed edge on the row's own geometry. */
.rd-wiz__gearrow--suggested {
  border-style: dashed;
  background: none;
}

/* The suggested name — `.rd-wiz__gearname`'s face, on a `span`: this cell is
   read, not typed, so it is not an `input` and takes no caret. */
.rd-wiz__suggestname {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  line-height: 1.2;
  color: var(--rd-fg);
}

/* The drawn `—`, in the placeholder's `t.fg3` rather than a filled
   quantity's `t.fg2`: the row this becomes stores NO quantity. */
.rd-wiz__suggestqty {
  flex: none;
  width: 11ch;
  text-align: right;
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  line-height: 1.2;
  color: var(--rd-fg3);
}

/* `Add` — the accent caps of `.rd-wiz__gearact` where `Remove` sits on a
   typed row, with the same hit-area extension. */
.rd-wiz__suggestadd {
  flex: none;
  border: 0;
  background: none;
  cursor: pointer;
  padding: 11px 0 11px 4px;
  margin: -11px 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9px;
  color: var(--rd-acc);
}

/* The shortfall under a floor row, and the line the empty list says instead —
   both `.rd-wiz__suggest-note`'s quiet face. */
.rd-wiz__suggestcap,
.rd-wiz__suggestempty {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.4;
  color: var(--rd-fg3);
}

/* The quiet rule's line — `fontSize: 11.5, color: t.fg3, lineHeight: 1.55`,
   inside the shared `.rd-wiz__quote` rule (2px `t.line`, 14px in, 58ch cap),
   which the drawing paints identically here.

   ⚠️ Its own class rather than `.rd-wiz__quote-body`: that one carries a 5px
   top margin for the second line of a TWO-line quote, and this quote has one
   line. `margin-top: 18px` on the wrapper is the drawn `marginTop: 18`. */
.rd-wiz__gear .rd-wiz__quote {
  margin-top: 18px;
}

.rd-wiz__gearnote {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--rd-fg3);
}

/* ── the phone half — `MWizGear` ─────────────────────────────────────────
   Same four blocks, the drawing's own two differences: the act words are
   bordered accent chips (`macw`) rather than bare caps, and the head row is
   `alignItems: center` where the desktop is `baseline`. */

.rd-wizm__gear {
  display: block;
}

.rd-wizm__gearhead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

/* `macw(t)` — the 1.5px `t.acc` chip at 9.5px with the drawn 36px minimum.
   Unlike `.rd-wizm__copydoor-act`, which is inert, this one taps; 36 is drawn
   and the kit's 48dp floor is about things a thumb answers, so the extra comes
   from padding that does not move a painted pixel. */
.rd-wizm__gearact {
  flex: none;
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0 11px;
  border: 1.5px solid var(--rd-acc);
  border-radius: 0;
  background: none;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9.5px;
  color: var(--rd-acc);
}

/* `display: grid, gap: 5`. */
.rd-wizm__gearlist {
  display: grid;
  gap: 5px;
}

/* `gap: 10, padding: '9px 12px'`, and a 48px floor the drawing's 9px padding
   does not reach on its own — this row holds a tap target (`Remove`) and two
   boxes a thumb has to land in. */
.rd-wizm__gearrow {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 48px;
  padding: 9px 12px;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
}

.rd-wizm .rd-wizm__gearname {
  flex: 1;
  min-width: 0;
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 400;
  font-size: 12px;
  line-height: 1.2;
  color: var(--rd-fg);
  outline: none;
  appearance: none;
  -webkit-appearance: none;
}

/* The quantity is drawn in `mono`, not in the row's body face — `...mono,
   fontSize: 12` (`rd-events-lab-mobile.jsx` `MWizGear`, the qty span). `mono`
   IS THE KIT'S NUMERIC ROLE AND NOT A SYSTEM MONOSPACE: Archivo 800 at 75%
   width with tabular figures (substrate §P2). The itinerary's times two
   sections down spell the same three declarations out — see
   `.rd-wizm__itintime` — and this rule shipped the body weight instead. */
.rd-wizm .rd-wizm__gearqty {
  flex: none;
  width: 9ch;
  text-align: right;
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  line-height: 1.2;
  color: var(--rd-fg2);
  outline: none;
  appearance: none;
  -webkit-appearance: none;
}

.rd-wizm .rd-wizm__gearname::placeholder,
.rd-wizm .rd-wizm__gearqty::placeholder { color: var(--rd-fg3); }

.rd-wizm .rd-wizm__gearname:focus,
.rd-wizm .rd-wizm__gearqty:focus {
  box-shadow: none;
  caret-color: var(--rd-acc);
  color: var(--rd-fg);
}

/* `pbody` at 10.5 in `t.fg3`, with the same non-visual extension. */
.rd-wizm__gearrm {
  flex: none;
  border: 0;
  background: none;
  cursor: pointer;
  padding: 14px 0 14px 4px;
  margin: -14px 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
}

/* `marginTop: 11`, and the caption sits UNDER the chip here rather than beside
   it (`marginTop: 4`) — a 9.5px chip and a sentence do not share a phone line. */
.rd-wizm__suggest {
  margin-top: 11px;
}

/* The chip again — and it TAPS as of batch 29 (it was a `span`; its Rust side
   is a `button` now, and the press reveals the suggestion list rather than
   adding anything). The chip is already 36px tall by the drawing, so the
   button reset is all this needs; the painted geometry is unchanged. */
.rd-wizm__suggest-act {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0 11px;
  border: 1.5px solid var(--rd-acc);
  border-radius: 0;
  background: none;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9.5px;
  color: var(--rd-acc);
}

/* `fontSize: 9.5, color: t.fg3, marginTop: 4, lineHeight: 1.4`. */
.rd-wizm__suggest-note {
  margin: 4px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
}

/* ── the revealed suggestion list, phone (batch 29) ──────────────────
   UNDRAWN, exactly as the desktop half above — the same borrow of the gear
   row's geometry at this width's scale. */
.rd-wizm__suggestlist {
  display: grid;
  gap: 6px;
  margin-top: 9px;
}

.rd-wizm__gearrow--suggested {
  border-style: dashed;
  background: none;
}

.rd-wizm__suggestname {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.2;
  color: var(--rd-fg);
}

.rd-wizm__suggestqty {
  flex: none;
  width: 9ch;
  text-align: right;
  font-family: 'Archivo', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  line-height: 1.2;
  color: var(--rd-fg3);
}

/* `Add`, with `.rd-wizm__gearrm`'s hit-area extension — the phone's is 14px
   where the desktop's is 11. */
.rd-wizm__suggestadd {
  flex: none;
  border: 0;
  background: none;
  cursor: pointer;
  padding: 14px 0 14px 4px;
  margin: -14px 0;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9.5px;
  color: var(--rd-acc);
}

.rd-wizm__suggestcap,
.rd-wizm__suggestempty {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.4;
  color: var(--rd-fg3);
}

/* `marginTop: 13` on the shared `.rd-wizm__quote` rule (2px `t.line`, 11px in
   — both already declared and both drawn identically here). */
.rd-wizm__gear .rd-wizm__quote {
  margin-top: 13px;
}

/* `fontSize: 10.5, color: t.fg3, lineHeight: 1.5` — one line, so its own class
   rather than `.rd-wizm__quote-body`'s 4px second-line margin. */
.rd-wizm__gearnote {
  margin: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   3g. DESKTOP — STEP 7, Itinerary (S20). `rd-events-lab.jsx:365`
   ("function WizItinerary") for the single day, `:491`
   ("function WizItineraryMulti") for the range.

   ONE BLOCK FOR BOTH FRAMES, because 7 IS 7c WITH ONE DAY. The drawings differ
   in three ways and no more, and each is a modifier here: the multi-day heading
   carries a 2px rule (`:502` ("borderBottom")), its rows sit tighter (`:509`
   ("padding: '9px 0'")), and the single-day frame closes with the step's
   sentence where the stacked one does not.

   THE DAY SECTIONS ARE NOT ON THE WIRE. They are derived from When's range at
   render — `event_wizard.rs`'s `itin_day_count` carries the reasoning and the
   wire DTO states the hazard: a client that derives its sections from the
   ENTRIES loses every empty day, and the empty day is drawn.
   --------------------------------------------------------------------------- */
/* `display: grid, gap: 20, alignContent: 'start'` (`rd-events-lab.jsx:499`
   ("gap: 20")). The single-day frame draws no wrapper of its own — its band and
   rows are the shell body's direct children — so the one-section case gets the
   same grid with nothing to space. */
.rd-wiz__itin {
  display: grid;
  gap: 20px;
  align-content: start;
}

/* The multi-day arm declares no geometry of its own; it exists to scope the
   three differences below, so `check css` sees a real declaration for a class
   the markup mounts. */
.rd-wiz__itin--multi {
  gap: 20px;
}

/* One day. A plain block in the drawing (`rd-events-lab.jsx:501` ("key={day}")),
   which is what lets the grid above own every gap. */
.rd-wiz__itinday {
  display: block;
}

/* The day's head — `display: flex, alignItems: 'baseline',
   justifyContent: 'space-between'` (`rd-events-lab.jsx:369`
   ("justifyContent: 'space-between'")). Baseline rather than centred, so a 9px
   act aligns to a 9px label. */
.rd-wiz__itinhead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

/* 7c's band — `borderBottom: 2px solid t.ctrl, paddingBottom: 7`
   (`rd-events-lab.jsx:502` ("2px solid")).

   ⚠️ TWO PIXELS, AND IT IS THE ONE THING THAT MAKES A STACK OF DAYS READ AS
   DAYS. The row hairlines below are 1px `t.line`; the day rule is 2px `t.ctrl`,
   a full step louder, which is how the eye finds where one day ends. The
   single-day frame draws no rule at all because there is nothing to separate. */
.rd-wiz__itinhead--band {
  padding-bottom: 7px;
  border-bottom: 2px solid var(--rd-ctrl);
}

/* `wl(t)` — the same 9px label face this part already ships, with the drawn
   `marginBottom: 8` in the single-day frame (`rd-events-lab.jsx:370`
   ("Sat 8 Aug")) and `marginBottom: 0` under the band (`:503`
   ("marginBottom: 0")).

   The BYTES come from `date_format::list_date`, role 1 — `Sat 8 Aug` is that
   role exactly. Nothing here formats a date. */
.rd-wiz__itinlabel {
  margin-bottom: 8px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .11em;
  font-size: 9px;
  color: var(--rd-fg3);
}

.rd-wiz__itinhead--band .rd-wiz__itinlabel { margin-bottom: 0; }

/* `pdisp(700, 78, '.08em')` at 9px in `t.acc` (`rd-events-lab.jsx:371`
   ("Add entry")) — the face `.rd-wiz__whatact` already carries. A button
   because it opens the entry form; drawn as a word, so it wears no box. */
.rd-wiz__itinadd {
  flex: none;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9px;
  color: var(--rd-acc);
}

.rd-wiz__itinadd:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* The empty day — `fontSize: 11.5, color: t.fg3, padding: '11px 0'`
   (`rd-events-lab.jsx:507` ("Nothing on this day yet")).

   ⚠️ THE DAY STAYS. C-52.2: "empty days legal and visible". A section that
   vanished when it had nothing in it would make the range unreadable and the
   `Add entry` for that day unreachable. */
.rd-wiz__itinempty {
  margin: 0;
  padding: 11px 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg3);
}

/* One entry — `display: flex, alignItems: 'center', gap: 20, padding: '11px 0',
   borderBottom: 1px t.line` (`rd-events-lab.jsx:374` ("gap: 20")).

   ⚠️ NOT `RdRow`, AND THE NAME AVOIDS THE `__row` TAXONOMY on purpose — the
   same call `.rd-wiz__pickedrow` states one section up, and for the same
   reason: `components/kit/pro_table.rs` sweeps admin pages for hand-rolled
   table rows, and this is a hairline-separated line of a schedule, not a table
   row. */
.rd-wiz__itinrow {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 11px 0;
  border-bottom: 1px solid var(--rd-line);
}

.rd-wiz__itin--multi .rd-wiz__itinrow { padding: 9px 0; }

/* The spine — `width: 62, flex: 'none', mono, fontSize: 15, color: t.fg2`
   (`rd-events-lab.jsx:375` ("width: 62")).

   `mono` IS THE KIT'S NUMERIC ROLE, NOT A SYSTEM MONOSPACE: Archivo 800 at 75%
   width with tabular figures — substrate §P2, "numbers are the loudest thing on
   a scoring surface, so they speak in the display voice". `tabular-nums` is
   what makes a column of times line up on the digit, which is the whole point
   of a spine.

   ⚠️ THE FACE IS 24-HOUR AND `date_format` HAS NO 24-HOUR FORMATTER — the value
   is the wire's own `HH:MM` string, rendered as given, because it is a wall
   clock at the range and not an instant anybody may re-zone. `48 Kit Rules`
   rules one clock vocabulary and the drawn spine is 24-hour; reconciling that
   with `clock_12h` is a live designer question (plan gap 134), flagged rather
   than settled here. */
.rd-wiz__itintime {
  width: 62px;
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 15px;
  color: var(--rd-fg2);
}

.rd-wiz__itin--multi .rd-wiz__itintime { font-size: 14px; }

/* `flex: 1, minWidth: 0, pbody at 13.5, t.fg` (`rd-events-lab.jsx:376`) — the
   words give way so the chip and `Remove` never compress. */
.rd-wiz__itinwhat {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
}

.rd-wiz__itin--multi .rd-wiz__itinwhat { font-size: 13px; }

/* `Remove` — undrawn on this frame, and `event_wizard.rs`'s `ITIN_REMOVE_ACT`
   carries the reason and the flag. The face is the one the What step's rows
   already use for the same word (`rd-events-lab.jsx:224` ("Remove")), so
   nothing here is an invented treatment either. */
.rd-wiz__itinremove {
  flex: none;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg3);
}

.rd-wiz__itinremove:hover { color: var(--rd-fg2); }
.rd-wiz__itinremove:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* Frame 7's closing sentence — `fontSize: 10.5, color: t.fg3, marginTop: 14,
   lineHeight: 1.5` (`rd-events-lab.jsx:380` ("marginTop: 14")).

   ⚠️ ITS OWN RULE RATHER THAN `.rd-wiz__hint`, which is 10.5/1.45 at a 6px top
   — three drawn numbers, two of which differ. And it prints on the SINGLE-DAY
   arm only: 7c closes its stacked sections with nothing (`:517`). */
.rd-wiz__itinhint {
  margin: 14px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   3h. DESKTOP — FRAME 7b, the Add Entry modal. `rd-events-lab.jsx:387`
   ("function WizItinAdd").

   THE SHELL IS 4b'S AND THE DRAWING IS WHY: `:393` is `width: 520` over `t.surf`
   behind a `t.line` hairline, byte-for-byte `:248`; the head, the act row and
   the bare `Cancel` match the same way. So `.rd-wiz__pickwrap`,
   `.rd-wiz__pick-scrim`, `.rd-wiz__modal*`, `.rd-wiz__pick*` and `.rd-wiz__door`
   are MOUNTED, not re-declared — two frames, one modal. Only the three rows
   this frame has and that one does not are below.
   --------------------------------------------------------------------------- */
/* The time row — `padding: '16px 22px', borderBottom: 1px t.line, display: flex,
   alignItems: 'center', gap: 16` (`rd-events-lab.jsx:398`
   ("padding: '16px 22px'")). */
.rd-wiz__itintimerow {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px 22px;
  border-bottom: 1px solid var(--rd-line);
}

/* `pdisp(700, 78, '.1em')` at 9.5, `t.fg3`, `width: 48, flex: 'none'`
   (`rd-events-lab.jsx:399` ("width: 48")). A real `<label for>`: this one IS a
   field label beside its control, unlike the two captions below it. */
.rd-wiz__itintimelabel {
  width: 48px;
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* `mono` at 16, `t.fg` (`rd-events-lab.jsx:400` ("13:00")) — the drawn clock is
   a VALUE, and the control under it is a native `time` input because
   `RdTextField`'s `TextFieldKind` is `Text | Password` and nothing else. The
   same wall the two `date` boxes on When met, answered the same way: no box,
   no border, the drawn face on the browser's own control. */
.rd-wiz__itintimebox {
  flex: none;
  border: 0;
  background: none;
  padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 16px;
  color: var(--rd-fg);
  color-scheme: dark light;
}

/* Both section captions — `pdisp(700, 78, '.1em')` at 9.5, `t.fg3`, in a
   `14px 22px 4px` block over the drill list (`rd-events-lab.jsx:402`
   ("padding: '14px 22px 4px'")) and a `14px 22px` one over the words (`:411`).
   One rule, because the drawing draws one face twice and the two must not
   drift. */
.rd-wiz__itincap {
  padding: 14px 22px 8px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* The free-text block — `padding: '14px 22px', borderBottom: 1px t.line`
   (`rd-events-lab.jsx:411` ("padding: '14px 22px'")). The caption inside it
   carries its own padding, so this block owns the sides and the rule only. */
.rd-wiz__itinfree {
  padding: 0 0 14px;
  border-bottom: 1px solid var(--rd-line);
}

/* The box — `background: t.field, border: 1px solid t.ctrl,
   padding: '10px 13px', fontSize: 12.5` (`rd-events-lab.jsx:413`
   ("padding: '10px 13px'")).

   ⚠️ THE DRAWN GREY IS PLACEHOLDER TEXT, NOT THE VALUE'S COLOUR. The frame shows
   the box holding `Lunch, safety brief, awards…` in `t.fg3`, which is the
   sample content — so the placeholder takes `fg3` and a typed value takes `fg`,
   the treatment `NAME_PLACEHOLDER` and `DESC_PLACEHOLDER` already get in this
   file. The box is drawn as `wb(t, 0)` with no second arm, unlike the date and
   Where boxes, so there is no `--on` state to switch to.

   `.rd-wiz .rd-wiz__itinwords` for `.rd-wiz__text`'s specificity reason: `text`
   IS on `_legacy-pre-kit.css`'s attribute list, so a (0,2,0) selector is what
   wins over the legacy bare `input` rule. */
.rd-wiz .rd-wiz__itinwords {
  display: block;
  width: calc(100% - 44px);
  margin: 0 22px;
  box-sizing: border-box;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  border-radius: 0;
  padding: 10px 13px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
}

.rd-wiz .rd-wiz__itinwords::placeholder { color: var(--rd-fg3); }

/* ---------------------------------------------------------------------------
   3i. PHONE — STEP 7 and FRAME 7b (S20). `rd-events-lab-mobile.jsx:287`
   ("function MWizItinerary"), `:385` ("function MWizItineraryMulti"), `:308`
   ("function MWizItinAdd").

   The phone drawing's own note is the whole brief: "39's mono time spine,
   unchanged — it is already a narrow shape" (`:286`). It folds by SCALE, not by
   shape — a 48px spine against 62, 12px words against 13.5 — and the one
   structural difference is the act, which is `macw`'s bordered 36px target
   rather than a bare accent word (`:17`, grown from 8.5px on 26 Aug under the
   kit's tap-target rule).
   --------------------------------------------------------------------------- */
/* `display: grid, gap: 13, alignContent: 'start'`
   (`rd-events-lab-mobile.jsx:393` ("gap: 13")). */
.rd-wizm__itin {
  display: grid;
  gap: 13px;
  align-content: start;
}

.rd-wizm__itin--multi {
  gap: 13px;
}

.rd-wizm__itinday {
  display: block;
}

/* `alignItems: 'center'` at this width, not `baseline`
   (`rd-events-lab-mobile.jsx:291` ("alignItems: 'center'")) — the act is a 36px
   box here and a baseline would hang it off the label's. */
.rd-wizm__itinhead {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* `borderBottom: 2px solid t.ctrl, paddingBottom: 5`
   (`rd-events-lab-mobile.jsx:396` ("paddingBottom: 5")). */
.rd-wizm__itinhead--band {
  padding-bottom: 5px;
  border-bottom: 2px solid var(--rd-ctrl);
}

/* `msl(t)` — 8.5px, `marginBottom: 5` (`rd-events-lab-mobile.jsx:292`
   ("Sat 8 Aug")), and `marginBottom: 0` under the band (`:397`). */
.rd-wizm__itinlabel {
  margin-bottom: 5px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .11em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

.rd-wizm__itinhead--band .rd-wizm__itinlabel { margin-bottom: 0; }

/* `macw(t)` — 9.5px accent inside a 1.5px accent outline, `minHeight: 36`,
   `padding: '0 11px'` (`rd-events-lab-mobile.jsx:17` ("minHeight: 36")). The
   same act `.rd-wizm__whatact` already carries; declared again rather than
   shared because the two belong to two slices and a face reached across them is
   a face somebody edits for one of them. */
.rd-wizm__itinadd {
  flex: none;
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0 11px;
  background: none;
  border: 1.5px solid var(--rd-acc);
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9.5px;
  color: var(--rd-acc);
}

.rd-wizm__itinadd:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* `fontSize: 10, color: t.fg3, padding: '8px 0'`
   (`rd-events-lab-mobile.jsx:401` ("padding: '8px 0'")). */
.rd-wizm__itinempty {
  margin: 0;
  padding: 8px 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  color: var(--rd-fg3);
}

/* `gap: 12, padding: '10px 0'` single-day (`rd-events-lab-mobile.jsx:296`
   ("padding: '10px 0'")), `'7px 0'` stacked (`:403`). */
.rd-wizm__itinrow {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--rd-line);
}

.rd-wizm__itin--multi .rd-wizm__itinrow { padding: 7px 0; }

/* `width: 48, mono, fontSize: 13.5` (`rd-events-lab-mobile.jsx:297`
   ("width: 48")), `width: 44, fontSize: 12` stacked (`:404`). */
.rd-wizm__itintime {
  width: 48px;
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 13.5px;
  color: var(--rd-fg2);
}

.rd-wizm__itin--multi .rd-wizm__itintime { width: 44px; font-size: 12px; }

/* `flex: 1, minWidth: 0, fontSize: 12` (`rd-events-lab-mobile.jsx:298`), and
   the stacked frame spells the ellipsis out at 11.5 (`:405`
   ("textOverflow: 'ellipsis'")). Carried on both, because a narrow row that
   wraps a title is a row whose spine stops lining up. */
.rd-wizm__itinwhat {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg);
}

.rd-wizm__itin--multi .rd-wizm__itinwhat { font-size: 11.5px; }

/* Undrawn, like its desktop twin — `ITIN_REMOVE_ACT` carries the flag. */
.rd-wizm__itinremove {
  flex: none;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
}

.rd-wizm__itinremove:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* `fontSize: 9.5, color: t.fg3, marginTop: 11, lineHeight: 1.45`
   (`rd-events-lab-mobile.jsx:302` ("marginTop: 11")) — single-day only, the
   same as the desktop. */
.rd-wizm__itinhint {
  margin: 11px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.45;
  color: var(--rd-fg3);
}

/* Frame 7b in the sheet. The sheet itself is the What picker's — the drawing
   mounts the same `MSheet` (`rd-events-lab-mobile.jsx:311` against `:222`) — so
   only the rows this frame adds are declared.

   `padding: '12px 16px', gap: 12, borderBottom: 1px t.line` (`:312`). */
.rd-wizm__itintimerow {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--rd-line);
}

/* `width: 38, flex: 'none'`, 8.5px (`rd-events-lab-mobile.jsx:313` ("width: 38")). */
.rd-wizm__itintimelabel {
  width: 38px;
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

/* `mono` at 14 (`rd-events-lab-mobile.jsx:314` ("fontSize: 14")). */
.rd-wizm__itintimebox {
  flex: none;
  border: 0;
  background: none;
  padding: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 14px;
  color: var(--rd-fg);
  color-scheme: dark light;
}

/* Both captions — `padding: '11px 16px 3px'` over the list (`:316`) and
   `marginBottom: 6` over the box (`:319`), 8.5px in `t.fg3`. One rule, one
   face, for the desktop caption's reason. */
.rd-wizm__itincap {
  padding: 11px 16px 6px;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

/* `padding: '11px 16px 0'` (`rd-events-lab-mobile.jsx:318`). The sheet's acts
   carry the clearance below it, so this block ends flush — which is what the
   drawn `0` says and what this rule shipped the reverse of, moving the 11px
   from above the block to below its box.

   Nothing is added back on top, because the block's own children already carry
   the drawn numbers: the caption rule above spends the 11 as its own
   `padding-top`, and the box below spends the 16 as its own side margins. So
   the block is a declared class with no spacing left to spend. */
.rd-wizm__itinfree {
  padding: 0;
}

/* `msb(t, 0)` — `background: t.field, border: 1px solid t.ctrl,
   padding: '9px 11px', fontSize: 12` (`rd-events-lab-mobile.jsx:16`
   ("padding: '9px 11px'")). The drawn grey is the placeholder, exactly as at
   desktop width. */
.rd-wizm .rd-wizm__itinwords {
  display: block;
  width: calc(100% - 32px);
  margin: 0 16px;
  box-sizing: border-box;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
  border-radius: 0;
  padding: 9px 11px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg);
}

.rd-wizm .rd-wizm__itinwords::placeholder { color: var(--rd-fg3); }

/* ---------------------------------------------------------------------------
   DESKTOP · STEP 8 · DOCUMENTS — `WizDocs` (`rd-events-lab.jsx:429`), its empty
   arm (`:437`) and the states frame `WizDocsStates` (`:546`).

   ⚠️ THE STEP UPLOADS AND OFFERS; IT TRACKS NOTHING. The drawing's own source
   comment: *"The reason the step exists is waivers and liability forms …
   Upload and offer only — no signature tracking is claimed."*
   (`rd-events-lab.jsx:425-428`). No rule below paints an acknowledgment, a
   signature line or a read receipt, because there is nothing to paint.

   ⚠️ NO PROGRESS BAR, AND ITS ABSENCE IS A DECISION. Frame 11e draws a 3px
   accent bar filled to 62% under an `Uploading — 62%` label
   (`rd-events-lab.jsx:559-560`). `upload_bound::put_presigned` emits no
   progress events, so the percentage cannot be measured — and a bar that
   cannot be filled truthfully is the one thing this slice was told not to ship.
   The row keeps the drawn label without the number and takes the kit's 13px
   sweep, `43 Loading States.html:53`'s ruled treatment for a control in flight
   beside its unchanged label. Gap 141; `event_wizard.rs` states it in full.

   The row is the drawn FIELD family — `t.field` ground behind a square `t.ctrl`
   hairline, `12px 16px` — not `RdRow` and not a table row, and its class names
   avoid the `__row` / `__head` taxonomy `pro_table.rs` sweeps admin pages for.
   Same call, same reasoning, as `.rd-wiz__pickedrow` one step back.
   --------------------------------------------------------------------------- */

/* The step's own container — children are laid out by their own margins
   (`rd-events-lab.jsx:432` onward), so this is a plain block and the rule
   exists to be a declared class rather than an implied one. */
.rd-wiz__docs { display: block; }

/* The heading row — `display: flex, alignItems: 'baseline',
   justifyContent: 'space-between'` (`rd-events-lab.jsx:433`). */
.rd-wiz__dochead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

/* `pdisp(700, 78, '.08em')` at 9px in `t.acc` (`rd-events-lab.jsx:435`
   ("Upload a document"), and `:568` ("Choose another file") on the refused
   row) — the same face `.rd-wiz__whatact` carries. A LABEL rather than a
   button, because the control underneath is a real `<input type="file">`:
   clicking a label opens the picker for the input it wraps. */
.rd-wiz__docact {
  flex: none;
  position: relative;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9px;
  color: var(--rd-acc);
}

/* ⚠️ THE RING IS ON THE LABEL, because the control the keyboard lands on is
   invisible. Ruled 2026-08-14 and already shipped for `.rd-drillm__up`, whose
   rule states the whole reasoning — `:focus-within` rather than
   `:focus-visible`, because a label is not focusable and the latter would never
   match. Safe as an `outline` here: neither of these two labels carries a
   `clip-path` to eat the stroke. */
.rd-wiz__docact:focus-within,
.rd-wiz__docdoor:focus-within {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* The empty arm's volt door (`rd-events-lab.jsx:442` ("Upload A Document")).
   Wears `.rd-pro-top__btn` for the fill and the chamfer and `.rd-wiz__door` for
   the drawn `10px 18px` at 11px, exactly as the What step's door does; this
   adds only the containing block the hidden input needs. */
.rd-wiz__docdoor { position: relative; }

/* The real `<input type="file">`, kept rather than reimplemented, and hidden
   the ONE way that leaves it focusable — `display: none` and
   `visibility: hidden` both take it out of the tab order, which is the defect
   `.rd-drillm__file`'s own rule records. */
.rd-wiz__docfile,
.rd-wizm__docfile {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  opacity: 0;
  pointer-events: none;
}

/* Frame 8 — `maxWidth: '54ch', paddingTop: 26` (`rd-events-lab.jsx:438`). Not
   `RdEmptyState`, for the reason `.rd-wiz__whatempty` states: that primitive
   centres on both axes at a 420 bound and has no action slot by ruling. */
.rd-wiz__docempty {
  max-width: 54ch;
  padding-top: 26px;
}

/* `pdisp(800, 75, '.02em')` at 17px, `t.fg` (`rd-events-lab.jsx:439`
   ("No documents yet")). */
.rd-wiz__docempty-head {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 75%;
  letter-spacing: .02em;
  font-size: 17px;
  color: var(--rd-fg);
}

/* `fontSize: 12.5, color: t.fg3, marginTop: 10, lineHeight: 1.6`
   (`rd-events-lab.jsx:440`). */
.rd-wiz__docempty-body {
  margin: 10px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--rd-fg3);
}

/* `display: flex, gap: 10, marginTop: 18` (`rd-events-lab.jsx:441`) — ONE door
   here where the What step draws two, which is the drawing's own count. */
.rd-wiz__docempty-acts {
  display: flex;
  gap: 10px;
  margin-top: 18px;
}

/* `display: grid, gap: 6` (`rd-events-lab.jsx:446`, and `:553` for the states
   frame — one list, whatever state its rows are in). */
.rd-wiz__doclist {
  display: grid;
  gap: 6px;
}

/* One document — `gap: 14, padding: '12px 16px'`, the `field` ground behind a
   `ctrl` hairline (`rd-events-lab.jsx:448`, `:554`). */
.rd-wiz__docrow {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 16px;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
}

/* Refused — the hairline goes danger and nothing else moves
   (`rd-events-lab.jsx:562` ("border: '1px solid #FF5A3C'")). The row keeps its
   place, its name and its acts: 11e draws the refusal *"as the same rows they
   interrupt"*. */
.rd-wiz__docrow--bad { border-color: var(--rd-danger); }

/* `flex: 1, minWidth: 0` (`rd-events-lab.jsx:449`) — the name gives way and the
   acts never compress. */
.rd-wiz__docmain {
  flex: 1;
  min-width: 0;
}

/* `pbody` at 13.5, `t.fg` (`rd-events-lab.jsx:450`, `:556`). */
.rd-wiz__docname {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
}

/* `PDF · 240 KB` — `fontSize: 10.5, color: t.fg3, marginTop: 3`
   (`rd-events-lab.jsx:451`). The in-flight row's label wears the same rule at
   the drawn `marginTop: 6` below. */
.rd-wiz__docmeta {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
}

/* The in-flight line — the drawn label at `marginTop: 6`
   (`rd-events-lab.jsx:560`), with the 13px sweep beside it where the drawn bar
   and percentage would be. See the section header. */
.rd-wiz__docwork {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
}

.rd-wiz__docwork .rd-wiz__docmeta { margin-top: 0; }

/* The refusal, under the name in the danger ink (`rd-events-lab.jsx:566`
   ("color: '#FF5A3C'")) — the server's own sentence, which is also the drawn
   one. */
.rd-wiz__docbad {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-danger);
}

/* `marginTop: 18` (`rd-events-lab.jsx:458` ("borderLeft")) — the shared quote
   rule, at this step's drawn spacing. One line, so the lead's 5px separation
   has nothing to separate. */
.rd-wiz__docs .rd-wiz__quote { margin-top: 18px; }
.rd-wiz__docs .rd-wiz__quote-body { margin-top: 0; }

/* ---------------------------------------------------------------------------
   PHONE · STEP 8 · DOCUMENTS — `MWizDocs` (`rd-events-lab-mobile.jsx:327`).

   The phone drops frame 11e and reads every word of the step from the desktop
   drawing — the phone file's own binding rule — so the states here are the
   desktop's in this width's geometry, and only the numbers below are new.
   --------------------------------------------------------------------------- */

.rd-wizm__docs { display: block; }

/* `alignItems: 'center', justifyContent: 'space-between'`
   (`rd-events-lab-mobile.jsx:331`) — CENTRED here where the desktop is
   baseline-aligned, because the act is a 36px box at this width. */
.rd-wizm__dochead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

/* `macw` — `pdisp(700, 78, '.08em')` at 9.5, `t.acc`, `minHeight: 36`,
   `padding: '0 11px'`, `border: 1.5px solid t.acc`
   (`rd-events-lab-mobile.jsx:17`, mounted at `:333`). The 1.5px stroke and the
   36px minimum are what make it a target at 9.5px — the 26 Aug ruling that
   retired the bare accent word at this width. A LABEL, for the reason the
   desktop rule gives. */
.rd-wizm__docact {
  flex: none;
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0 11px;
  border: 1.5px solid var(--rd-acc);
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9.5px;
  color: var(--rd-acc);
}

.rd-wizm__docact:focus-within,
.rd-wizm__docdoor:focus-within {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* Frame 8 at phone width — `paddingTop: 18`
   (`rd-events-lab-mobile.jsx:336`). No `maxWidth`: the screen already is one. */
.rd-wizm__docempty { padding-top: 18px; }

/* `pdisp(800, 75, '.02em')` at 15, `t.fg` (`rd-events-lab-mobile.jsx:337`). */
.rd-wizm__docempty-head {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 75%;
  letter-spacing: .02em;
  font-size: 15px;
  color: var(--rd-fg);
}

/* `fontSize: 11.5, color: t.fg3, marginTop: 8, lineHeight: 1.5`
   (`rd-events-lab-mobile.jsx:338`). */
.rd-wizm__docempty-body {
  margin: 8px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

/* The volt door — `padding: '10px 4px'`, centred, 10.5px, `tcham(7)` at
   `marginTop: 14` (`rd-events-lab-mobile.jsx:339`). Wears `.rd-pro-top__btn`
   and `.rd-wizm__door` for the fill, the chamfer and the drawn padding; this
   adds the full width the drawn door has at this scale and the containing
   block the hidden input needs. */
.rd-wizm__docdoor {
  display: flex;
  position: relative;
  margin-top: 14px;
}

/* `display: grid, gap: 5` (`rd-events-lab-mobile.jsx:342`). */
.rd-wizm__doclist {
  display: grid;
  gap: 5px;
}

/* `padding: '9px 12px', gap: 10`, the same field ground and hairline as the
   desktop row (`rd-events-lab-mobile.jsx:344`). */
.rd-wizm__docrow {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  background: var(--rd-field);
  border: 1px solid var(--rd-ctrl);
}

/* See the desktop rule: the hairline goes danger and the row keeps everything
   else. */
.rd-wizm__docrow--bad { border-color: var(--rd-danger); }

.rd-wizm__docmain {
  flex: 1;
  min-width: 0;
}

/* `fontSize: 12, color: t.fg` (`rd-events-lab-mobile.jsx:346`). */
.rd-wizm__docname {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12px;
  color: var(--rd-fg);
}

/* `fontSize: 9.5, color: t.fg3, marginTop: 2`
   (`rd-events-lab-mobile.jsx:347`). */
.rd-wizm__docmeta {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* The in-flight line at this width — the desktop's rule at the phone's
   spacing. */
.rd-wizm__docwork {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: 4px;
}

.rd-wizm__docwork .rd-wizm__docmeta { margin-top: 0; }

/* The refusal — the desktop's sentence in the phone's sub-line size. */
.rd-wizm__docbad {
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-danger);
}

/* `marginTop: 14` (`rd-events-lab-mobile.jsx:354`). */
.rd-wizm__docs .rd-wizm__quote { margin-top: 14px; }
.rd-wizm__docs .rd-wizm__quote-body { margin-top: 0; }

/* ===========================================================================
   STEP 1b · COPY A PAST EVENT (S22) — `WizCopyPick` / `MWizCopyPick`

   ⚠️ ALMOST NOTHING IS DECLARED HERE, AND THAT IS THE POINT. Frame 1b is drawn
   as frame 4b: the scrim, the 520px modal, its head, its search line, its rows,
   its act row and its bare `Cancel` are the same numbers to the pixel
   (`rd-events-lab.jsx:113`-`:131` against `:247`-`:268`), so this frame mounts
   `.rd-wiz__pickwrap` / `.rd-wiz__modal*` / `.rd-wiz__pick*` — already declared
   above — rather than a second family that can drift from them. At phone width
   the drawing literally mounts the same `MSheet` (`rd-events-lab-mobile.jsx:129`
   against `:222`), so the same reuse holds there.

   What 1b draws that 4b does not is the ROW'S TEXT: 4b is a name and a tag on
   one line, 1b is a name over a grey second line — `Apr 2026 · 4 drills · $25`
   (`rd-events-lab.jsx:109` ("4 drills")). Those three classes, twice, are the
   whole of this section. No `@media` rule: this part has none by the file's own
   ruling, and the two widths are two class families the Rust side picks between.
   =========================================================================== */

/* `flex: 1, minWidth: 0` (`rd-events-lab.jsx:123` ("minWidth: 0")) — the stack
   between the round mark and the row's right edge. `min-width: 0` is what lets
   the two lines below ellipsise inside a flex row rather than pushing it wide. */
.rd-wiz__copytxt {
  flex: 1;
  min-width: 0;
  display: block;
}

/* `pbody` at 13.5 in `t.fg` (`rd-events-lab.jsx:124` ("fontSize: 13.5")) — the
   same face `.rd-wiz__pick-name` carries in the picker beside it. */
.rd-wiz__copyname {
  display: block;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* `pbody` at 10.5 in `t.fg3`, `marginTop: 3` (`rd-events-lab.jsx:125`
   ("marginTop: 3")). */
.rd-wiz__copysub {
  display: block;
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   The same three at phone width — `MPickRow`'s own stack, which is the row
   shape the drawing hands this sheet (`rd-events-lab-mobile.jsx:84`
   ("minWidth: 0")). Only the geometry moves.
   --------------------------------------------------------------------------- */
.rd-wizm__copytxt {
  flex: 1;
  min-width: 0;
  display: block;
}

/* `fontSize: 12.5, color: t.fg` (`rd-events-lab-mobile.jsx:85`
   ("fontSize: 12.5")). */
.rd-wizm__copyname {
  display: block;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* `fontSize: 9.5, color: t.fg3, marginTop: 2` (`rd-events-lab-mobile.jsx:86`
   ("marginTop: 2")). */
.rd-wizm__copysub {
  display: block;
  margin-top: 2px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  color: var(--rd-fg3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   STEP 9 · REVIEW — `WizReview` (`rd-events-lab.jsx:467`), slice S23, DESKTOP

   Eight k/v rows on a 110px label column, each closed by a hairline, with the
   accent `Change` word at the right and Basics' description as a second line.

   ⚠️ NOTHING HERE IS A TABLE. The rows are a k/v summary, not a record list —
   no `ProTable`, no `__row`/`__head` name (`components/kit/pro_table.rs`
   sweeps admin pages for those), and the same `-rev*` naming call the Where and
   Itinerary sections already made one step back.
   --------------------------------------------------------------------------- */

/* `display: 'grid', gap: 0` (`rd-events-lab.jsx:471`). Gap zero and not a
   missing property: the hairline on each row IS the separation, so a gap would
   put air between a rule and the row under it. */
.rd-wiz__review {
  display: grid;
  gap: 0;
  align-content: start;
}

/* `alignItems: 'baseline', gap: 18, padding: '14px 0',
   borderBottom: 1px solid t.line` (`rd-events-lab.jsx:473`).

   ⚠️ BASELINE, not centre, and it is what the two type sizes need: the 9.5px
   label and the 13.5px value sit on one line of text, and centring them would
   float the label off it. The phone's row centres instead — its label is ABOVE
   its value, so there is no shared baseline to sit on. */
.rd-wiz__revrow {
  display: flex;
  align-items: baseline;
  gap: 18px;
  padding: 14px 0;
  border-bottom: 1px solid var(--rd-line);
}

/* `width: 110, flex: 'none'`, `pdisp(700, 78, '.1em')`, 9.5px in `t.fg3`
   (`rd-events-lab.jsx:474` ("width: 110")).

   The 110 is the drawn column and it is what makes eight values start at one
   left edge — the whole readability of the screen. */
.rd-wiz__revkey {
  width: 110px;
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* `flex: 1, minWidth: 0` (`rd-events-lab.jsx:475`). `min-width: 0` is what lets
   a long value shrink inside a flex row instead of pushing `Change` off the
   right edge. */
.rd-wiz__revcell {
  flex: 1;
  min-width: 0;
}

/* `pbody`, 13.5px in `t.fg` (`rd-events-lab.jsx:476`). */
.rd-wiz__revval {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 13.5px;
  color: var(--rd-fg);
}

/* Basics' second line — `fontSize: 11.5, color: t.fg3, marginTop: 4,
   lineHeight: 1.5` (`rd-events-lab.jsx:477`). Drawn on the Basics row alone;
   [`review_rows`] is what decides which row gets one. */
.rd-wiz__revsub {
  margin-top: 4px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--rd-fg3);
}

/* `pdisp(700, 78, '.08em')`, 9px in `t.acc` (`rd-events-lab.jsx:479`).

   A bare accent word with no box and no chamfer — the drawing gives it neither,
   and eight boxed acts in a column would be the chrome the phone's own comment
   rules against. The border-reset trio is `.rd-wiz__itinadd`'s, which is this
   part's shape for an act drawn as a word. */
.rd-wiz__revchange {
  flex: none;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9px;
  color: var(--rd-acc);
}

.rd-wiz__revchange:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* The closing sentence — `fontSize: 11.5, color: t.fg3, marginTop: 18,
   lineHeight: 1.55, maxWidth: '64ch'` (`rd-events-lab.jsx:483`).

   ⚠️ NO LEFT RULE, unlike `.rd-wiz__intro`. The drawing gives every other
   step's opener the quiet accent bar and gives this one none — it closes the
   table rather than introducing it. 64ch, not the intro's 58ch: also drawn. */
.rd-wiz__revnote {
  margin: 18px 0 0;
  max-width: 64ch;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--rd-fg3);
}

/* ⚠️ UNDRAWN — a refused `Set Active`. Planner gap 128: nothing in 52 draws
   what the act says when When was skipped, and the sentence itself comes from
   the server. The treatment borrows `.rd-wiz__err`'s geometry (the 32px body
   gutter, the danger hairline) and drops its act, because a precondition
   refusal has nothing to retry. Designer question, stated in the PR body. */
.rd-wiz__reverr {
  flex: none;
  margin: 0 32px;
  padding: 10px 14px;
  border: 1px solid var(--rd-danger);
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--rd-danger);
}

/* ---------------------------------------------------------------------------
   STEP 9 · REVIEW — `MWizReview` (`rd-events-lab-mobile.jsx:363`), S23, PHONE

   "The desktop's k/v table stacks — label over answer, Change on the right
   edge" (`:361-362`). Same eight rows and the same eight strings; only the
   geometry moves.
   --------------------------------------------------------------------------- */

/* ⚠️ NO GUTTER OF ITS OWN — the page already carries one, and this is the ONE
   step body that tried to carry a second. `MWizShell` sits inside `TShell`'s
   `pad={18}` (`rd-events-lab-mobile.jsx:24`) and Review's rows take no inset
   inside it (`:368`); shipped, `adm0-mobile.css:86` paints that same 18px on
   the console's phone page and every other step body — `.rd-wizm__basics`,
   `.rd-wizm__where`, `.rd-wizm__what` — states no padding at all and rides it.
   A `0 16px` here put Review's rows and their hairlines at 34px where the
   drawing and their eight siblings sit at 18. The sheets' own `10px 16px` is a
   different gutter: those panels are full-bleed and mounted outside this body.
   The desktop twin makes the same call from the other side — `.rd-wiz__review`
   states no padding because `.rd-wiz__body` carries its 32px. */
.rd-wizm__review {
  display: grid;
  gap: 0;
}

/* `alignItems: 'center', gap: 10, padding: '6px 0',
   borderBottom: 1px solid t.line` (`rd-events-lab-mobile.jsx:368`).

   CENTRE here where the desktop is baseline — see that row's note: the label is
   above the value at this width, so the two have no shared baseline. */
.rd-wizm__revrow {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 0;
  border-bottom: 1px solid var(--rd-line);
}

/* `flex: 1, minWidth: 0` (`rd-events-lab-mobile.jsx:369`). */
.rd-wizm__revcell {
  flex: 1;
  min-width: 0;
}

/* `pdisp(700, 78, '.1em')`, 8.5px in `t.fg3`
   (`rd-events-lab-mobile.jsx:370`). */
.rd-wizm__revkey {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 8.5px;
  color: var(--rd-fg3);
}

/* `fontSize: 11.5, color: t.fg, marginTop: 3, whiteSpace: 'nowrap',
   overflow: 'hidden', textOverflow: 'ellipsis'`
   (`rd-events-lab-mobile.jsx:371`).

   ⚠️ ONE LINE, ELLIPSISED, AND THAT IS THE DRAWING'S CALL. The value is a
   summary of a step the reader can open with the word beside it, so the phone
   trades the tail of a long line for eight rows that fit on one screen. */
.rd-wizm__revval {
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--rd-fg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* `pdisp(700, 78, '.08em')`, 9px in `t.acc`, `flex: 'none'`,
   `padding: '8px 0 8px 10px'` (`rd-events-lab-mobile.jsx:375`).

   ⚠️ THE PADDING IS THE TARGET AND THE COMMENT ABOVE IT IS THE RULING
   (`:373-374`): "eight boxes in a stack is chrome; the row itself is the
   target." So the 8px above and below is not spacing — it is what gives a 9px
   word a tappable height. */
.rd-wizm__revchange {
  flex: none;
  border: 0;
  background: none;
  padding: 8px 0 8px 10px;
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 9px;
  color: var(--rd-acc);
}

.rd-wizm__revchange:focus-visible { outline: 2px solid var(--rd-acc); outline-offset: 2px; }

/* `fontSize: 9.5, color: t.fg3, marginTop: 8, lineHeight: 1.45`
   (`rd-events-lab-mobile.jsx:378`). Horizontally flush for `.rd-wizm__review`'s
   reason: the sentence is drawn on the same left edge as the rows above it, and
   the page's 18px is the only gutter either of them takes. */
.rd-wizm__revnote {
  margin: 8px 0 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 9.5px;
  line-height: 1.45;
  color: var(--rd-fg3);
}

/* Undrawn — the desktop rule's twin, in `.rd-wizm__err`'s geometry. */
.rd-wizm__reverr {
  padding: 8px 10px;
  border: 1px solid var(--rd-danger);
  margin-bottom: 8px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10px;
  line-height: 1.4;
  color: var(--rd-danger);
}
/* ==== ADM-RUN · 59 (Mobile) Pro Scoring, frame 1 — the day as a grid · owner: 1-track3 ==== */

/* ---------------------------------------------------------------------------
   The Pro scoring day grid at PHONE width: squads down, the event's stages
   across, scored-of-size in each cell.

   Drawn as `ScreenProScoreDay` in `rd-pro-scoring-lab.jsx:18-60`
   ("function ScreenProScoreDay"), under
   `59 (Mobile) Pro Scoring`. Mounted by `pages/admin/run_day.rs` at
   `/admin/events/:id/run`.

   ⚠️ THIS IS NOT THE CONSOLE TABLE, AND THE PART IS THE PROOF.
   `kit-pro-table.css` paints `.rd-ptable` / `.rd-prow` — a FLEX row stack at
   three desktop densities (13 / 14 / 16), with one destination per row. Frame 1
   is a CSS GRID on a fixed template with a destination per CELL, at 47's 56px
   phone scoring floor. The decision and its three reasons are written out on
   `run_day.rs`'s module head, and the file carries an `ALLOWED` entry in
   `components/kit/pro_table.rs` naming what this element actually is. Nothing
   here overrides a kit rule; nothing here re-declares one either.

   ⚠️ NO SHELL, NO GUTTER, NO GROUND. The frame is `.rd-prom-bare`
   (`adm0-mobile.css:141`, the chromeless root the layout renders for the three
   scoring routes) and the head is `ProPushedScreen`'s. This part paints only
   what hangs under that head.

   ⚠️ NO `@media`. The breakpoint is asked ONCE, in Rust — and on this surface it
   is not asked at all: the tablet arm is HELD (James, 27 Aug, P4), so these
   routes render inside the ordinary Pro rail shell at width. A threshold stated
   here would be a second answer to a question this file's Rust does not yet ask.
   Pinned by `run_day.rs::the_part_declares_no_competing_scroller_and_no_media_query`.

   ⚠️ NO `overflow`, NO `100vh`/`100dvh`. The scroll model is `.rd-prom-bare`'s —
   `min-height: 100dvh` and the DOCUMENT scrolls — and a scrolling box here would
   be a second scroll model on the one surface whose whole point is the full
   device height. `pro/shell.rs::ruling_no_console_part_declares_a_competing_scroller`
   is the sweep that catches it; the same test above states it from this side.

   ⚠️ NO NEW TOKENS. Every value below is an existing legacy token.

   ⚠️ TWO TYPE FLOORS, applied and not transcribed (`01 Foundations` §05, "Two
   floors, one exemption"): mixed-case text never goes below 11px; condensed
   uppercase labels with tracking may go to 9.5px. The drawing's 9.5px mixed-case
   sub-line and foot therefore ship at 11px, and its 9px tracked head strip at
   9.5px. Marked at each site.

   ⚠️ THE GRID TEMPLATE IS NOT HERE. `grid-template-columns` is set inline by the
   page, because the number of stage columns is DATA and `1fr 52px 52px 52px`
   — `rd-pro-scoring-lab.jsx:34` ("gridTemplateColumns") — is the drawn
   three-stage case of it. More than
   three stages has no drawn behaviour — designer question 173 — so the template
   simply widens and this part invents neither a wrap nor a scroller.
   --------------------------------------------------------------------------- */

/* `marginTop: 12` under the head's facts line — `rd-pro-scoring-lab.jsx:33`
   ("marginTop: 12"). */
.rd-prun {
  margin-top: 12px;
}

/* ---------------------------------------------------------------------------
   1. The head strip — `rd-pro-scoring-lab.jsx:34` ("padding: '0 0 8px'").
   --------------------------------------------------------------------------- */
/* `display: 'grid', gridTemplateColumns: '1fr 52px 52px 52px', gap: 8,
   padding: '0 0 8px', borderBottom: 1px t.line`. The template is the page's
   (see the banner); everything else is the drawing's. */
.rd-prun__head {
  display: grid;
  gap: 8px;
  padding: 0 0 8px;
  border-bottom: 1px solid var(--rd-line);
}

/* `tdisp(700, 78, '.11em')` at `fontSize: 9`, `color: t.fg3` — Archivo 78%,
   uppercase, tracked. 9 -> 9.5px: the condensed-uppercase floor, which this
   label qualifies for (it is tracked and it is uppercase). */
.rd-prun__hcell {
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .11em;
  font-size: 9.5px;
  color: var(--rd-fg3);
}

/* `textAlign: 'right'` on the three stage heads — `rd-pro-scoring-lab.jsx:36`
   ("textAlign: ") —
   so each label lands over its own column of numbers. The `Squad` head keeps
   the default. */
.rd-prun__hcell--stage {
  text-align: right;
}

/* ---------------------------------------------------------------------------
   2. The row — a squad, across the stages. `rd-pro-scoring-lab.jsx:39` ("minHeight: 56").
   --------------------------------------------------------------------------- */
/* `display: 'grid', gridTemplateColumns: same, gap: 8, alignItems: 'center',
   minHeight: 56, borderBottom: 1px t.line`.

   ⚠️ 56 IS A FLOOR AND NOT A HEIGHT, and the drawing says so itself
   (`minHeight`). It is 47's phone scoring floor, quoted in this lab's own file
   head: "The phone floor on scoring surfaces is 47's 56px." A fixed 56 would
   clip a squad name that wraps; the floor grows instead. Pinned by
   `run_day.rs::ruling_every_grid_row_clears_the_fifty_six_pixel_floor`. */
.rd-prun__row {
  display: grid;
  gap: 8px;
  align-items: center;
  min-height: 56px;
  border-bottom: 1px solid var(--rd-line);
}

/* `minWidth: 0` — `rd-pro-scoring-lab.jsx:40` ("minWidth: 0") — the words give way so the three
   52px cells never compress. A grid track's `min-width` is `auto` by default,
   which is what lets a long squad name push a fixed column out of shape. */
.rd-prun__squad {
  min-width: 0;
}

/* `tbody` at 12.5, `t.fg` — `rd-pro-scoring-lab.jsx:41` ("fontSize: 12.5, color: t.fg"). */
.rd-prun__squad-name {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
}

/* `{bay} · {size} shooters` — `tbody` at 9.5, `t.fg3`, `marginTop: 2`
   — `rd-pro-scoring-lab.jsx:42` ("{size} shooters"). 9.5 -> 11px: MIXED CASE, so the lower floor
   does not apply to it. */
.rd-prun__squad-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  margin-top: 2px;
}

/* ---------------------------------------------------------------------------
   3. The cell — a squad on a stage, and the only tap target on this screen.
      `rd-pro-scoring-lab.jsx:44-48` ("counts.map((c, i)").
   --------------------------------------------------------------------------- */
/* `height: 44, display: 'flex', alignItems: 'center', justifyContent:
   'flex-end', padding: '0 8px', border: 1px solid t.ctrl, background: t.field,
   mono, fontSize: 12.5, color: t.fg3`.

   ⚠️ 44 INSIDE 56 IS TWO NUMBERS, NOT ONE. The row's 56 is the touch floor; the
   44 is how much of that box is PAINTED as a target, centred in it by the row's
   `align-items: center`. A part that collapsed them would clear neither rule.

   `mono` IS THE KIT'S NUMERIC ROLE, NOT A SYSTEM MONOSPACE: Archivo 800 at 75%
   width with tabular figures — `rd-train-kit.jsx:72` ("const mono ="). The
   tabular figures are what make `5/5` and `2/6` line up down a 52px column,
   which is the whole point of the column being fixed.

   THE BASE STATE IS THE UNSTARTED CELL — `—`, in `t.fg3`. Started and finished
   are the two modifiers below, and the nested ternary they transcribe is
   `c === size ? t.acc : c ? t.fg : t.fg3`. */
.rd-prun__cell {
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding: 0 8px;
  border: 1px solid var(--rd-ctrl);
  background: var(--rd-field);
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 12.5px;
  color: var(--rd-fg3);
  text-decoration: none;
}

/* The cell is a router `Link` at every state, including `--done`: the drawn
   branch is ONE element with two colour sets, and a finished squad-stage is
   exactly where an RO goes to review or void. See `run_day.rs::cell_class`. */
.rd-prun__cell:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* STARTED, not finished — `c ? t.fg`. Same control chrome, body tone: the count
   is a real number now, so it reads like one. */
.rd-prun__cell--part {
  color: var(--rd-fg);
}

/* FINISHED — `c === size ? t.acc`, and the control chrome comes OFF:
   "a full cell goes quiet and flat, accent text — done reads as done"
   — `rd-pro-scoring-lab.jsx:45-47` ("done reads as done"). Transparent ground, hairline stroke, accent
   text. Pinned by
   `run_day.rs::ruling_an_open_cell_draws_as_a_control_and_a_full_cell_does_not`. */
.rd-prun__cell--done {
  background: transparent;
  border-color: var(--rd-line);
  color: var(--rd-acc);
}

/* ---------------------------------------------------------------------------
   4. The foot — `rd-pro-scoring-lab.jsx:53` ("lineHeight: 1.5").
   --------------------------------------------------------------------------- */
/* `tbody` at 9.5, `t.fg3`, `marginTop: 10`, `lineHeight: 1.5`. 9.5 -> 11px:
   mixed case again.

   A PARAGRAPH, NOT A CAPTION — the same reading `ProTableFoot` records for the
   console's own foot slot: it carries the ruling ("A squad is an order, never a
   boundary"), it is not a label on the table above it. The measure is the
   frame's 18px gutters and nothing narrower; the drawn block runs the full
   width of the screen. */
.rd-prun__foot {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.5;
  color: var(--rd-fg3);
  margin: 10px 0 0;
}

/* UNDRAWN — the empty arms. `PSC_SQ` is always two squads, so `59` has no empty
   frame for this screen. The sentences are the console's own (borrowed from
   `event_squads_mobile.rs` and `admin/squads.rs`, see their consts), and the
   treatment is the foot's quiet tone one size up: it is a statement about the
   day, in the place the rows would have been. Designer gaps 192 and 193. */
.rd-prun__none {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg3);
  margin: 16px 0 0;
}

/* ---------------------------------------------------------------------------
/* ---------------------------------------------------------------------------
   5. FRAMES 2 / 2b — a squad on a stage, and the breakdown under a scored row.
      Drawn as `ScreenProScoreSquad` in `rd-pro-scoring-lab.jsx:68-107`
      ("function ScreenProScoreSquad"), mounted by `pages/admin/run_squad.rs` at
      `/admin/events/:id/run/:squad/:stage`.

   ⚠️ ONE ROW, TWO BEHAVIOURS — AND THAT IS WHY THE ROW IS PAGE-LOCAL.
   `rd-pro-scoring-lab.jsx:86` ("minHeight: 56") is ONE element; the `expand`
   arm switches the chevron glyph and the bottom rule and nothing else. Half the
   rows NAVIGATE (an unscored row pushes the recording page) and half DISCLOSE
   (a scored row expands in place). `kit/row.rs` has a row that navigates
   (`RdRowLink`, an `<a>` by construction) and a row that acts (`RdRow`, which
   takes no on-row click, deliberately — `row.rs:730-733`), and no row that
   discloses. Splitting the drawn row across the two would give one drawn
   identity two geometries that agree only by hand. So the row is declared here,
   once, and both elements wear it. The kit want is filed in the PR body; the
   chevron itself is still the kit's (`kit::row_chevron`) rather than a fifth
   inline copy of the same 7x12 path.

   ⚠️ NOTHING HERE RE-DECLARES `kit-row.css`. This part names no `.rd-row*`
   selector and overrides none; `run_squad.rs` mounts `row_chevron()` and paints
   the wrapper it sits in, which is the same division `pages/friends.rs:1135`
   already runs.

   ⚠️ SAME TWO TYPE FLOORS as section 1-4 above (`01 Foundations.html:133`,
   "Two floors, one exemption"): mixed-case never below 11px, condensed
   uppercase with tracking may go to 9.5px. Frame 2b's drawn 10 / 9.5 / 9.5
   therefore all ship at 11px, which flattens three drawn steps into one — the
   scale outranks the drawing, in that section's own words. Marked at each site.
   --------------------------------------------------------------------------- */

/* `marginTop: 10` over the first row — `rd-pro-scoring-lab.jsx:83`
   ("marginTop: 10"). */
.rd-prunsq {
  margin-top: 10px;
}

/* ---------------------------------------------------------------------------
   6. The row — `rd-pro-scoring-lab.jsx:86` ("minHeight: 56").
   --------------------------------------------------------------------------- */
/* `display: 'flex', alignItems: 'center', gap: 10, minHeight: 56,
   borderBottom: 1px solid t.line`.

   ⚠️ 56 IS A FLOOR, and it is 47's phone scoring floor: "The phone floor on
   scoring surfaces is 47's 56px, and it holds on every control here"
   (`59 (Mobile) Pro Scoring.html:27`). A fixed height clips a wrapped name.

   The four `border: 0` / `background: none` / `color: inherit` /
   `text-align: left` declarations are the BUTTON RESET, not geometry: the
   scored arm of this row is a real `<button>` so that a disclosure is reachable
   by keyboard, and a `<button>` arrives with a ground, a border, a centred
   label and its own font. The unscored arm is a router `Link` and needs only
   the last of them. One class, so the two states cannot drift apart. */
.rd-prunsq__row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 56px;
  padding: 0;
  border: 0;
  border-bottom: 1px solid var(--rd-line);
  background: none;
  color: inherit;
  font-family: inherit;
  font-size: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.rd-prunsq__row:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: -2px;
}

/* OPEN — `borderBottom: expand && i === 0 ? 'none' : ...`. The rule is made
   transparent rather than removed: `none` would take 1px out of the flow and
   lurch every row below the one the reader just opened. */
.rd-prunsq__row--open {
  border-bottom-color: transparent;
}

/* `flex: 1, minWidth: 0, tbody, fontSize: 12.5, color: t.fg`
   — `rd-pro-scoring-lab.jsx:87` ("minWidth: 0").

   ⚠️ NO ELLIPSIS, AND THAT IS THE DRAWING'S ANSWER RATHER THAN A GAP. A grid
   track's `min-width` is `auto`, so `minWidth: 0` is what lets a long name give
   way instead of pushing the value and the chevron off the row; what it does
   THEN — wrap, over the 56px floor, which is a floor — is what the drawing
   draws. Clipping it with an ellipsis would need `overflow`, and this part
   declares none (see the banner). Pinned by
   `run_squad.rs::the_part_declares_no_competing_scroller_and_no_media_query`. */
.rd-prunsq__name {
  flex: 1;
  min-width: 0;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 12.5px;
  color: var(--rd-fg);
}

/* `mono, fontSize: 12.5, color: val[1] || t.fg, flex: 'none'`
   — `rd-pro-scoring-lab.jsx:88` ("fontSize: 12.5, color: val[1]"). `mono` is the
   kit's numeric role — Archivo 800 at 75% with tabular figures — and the
   tabular figures are what stop the values shuffling down the column. The
   per-row tone slot (`val[1]`) is the QUALIFIER face — S7's two modifiers
   below. */
.rd-prunsq__val {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 12.5px;
  color: var(--rd-fg);
}

/* ── The qualifier face on this row (S7) ────────────────────────────────────
   `58 (Mobile) Qualifications.html:36` — "The verdict simply takes the value
   slot on qualifier rows — `PASS · 431` where a drill row says `2.31s`. Same
   row, different headline." — routed to this screen by
   `59 (Mobile) Pro Scoring.html:35` ("the qualifier stage inherits 58's face").

   So there is NO new element and no badge: the drawn `color: val[1] || t.fg`
   (`rd-pro-scoring-lab.jsx:88`) is a colour slot on the span this part already
   declares, and these two rules are that slot. The size, the family and the
   tabular figures are inherited unchanged — the row shape is the thing 58:36
   is protecting.

   ⚠️ **FAIL IS WARN, NOT DANGER, AND THE VOCABULARY IS `s58-qual.css`'s.**
   `rd-pro-scoring-lab.jsx:71` hard-codes `#FF5A3C` (`TT.dark.danger`), the same
   literal 58's own fixture uses; `s58-qual.css` reads it as fixture drift
   against `08 (Mobile) Assessments` (*"FAIL in fg, not danger. Danger stays
   reserved for things that are broken"*) and against the shipped
   `admin/progress.rs` warn chip, and paints `.rd-history__row-result--fail`
   with `--rd-warn`. One fact must not have two inks across the two sides of the
   app, so these take the same two tokens. Pinned from the Rust side by
   `run_squad.rs::ruling_the_verdict_takes_the_value_slot_and_the_row_shape_does_not_change`,
   which reads both files.

   Declared here rather than reusing `.rd-history__row-result--*` because that
   name belongs to the shooter's history row: this element is not one, and a
   class whose name lies about its element is the drift the BEM-per-part rule
   exists to stop. The TOKENS are shared; the selectors are not. */
.rd-prunsq__val--pass {
  color: var(--rd-ok);
}

.rd-prunsq__val--fail {
  color: var(--rd-warn);
}

/* TWO USERS, ONE QUIET INK, and both mean "nothing to say here".
   1. The em dash on a row that is neither scored nor next — `t.fg3`,
      `rd-pro-scoring-lab.jsx:90` ("color: t.fg3").
   2. **The UNDECLARED verdict** (S7) — a qualifier run that was asked for the
      word and never given one (`proto/score/v1/score_service.proto:300-308`,
      the third state). It prints the ruled `Not declared`, NOT an em dash: a
      dash would say nothing was shot. It is not a verdict, so it takes no
      state colour — the same call `verdict.rs::history_result_tone` makes. */
.rd-prunsq__val--none {
  color: var(--rd-fg3);
}

/* `Up next` — `tdisp(700, 78, '.1em')` at 9, `t.acc`
   — `rd-pro-scoring-lab.jsx:89` ("Up next"). 9 -> 11px: the word is MIXED CASE,
   so the 9.5 exemption (condensed UPPERCASE with tracking) does not reach it
   and the higher floor applies. The casing is the drawing's and is held that
   way on purpose — `text-transform` is declared `none` rather than left to
   inherit so a later cascade cannot shout it.

   ⚠️ The same word ships at 8.5px on the shooter-side board
   (`s09-board.css:558-566`, `.rd-board__status`). Different file, different
   slice; not changed here, and named so the difference is a thing a reader
   finds rather than a thing they discover. */
.rd-prunsq__up {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: none;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 11px;
  color: var(--rd-acc);
}

/* The chevron's WRAPPER — the glyph inside it is `kit::row_chevron`, the kit's
   own 7x12 path, and this part declares nothing about it. Drawn as a text
   glyph that switches `›` -> `⌄` (`rd-pro-scoring-lab.jsx:91`, "'⌄' : '›'"); a
   quarter turn of the one mark is the same two shapes out of one source. */
.rd-prunsq__chev {
  flex: none;
  display: flex;
  align-items: center;
}

.rd-prunsq__chev--open {
  transform: rotate(90deg);
}

/* ---------------------------------------------------------------------------
   7. The breakdown — frame 2b, `rd-pro-scoring-lab.jsx:94` ("padding: '10px 12px 12px'").
   --------------------------------------------------------------------------- */
/* `border: 1px solid t.line, padding: '10px 12px 12px', marginBottom: 6`.
   RENDER-ALWAYS, COLLECT-NEVER: everything in here is read back, and the one
   control is the void act below — the same rule `47 (Mobile) RO Range Day.html:56`
   states for the shooter-side board. */
.rd-prunsq__breakdown {
  border: 1px solid var(--rd-line);
  padding: 10px 12px 12px;
  margin-bottom: 6px;
}

/* `A 9 · C 2 · D 1 · 0 pen · 2.31s` — `mono` at 11.5, `t.fg2`
   — `rd-pro-scoring-lab.jsx:95` ("0 pen"). A numeric readout, so the mono role
   and its tabular figures; 11.5 clears the mixed-case floor as drawn. */
.rd-prunsq__zones {
  font-family: 'Archivo', system-ui, sans-serif;
  font-weight: 800;
  font-stretch: 75%;
  font-variant-numeric: tabular-nums;
  font-size: 11.5px;
  color: var(--rd-fg2);
}

/* `= 52 pts · hit factor 8.12` — `tbody` at 10, `t.fg2`, `marginTop: 4`
   — `rd-pro-scoring-lab.jsx:96` ("hit factor 8.12"). 10 -> 11px: mixed case. */
.rd-prunsq__derived {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg2);
  margin-top: 4px;
}

/* `Recorded by you · 9:41 am` — `tbody` at 9.5, `t.fg3`, `marginTop: 6`
   — `rd-pro-scoring-lab.jsx:97` ("Recorded by you"). 9.5 -> 11px: mixed case.
   The clock is `date_format::clock_12h`'s, never a hand-rolled one. */
.rd-prunsq__attr {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  margin-top: 6px;
}

/* `Void this score…` — `tdisp(700, 78, '.1em')` at 9.5, `t.danger`,
   `marginTop: 10` — `rd-pro-scoring-lab.jsx:98` ("Void this score"). 9.5 -> 11px:
   mixed case, as above.

   A `<button>` and not the drawn `<div>`: it is pressable and must be reachable
   by keyboard. The reset declarations are the button's, not a treatment; the
   drawn tier-1 act is bare danger-toned text (`48 Kit Rules.html:39`) and grows
   no ground here. */
.rd-prunsq__void {
  display: block;
  margin-top: 10px;
  padding: 0;
  border: 0;
  background: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: none;
  font-weight: 700;
  font-stretch: 78%;
  letter-spacing: .1em;
  font-size: 11px;
  color: var(--rd-danger);
  text-align: left;
  cursor: pointer;
}

.rd-prunsq__void:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* ---------------------------------------------------------------------------
   8. WHAT FRAME 2 DOES NOT DECLARE, AND WHY.

   The foot sentence and the undrawn empty note are `.rd-prun__foot` and
   `.rd-prun__none`, section 4 above — the same treatment, from the same
   drawing, at the same two sites one screen apart: `tbody` 9.5 / `t.fg3` /
   `marginTop: 10` / `lineHeight: 1.5` is `rd-pro-scoring-lab.jsx:53`
   ("lineHeight: 1.5") for the grid and `rd-pro-scoring-lab.jsx:104`
   ("A scored row expands") for this screen. Two classes carrying identical
   declarations in one part is two places to change one paint; the SENTENCE is
   per screen and lives in `run_squad.rs`, the paint is shared. Pinned by
   `run_squad.rs::the_foot_and_the_empty_note_reuse_the_parts_quiet_paint`.

   No `@media`, no `overflow`, no `100vh`/`100dvh`, no new token — the part's
   banner states all four for the whole file and this section holds to them.
   --------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
   9. Frame 5 — the connectivity mark, and the offline block's spacing.
   --------------------------------------------------------------------------- */
/* ⚠️ A NEW SECTION AT THE END OF THE FILE, ON PURPOSE — and it earned its keep.
   S6 and S7 appended sections 5-8 to this part on their own branches while this
   one was open, and S7 amended a rule INSIDE S6's section 6. Numbered blocks
   that grow at the tail merge on their own; an edit inside section 3 would not
   have. The rebase's only conflict was the section NUMBER (both sides wrote a
   `5.`), resolved by renumbering this one to 9 — no rule of either side moved,
   and no selector in this part is declared twice.

   `59 (Mobile) Pro Scoring.html:36`: "Offline is a queue, never a wall. The
   header states Saved / Offline · N waiting." Drawn at
   `rd-pro-scoring-lab.jsx:31` ("Offline · 2 waiting") as
   `{ ...tbody, fontSize: 10.5, color: off ? t.warn : t.fg3, flex: 'none' }`.

   ⚠️ ONLY THE MARK'S OWN TYPE IS HERE. The drawn head row is a lead at `flex: 1`
   beside a mark at `flex: 'none'`, and that row already exists: `.rd-pro-top`
   with `.rd-pro-top__lead` and `.rd-pro-top__action` (`adm0-chrome.css:516-527`,
   `:617`, and the phone arm at `adm0-mobile.css:389-414`). This page mounts the
   mark INTO that slot rather than declaring a second head row, so nothing here
   restates a width, a flex or an alignment. Reuse of the ACT slot for a STATUS
   is designer/kit gap 217 — see `run_day.rs`'s module head.

   10.5 -> 11px: mixed-case body type, the floor this app already applies to the
   line beside it (`.rd-prom .rd-pro-top__sub`, `adm0-mobile.css:418-421`, drawn
   10.5 and shipped 11). The two are drawn the same size and ship the same size.

   `white-space: nowrap` is not in the drawing and is not a taste either: the
   drawn box is `flex: 'none'`, which is a promise that the mark does not give
   ground, and `Offline · 12 waiting` wrapped under a `flex: 1` sub-line is that
   promise broken at exactly the count the mark exists for. */
.rd-prun__conn {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 11px;
  color: var(--rd-fg3);
  white-space: nowrap;
}

/* OFFLINE — `off ? t.warn`. The warn token, and it is the ONLY tone change:
   `--rd-danger` here would say the range had a failure, and row 08's ruling is
   the opposite ("no bars is the range's normal weather",
   `47 (Mobile) RO Range Day.html:178`). Pinned by
   `run_day.rs::ruling_the_offline_banner_is_grey_never_red`, which reads this
   rule as well as the banner's tone — one ruling, two elements. */
.rd-prun__conn--off {
  color: var(--rd-warn);
}

/* The offline block's SPACING, and nothing else. `rd-pro-scoring-lab.jsx:54`
   draws `marginTop: 10` on the box; its ground, its border and its type are
   `.rd-banner` + `.rd-banner--neutral`'s (`p1.css:53-59`, `:108`), which this
   page does not restate — a page-local grey strip is the drift that earned the
   Neutral tone its ruling in the first place (`p1.css:43-49`).

   ⚠️ The drawn block's INNER type (an 8.5px eyebrow over a 10px body) is not
   here either, and it is not lost: `RdBanner` has one message slot, so the two
   drawn lines ship as one line at the kit's 12.5px. Designer/kit gap 218. */
.rd-prun__offline {
  margin-top: 10px;
}
/* ==== adm-run-sheet · 59 (Mobile) Pro Scoring frame E3 — the Add Time sheet · owner: 1-track3 ==== */

/* The drawing is `59 (Mobile) Pro Scoring` frame E3, mounted at
   `59 (Mobile) Pro Scoring.html:77` ("E3 · Add Time") and drawn as the `sheet`
   arm of `ScreenPscGloveE` — rd-pro-scoring-lab.jsx:200 ("if (!sheet)")
   through rd-pro-scoring-lab.jsx:221 ("Save · Back To Squad").
   Component: `AddTimeSheet` in apps/web/src/pages/admin/run_shooter.rs.

   ⚠️ THIS IS A PAGE PART, NOT A KIT PART, AND THAT IS THE §4.3 DECISION MADE.
   The batch plan (`tmp/b23-scoring-plan.md` §4.3 item 3) left the call to this
   slice: E3's panel is `top: 90, bottom: 0` over a scrim — neither the console's
   bottom sheet (`.rd-prom__sheet`, adm0-mobile.css, capped at `max-height: 80%`)
   nor `RdConfirmModal`. It is drawn ONCE, on ONE screen, and its two acts are
   this screen's acts. A kit primitive for a shape with one call site would be a
   name space nobody else can use and a spec nobody else reads.

   ⚠️ EVERYTHING INSIDE IT THAT IS A CONTROL IS THE KIT'S. The value is
   `RdNumericField` over a `DigitBuffer` (kit-numeric-field rules live in
   `_legacy-kit-components-and-screens.css`) and the pad is `RdKeypad`
   (kit-keypad.css). This part contributes the FRAME and the two foot acts, and
   re-spaces the pad by one number — see §4.

   ⚠️ NO `overflow` AND NO VIEWPORT UNIT IN THIS FILE. `pro/shell.rs`'s
   `ruling_no_console_part_declares_a_competing_scroller` reads every `adm*.css`
   part, and an inset panel holding a key grid is exactly the shape it exists to
   catch. The drawing declares no scroller either: the pad is
   `flex: 1, minHeight: 0, alignContent: 'start'`
   (rd-pro-scoring-lab.jsx:214 ("alignContent: 'start'")), so the slack lands in
   the pad and the foot stays put. `min-height: 0` is declared at §4 for that
   reason and not as a decoration. */

/* ---------------------------------------------------------------------------
   1. The overlay wrap — the console's phone-sheet grammar, taken whole.
   --------------------------------------------------------------------------- */

/* ⚠️ THE FOURTH INSTANCE OF ONE GRAMMAR, NOT A FOURTH SPELLING.
   `.rd-prom__overlay` + `.rd-prom__sheet` (adm0-mobile.css), `.rd-wizm__pickwrap`
   + `.rd-wizm__sheet` (adm1-wiz.css) and `.rd-prome-sqe__pickwrap` +
   `.rd-prome-sqe__scrim` + `.rd-prome-sqe__sheet` (adm-evd-mob.css) are the same
   three rules: a `position: fixed; inset: 0; z-index: 40` wrap, an absolute
   scrim inside it, and an absolute panel. This is that, at the same z-index, so
   the console has one stacking answer rather than four.

   What differs — and it is the whole reason this is a page part — is the PANEL:
   the three above are bottom-anchored and capped at `max-height: 80%`; this one
   is anchored at `top: 90px` and runs to the foot. See §2. */
.rd-runsh__wrap {
  position: fixed;
  inset: 0;
  z-index: 40;
}

/* `background: 'rgba(0,0,0,.55)'` — rd-pro-scoring-lab.jsx:205 ("rgba(0,0,0,.55)"),
   and the same literal all three sibling sheets use. A
   dimming of whatever is behind it rather than a surface colour, so it does not
   flip with the theme.

   It is a `<button>` in the markup and not a `<div>`: tapping outside a sheet to
   dismiss it is a pointer affordance, and one that a keyboard cannot reach is
   half an affordance. The UA's own button paint is reset here for the same
   reason `.rd-zdial__btn` resets it. */
.rd-runsh__scrim {
  position: absolute;
  inset: 0;
  padding: 0;
  border: none;
  background: rgba(0, 0, 0, .55);
  cursor: pointer;
}

/* ---------------------------------------------------------------------------
   2. The sheet — TALL, and that is the point.
   --------------------------------------------------------------------------- */

/* rd-pro-scoring-lab.jsx:206 ("bottom: 0, top: 90"):
   `position: absolute, left: 0, right: 0, bottom: 0, top: 90,
    background: t.tex ? '#141414' : t.surf, borderTop: 1px solid t.line,
    display: flex, flexDirection: 'column', padding: '15px 18px 40px'`.

   ⚠️ `top: 90px` IS AN ANCHOR, NEVER A HEIGHT. The drawn frame is a 312x676
   mock; a `height: 586px` transcribed from that arithmetic would be right on one
   device and wrong on every other. Anchoring all four edges lets the panel be
   whatever the real viewport leaves under 90px of scrim — which is also what
   keeps the sheet off `100dvh` and out of the competing-scroller sweep.

   ⚠️ THE 90 IS DELIBERATELY NOT `--rd-home-clear`'s SIBLING TOKEN OR ANY OTHER
   SHARED NUMBER. It is the drawn depth of the scrim above the sheet — how much
   of the tally the reader can still see while the sheet is up — and it matches
   nothing else in the console. Routed as designer gap 213.

   ⚠️ `--rd-surf`, and the drawn `t.tex ? '#141414'` arm is NOT transcribed.
   `t.tex` is the lab's texture toggle, a mock affordance for the drawing's own
   frames; the console has one surface token and this is it.

   ⚠️ THE BOTTOM PAD IS `--rd-sheet-clear` AND NOT A LITERAL 40.
   `48 Kit Rules.html:47`: *"Nothing tappable sits in the home-indicator zone …
   every bottom sheet ends with 40px under its last act"* (James, 25 Aug, from
   first-hand misfires). `kit-shell.css` §0 owns the number as
   `max(40px, env(safe-area-inset-bottom))` so the screens that carry it cannot
   drift apart, and so a device with a deeper real inset gets the deeper one.
   Same call `.rd-wizm__sheet-acts` (adm1-wiz.css), `.rd-prom-sheet__panel`
   (adm-mob.css), `.rd-evsheet__panel` (adm1-draft.css) and `.rd-board__void-sheet`
   (s09-board.css) already record. Pinned by
   `ruling_the_sheet_ends_forty_pixels_above_the_edge`. */
.rd-runsh__sheet {
  position: absolute;
  top: 90px;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  padding: 15px 18px var(--rd-sheet-clear);
  background: var(--rd-surf);
  border-top: 1px solid var(--rd-line);
}

/* ---------------------------------------------------------------------------
   3. The two lines at the head.
   --------------------------------------------------------------------------- */

/* `tdisp(900, 66, '.01em'), fontSize: 16, color: t.fg` —
   rd-pro-scoring-lab.jsx:207 ("fontSize: 16"). The sheet's own title register,
   the same one `.rd-prome-sqe__sheet-title` takes at 15. */
.rd-runsh__title {
  flex: none;
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 900;
  font-stretch: 66%;
  letter-spacing: .01em;
  font-size: 16px;
  line-height: 1;
  color: var(--rd-fg);
}

/* `tbody, fontSize: 10.5, color: t.fg3, marginTop: 3` —
   rd-pro-scoring-lab.jsx:208 ("marginTop: 3").

   ⚠️ ITS CONTENT IS DERIVED FROM THE TALLY AND IS NEVER A CONSTANT. The drawn
   string is *"4 alphas · 2 misses · 1 no-shoot · tap to adjust"* while the same
   component's E2 counts are `A 22 · M 2 · NS 1`
   (rd-pro-scoring-lab.jsx:174 ("const counts")) — the two disagree, which is
   designer question 172, and a transcribed line would have picked a side by
   accident. `run_shooter.rs::tally_line` builds it from the dials. */
.rd-runsh__sub {
  flex: none;
  margin-top: 3px;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.25;
  color: var(--rd-fg3);
}

/* ---------------------------------------------------------------------------
   4. The value and the pad — both the kit's, re-spaced by their caller.
   --------------------------------------------------------------------------- */

/* `<div style={{ marginTop: 14 }}>` around the value box —
   rd-pro-scoring-lab.jsx:209 ("marginTop: 14").

   ⚠️ THE WRAPPER IS LOAD-BEARING AND NOT A HOOK FOR A MARGIN. `.rd-numf` is
   `flex: 1` (kit's own rule), and this sheet is a flex COLUMN — dropped in
   directly the field would grow into every pixel the pad wants. A plain block
   between them makes that `flex: 1` inert, which is what the drawing's own
   wrapper does. */
.rd-runsh__field {
  flex: none;
  margin-top: 14px;
}

/* ⚠️ THE ONE PLACE THIS PART RE-SIZES A KIT CONTROL, AND IT IS FLAGGED RATHER
   THAN ABSORBED. `NumericSize::Large` is the kit's time-field size and it draws
   the value at 26px (`.rd-numf--lg .rd-numf__value`,
   `_legacy-kit-components-and-screens.css`); E3 draws it at 36
   (rd-pro-scoring-lab.jsx:211 ("fontSize: 36")). Both numbers are the
   designer's, in two documents, and 36 is the specific one — this is the glove
   direction's whole argument, the same one that took the dial from 44 to 84.

   Built at the drawn 36 and ROUTED as designer gap 210: is this a third named
   `NumericSize` for the kit, or does `Large` move to 36 everywhere? Until that
   is answered a single call site overriding a kit size is the smaller lie than
   a kit size changed under seven other call sites.

   The caret rides the value: `.rd-numf--lg .rd-numf__caret` is 26 for the same
   reason, and a 26px bar beside a 36px figure is the mismatch a reader sees. */
.rd-runsh__field .rd-numf__value {
  font-size: 36px;
}

.rd-runsh__field .rd-numf__caret {
  height: 36px;
}

/* `flex: 1, minHeight: 0, marginTop: 14` around the key grid —
   rd-pro-scoring-lab.jsx:214 ("flex: 1, minHeight: 0").

   The pad is the slack-taker: the head and the foot are `flex: none`, so a
   taller device gives its extra pixels here and the foot stays on the foot.
   `min-height: 0` is a flex child's escape from its `auto` minimum and is the
   declaration that keeps the pad from pushing the acts off the bottom edge on a
   short one. It is NOT an `overflow` and this part declares none. */
.rd-runsh__pad {
  flex: 1;
  min-height: 0;
  margin-top: 14px;
}

/* ⚠️ THE PAD IS `RdKeypad` AND THIS RULE IS THE WHOLE DIFFERENCE BETWEEN THE
   TWO CALL SITES. 47's RO entry draws the grid at `gap: 6`
   (rd-ro-day.jsx:289 ("gridTemplateColumns: '1fr 1fr 1fr'")) and E3 draws the
   same grid at `gap: 8` (rd-pro-scoring-lab.jsx:214 ("gap: 8")). Everything
   else — three columns, the blank slot, `height: 56`, `--rd-surf2`, the 9px
   chamfer, `mono` at 18 — is identical, which is why this is a caller-side gap
   and not a second pad.

   `59 (Mobile) Pro Scoring.html:77` says so in words: *"the in-app keypad from
   47's RO entry below it (no cursor, no decimal key)"*. Pinned by
   `the_keypad_is_the_kits_and_not_a_second_pad`, which fails if this part ever
   grows a column count, a key height or a key fill of its own. */
.rd-runsh__pad .rd-kpad {
  gap: 8px;
}

/* ---------------------------------------------------------------------------
   5. The foot — two acts, `display: flex, gap: 8, marginTop: 14`
      (rd-pro-scoring-lab.jsx:219 ("gap: 8, marginTop: 14")).
   --------------------------------------------------------------------------- */

/* ⚠️ THE S11 SLOT SITS ABOVE THIS ROW, NOT INSIDE IT. Q187 (the declared
   Pass/Fail verdict on qualifier drills) is held for James — the recommendation
   is a verdict pair ABOVE the save row so the foot keeps exactly two acts. When
   it lands it gets its own section between §4 and §5, and this rule does not
   move. */
.rd-runsh__foot {
  flex: none;
  display: flex;
  gap: 8px;
  margin-top: 14px;
}

/* `flex: 'none', width: 90, height: 56, border: 1.5px solid t.ctrl, color: t.fg,
    tdisp(800, 78, '.06em'), fontSize: 11.5` —
   rd-pro-scoring-lab.jsx:220 ("width: 90, height: 56").

   ⚠️ IT IS THE SHEET'S DISMISS AND NOT THE SCREEN'S BACK ROW. The stack's own
   `‹` (`.rd-zdial-stack__back`, kit-zone-dial.css) leaves the SCREEN; this one
   closes the sheet and puts the reader back on the dials with the tally intact.
   Two controls, two destinations, and the drawing draws both — the sheet is a
   layer over the tally, not a page after it.

   No chamfer, unlike the save beside it: rd-pro-scoring-lab.jsx:220 ("width: 90")
   carries a stroke and no `tcham`, which is the drawing's own way of saying
   which of the two acts is the act. */
.rd-runsh__back {
  flex: none;
  width: 90px;
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: transparent;
  border: 1.5px solid var(--rd-ctrl);
  color: var(--rd-fg);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .06em;
  font-size: 11.5px;
  line-height: 1;
  cursor: pointer;
}

.rd-runsh__back:focus-visible {
  outline: 2px solid var(--rd-acc);
  outline-offset: 2px;
}

/* `flex: 1, height: 56, background: t.volt, color: t.onVolt,
    tdisp(800, 78, '.08em'), fontSize: 12, tcham(9)` —
   rd-pro-scoring-lab.jsx:221 ("Save · Back To Squad").

   ⚠️ 56, AND THE ACT BEHIND IT IS 68. `.rd-zdial-stack__act` (`Add Time`) is the
   TALLY screen's single primary act and is drawn at 68; both acts in this sheet
   are drawn at 56, the phone floor `59 (Mobile) Pro Scoring.html:27` states.
   Two different numbers on two different surfaces, both transcribed, neither
   reconciled by a lane. The 68 is already routed as `18-zone-dial.md` §3.2.

   ⚠️ ON THIS SLICE IT SAVES NOTHING — `// S11 writes the score`. What it DOES
   carry is an honest enabled state: `:disabled` while the buffer is empty, live
   the moment a digit lands. A save act that looked pressable and did nothing
   would be the interim that reads as a save.

   `clip-path` is `tcham(9)` transcribed exactly —
   rd-train-kit.jsx:23 ("const tcham") — the bottom-right corner cut, not a
   radius.

   ⚠️ NO LIGHT HAIRLINE ON THIS ACT — HELD, QUESTION 220 (slice S12).
   Identical to `.rd-zdial-stack__act` (kit-zone-dial.css) and held for the same
   reason: `48 Kit Rules.html:38` asks for an `acc` hairline on every volt fill
   in light, and `48 Kit Rules.html:33` forbids reaching it with a border — or
   an inset ring, which is the same rectangle — under a `clip-path`, because
   the diagonal comes out bare. The fix is `RdChamferBox` with `Fill::Volt` +
   `Stroke::PrimaryEdge`, which is markup and a slice. The unchamfered volt fill
   in this arc, `.rd-zdial__btn--plus`, DOES carry `--rd-btn-edge`; that split is
   the answer, not a drift. */
.rd-runsh__save {
  flex: 1;
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  background: var(--rd-volt);
  color: var(--rd-on-volt);
  font-family: 'Archivo', system-ui, sans-serif;
  text-transform: uppercase;
  font-weight: 800;
  font-stretch: 78%;
  letter-spacing: .08em;
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 9px), calc(100% - 9px) 100%, 0 100%);
}

/* No drawn disabled state on this act — the drawing has no untimed frame — so
   this is the console's own: `--rd-surf2` under `--rd-fg3`, which is the pair
   `RdButton`'s disabled branch and the kit's read-only boxes both take. The
   volt comes OFF rather than being dimmed: a faded volt still reads as the
   primary act, and this one is not pressable yet. */
.rd-runsh__save:disabled {
  background: var(--rd-surf2);
  color: var(--rd-fg3);
  cursor: default;
}

/* `clip-path` clips an `outline` away, so the ring is drawn as an inset shadow
   instead — the same construction, and the same reason, `.rd-zdial-stack__act`
   and adm-lib-mob.css's chamfered chip both record. */
.rd-runsh__save:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--rd-acc);
}
/* ==== KIT-HIT · the shared act rule — touch-action on every tappable primitive · owner: lane-track8 ==== */

/* ---------------------------------------------------------------------------
   ONE cross-cutting declaration, said once.

   2026-08-29, James, NTOA rehearsal on an installed PWA on a tablet: several
   buttons "only register a tap on the TEXT, not the whole drawn box". The audit
   behind this part found two separate causes, and this file is the fix for the
   first of them.

   CAUSE 1 — THE BROWSER, NOT THE BOX. Without `touch-action: manipulation` a
   touch on any element is a candidate for double-tap-to-zoom, so the browser
   holds the tap for ~300ms deciding whether a second one is coming. On a
   painted control that is not merely slow: a finger that moves a pixel inside
   the hold window is reinterpreted as a pan or a zoom gesture and the click is
   never dispatched at all. A tap dead on the label is short and still; a tap
   near the edge of a wide box, made in gloves, is neither — which is exactly
   the "only the text works" report, from a hit box that was already correct.
   `manipulation` keeps panning and pinch-zoom and drops double-tap-zoom, so the
   click fires on touchend.

   CAUSE 2 — THE BOX GENUINELY WAS THE TEXT. Six primitives carried their
   `onclick` on an element barely taller than its own line box. Those are fixed
   in each primitive's OWN part, not here: geometry belongs to the component
   that draws it, and a part that reached across six owners to re-size their
   controls would be unreviewable. Each carries a dated note pointing back at
   this one. The two shapes used there:

     - a horizontal act (a chip in a row, a text link beside its explanation)
       grows its target with a transparent `::after` that is 44px tall and the
       element's own width. It is the element's own pseudo, so the hit lands on
       the element; it paints nothing and it moves nothing; and because the
       extension is VERTICAL ONLY it cannot overlap a sibling that sits beside
       it. This is the `.rd-chip-hit` trade-off read the other way round: that
       note refused a target that would overlap its neighbour, and a row's
       neighbours are horizontal.
     - a vertical act (a stacked act line, a full-width foot act) takes a real
       `min-height: 44px` instead. A pseudo-extension there WOULD overlap the
       sibling above or below and hand it the tap, which is the worse defect.

   WHY A PART AND NOT A LINE IN EACH FILE. `touch-action` is not a drawing
   decision and has no per-component value — every act in the app wants the same
   one. Spelled 34 times it is 34 places to forget it in the 35th; spelled once
   it is a list somebody can read and a test can hold.

   SPECIFICITY: this part is appended LAST, and nothing else in the sheet
   declares `touch-action` on any of these classes. The one other
   `touch-action` in the pool is `.rd-se-tab__stage`'s `none` (`s05-editor.css`)
   — the stage-editor canvas, which owns its own gestures and is DELIBERATELY
   not in this list. Adding it here would break dragging a target on the stage,
   and `stage_editor.rs` has a test asserting that `none`.

   ⚠️ ADDING A NEW ACT? Add it here. `components/kit/hit_area.rs` sweeps the
   sheet for act-shaped class names and fails when one of them is missing from
   this rule, so the list cannot quietly fall behind the kit.
   -------------------------------------------------------------------------- */
.rd-btn,
.rd-chip-hit,
.rd-tchip-hit,
.rd-tab,
.rd-stabs__tab,
.rd-seg__opt,
.rd-swatch,
.rd-door__link,
.rd-row,
.rd-row__line--act,
.rd-thub__row,
.rd-trail__row,
.rd-tland__card,
.rd-zdial__btn,
.rd-zdial-stack__back,
.rd-zdial-stack__act,
.rd-repstep__btn,
.rd-kpad__key,
.rd-confirm__go,
.rd-confirm__keep,
.rd-confirm__discard,
.rd-pfilter__chip,
.rd-protabs__tab,
.rd-pro-orgmenu__row,
.rd-pro-rail__head,
.rd-prom__menu,
.rd-prom__org--btn,
.rd-prom__drawer-head--btn,
.rd-prom__item,
.rd-prom__row,
.rd-prom__scrim,
.rd-ev-life__next,
.rd-ev-life__back,
.rd-sns__start-act,
.rd-sns__thumb {
  touch-action: manipulation;
}
