/* ══════════════════════════════════════════════════════════════════════════
   THE WHITEPAPER SHELF — the design Tom approved in
   Application Docs/help-prototype-v2.html, ported onto the live pages.

   ONE FILE, TWO MOUNTS. /docs and /docs/whitepapers both show paper cards and
   both open the same quick-look overlay. CLAUDE.md, "THE HARDEST RULE IN THIS
   PROJECT", clause 5: anything that appears on more than one page is a
   component, not a copy. Inlining these rules into each generated page would
   have been two copies on day one and N copies the first time a third surface
   wants a card.

   SCOPED ON PURPOSE. Every rule below is under `.qsh` (the shelf region) or
   `#qlRoot` / `#qlScrim` (the overlay, which is body-level because a modal
   cannot live inside the content it covers). Class names like `.pc` and
   `.split` are short and generic, and four other pages already define a `.pc`
   or a `.feat` of their own in their OWN inline stylesheets. The scope is what
   makes that a non-event.

   TOKENS. The live library stylesheet is the donor: --cream, --surface, --ink,
   --ink-mid, --cream-border, --amber, --amber-text, --serif, --mono, --sans.
   This file adds only what the design needs and the donor has no word for:
   the two non-amber accents, a soft amber fill, and one mid ink.

   --sh-ink-3 IS NOT --ink-light. The donor's --ink-light (#9A9088) is 2.90:1
   on white, below AA for body text. The prototype's own meta ink is #6F6960,
   which is 5.43:1 on white and 4.73:1 on the tail card's --sh-surf-2. Every
   small-print pair in this file uses the prototype's value; nothing here
   introduces a sub-AA pair. Fifteen pairs computed, worst 4.73:1, best
   17.73:1 — the prototype measured 4.63:1–17.90:1 across 24, so this is the
   same band.

   THE DARK THEME ARRIVED 2026-08-29 and lives at the FOOT of this file. This
   note used to read "NO DARK THEME ... these library pages do not have one to
   extend", and that was true when it was written: the donor palette was
   hand-copied into 33 page files with no dark half anywhere on the site. There
   is one to extend now -- analytics.js declares the dark tokens once, before
   first paint, so every --cream / --ink / --amber referenced below resolves
   through it with no change here.
   WHAT DOES NOT RESOLVE AUTOMATICALLY is the block immediately under this one,
   plus the three accent inks: those are LITERALS chosen against white, and a
   literal cannot follow a token. Overriding them is the dark theme's entire job
   in this file, and it is done at the foot rather than restated inline so the
   light design stays readable as one continuous document.
   ══════════════════════════════════════════════════════════════════════════ */

.qsh, #qlRoot, #qlScrim {
  --sh-line: var(--cream-border);          /* #D8D0C0 */
  --sh-line-2: #C5BBA7;                    /* one step down, borders only */
  --sh-surf-2: #F1EFEA;
  --sh-ink-3: #6F6960;                     /* 5.43:1 on white — see above */
  --sh-amber-soft: #FBF3E2;
  --sh-cover: #17150F;                     /* the covers' own field colour */
  --sh-r-sm: 8px;
  --sh-r: 11px;
  --sh-r-lg: 16px;
  --sh-sh-1: 0 1px 2px rgba(26,24,20,.05);
  --sh-sh-2: 0 1px 2px rgba(26,24,20,.05), 0 6px 16px -6px rgba(26,24,20,.10);
  --sh-sh-3: 0 2px 4px rgba(26,24,20,.06), 0 16px 40px -12px rgba(26,24,20,.20);
  --sh-sh-4: 0 24px 70px -18px rgba(26,24,20,.38), 0 4px 12px rgba(26,24,20,.10);
  --sh-wash: rgba(26,24,20,.46);
}

/* ── collection accents ────────────────────────────────────────────────────
   --acc is DECORATIVE (a wash, a 3px bar, a 6px dot) and carries no text.
   --acc-ink is the text-safe partner and is the only one allowed near a
   letterform. Keeping them separate is what lets the wash stay saturated
   without dragging an eyebrow below AA. Amber is the donor's own --amber so
   the shelf does not introduce an eighth gold. */
/* THE DEFAULT PAIR, ADDED 2026-08-28 WITH THE ROW CARD. Every .pc and .feat
   carries one of the three classes below on its own element, so this only ever
   applies where nothing else did -- and there it is the difference between a
   3px accent rail and an INVISIBLE one: `background:var(--acc)` with --acc
   undefined is an invalid declaration, which paints nothing and looks exactly
   like a card that was built without a rail. Amber is the donor's own gold, so
   the fallback introduces no colour. */
.qsh, #qlRoot { --acc:var(--amber); --acc-ink:var(--amber-text); }
.a-amber { --acc:#B8860B; --acc-ink:var(--amber-text); }   /* 5.48:1 on white */
.a-slate { --acc:#6B7A8D; --acc-ink:#4F5C6D; }             /* 6.81:1 on white */
.a-coral { --acc:#8B5E3C; --acc-ink:#7A4F30; }             /* 7.03:1 on white */

/* ── the shelf region ──────────────────────────────────────────────────────
   THE PAGE'S .wrap IS 840px AND MUST STAY 840px. tests/help-section-geometry
   G2 pins that rule across all nine Help-section pages, after a ux pass found
   a single page declaring 800px and shifting its content column 20px. The h1,
   the lede, the crumbs and the sub-nav all still sit in that column, which is
   the invariant the test exists to protect.

   The SHELF is the one block that cannot live in 760px: three cards plus a
   210px rail needs ~1050px, and squeezed into 760px the third card's action
   control drops below the container-query floor and loses its label — the
   approved control, degraded, on every card. So the shelf centres itself on
   the viewport instead, capped at the prototype's own 1120px.

   `50%` is the middle of the 760px content box, which is also the middle of
   the viewport, so subtracting half the shelf width centres it. Capped at
   `100vw - 40px` rather than `100vw` so a classic scrollbar cannot push the
   block wider than the visible area.

   THE SCOPE AND THE BLEED-OUT ARE TWO CLASSES ON PURPOSE. /docs mounts `.qsh`
   for the mini-card shelf and must stay inside the 760px column with the four
   sections around it; only /docs/whitepapers adds `.qsh-wide`. One class doing
   both would have widened a three-card preview out of alignment with every
   other section on that page. */
.qsh { font-family:var(--sans); }
.qsh-wide {
  width: min(1120px, calc(100vw - 40px));
  margin-left: calc(50% - min(560px, 50vw - 20px));
}
@media (max-width:880px) {
  /* Below the .wrap cap the content box is already the full width minus its
     40px gutters, so the bleed-out has nothing to gain and would only risk an
     off-by-one against the gutter. Stand down. */
  .qsh-wide { width:auto; margin-left:0; }
}
.qsh .sr-only {
  position:absolute; width:1px; height:1px; margin:-1px; padding:0;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0;
}

.qsh .lib { display:grid; grid-template-columns:1fr; gap:28px; align-items:start; }
@media (min-width:900px) { .qsh .lib { grid-template-columns:210px 1fr; gap:36px; } }
.qsh .lib-main { min-width:0; }

/* ── the rail: collections × topics ───────────────────────────────────────*/
.qsh .rail { min-width:0; }
/* The prototype stuck the rail under a sticky header + sub-nav at 118px.
   Neither is sticky on these pages (nav.js paints a static header), so the
   offset is a plain gutter instead of a copied number. */
@media (min-width:900px) { .qsh .rail { position:sticky; top:24px; } }
.qsh .rail-h {
  font-family:var(--mono); font-size:10px; letter-spacing:.17em;
  text-transform:uppercase; color:var(--sh-ink-3); margin:0 0 8px; padding:0 2px;
}
.qsh .facets + .rail-h { margin-top:24px; }
.qsh .facets { display:flex; flex-direction:column; gap:1px; margin:0 0 4px; }
.qsh .fac {
  display:flex; align-items:center; gap:9px; width:100%; text-align:left;
  background:none; border:0; border-radius:var(--sh-r-sm); padding:7px 9px;
  cursor:pointer; font:inherit; font-size:13.5px; color:var(--ink-mid);
  transition:background .14s, color .14s;
}
.qsh .fac:hover { background:var(--sh-surf-2); color:var(--ink); }
.qsh .fac .dot { width:8px; height:8px; border-radius:2px; background:var(--acc,var(--sh-line-2)); flex:none; }
.qsh .fac .dot.rnd { border-radius:50%; }
.qsh .fac .lb { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.qsh .fac .n { font-family:var(--mono); font-size:11px; color:var(--sh-ink-3); flex:none; }
.qsh .fac[aria-pressed="true"] {
  background:var(--sh-amber-soft); color:var(--amber-text); font-weight:600;
}
.qsh .fac[aria-pressed="true"] .n { color:var(--amber-text); }
/* A topic has no colour of its own — collections do. Pressed, its dot takes
   the button's own ink so "on" reads the same way in both groups without
   inventing a seventh accent. */
.qsh .fac[aria-pressed="true"] .dot.rnd { background:currentColor; }
.qsh .rail-note {
  font-size:11.5px; line-height:1.55; color:var(--sh-ink-3); margin:22px 0 0;
  padding-top:14px; border-top:1px solid var(--sh-line);
}
/* Below 900px the rail is not a rail. A stacked list of ten facets would push
   the first card a screen and a half down, so it becomes a horizontal chip
   scroller — same controls, same state, one row. */
@media (max-width:899px) {
  .qsh .facets {
    flex-direction:row; gap:6px; overflow-x:auto; padding-bottom:4px;
    scrollbar-width:none; -ms-overflow-style:none;
  }
  .qsh .facets::-webkit-scrollbar { display:none; }
  .qsh .fac {
    width:auto; flex:none; border:1px solid var(--sh-line);
    background:var(--surface); border-radius:99px; padding:6px 12px;
  }
  .qsh .fac[aria-pressed="true"] { border-color:var(--sh-line-2); }
  .qsh .rail-note { display:none; }
}

/* ── toolbar: search + sort ───────────────────────────────────────────────*/
.qsh .toolbar { display:flex; flex-wrap:wrap; align-items:center; gap:10px; margin:0 0 6px; }
.qsh .search { position:relative; flex:1 1 220px; min-width:0; }
.qsh .search > svg {
  position:absolute; left:11px; top:50%; transform:translateY(-50%);
  width:15px; height:15px; color:var(--sh-ink-3); pointer-events:none;
}
.qsh .search input {
  width:100%; height:38px; padding:0 40px 0 34px; border-radius:var(--sh-r-sm);
  border:1px solid var(--sh-line); background:var(--surface); color:var(--ink);
  font-family:var(--sans); font-size:14px; transition:border-color .15s, box-shadow .15s;
}
.qsh .search input::placeholder { color:var(--sh-ink-3); }
.qsh .search input:focus {
  outline:none; border-color:var(--amber);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--amber) 18%, transparent);
}
.qsh .search kbd {
  position:absolute; right:9px; top:50%; transform:translateY(-50%);
  font-family:var(--mono); font-size:10.5px; color:var(--sh-ink-3);
  border:1px solid var(--sh-line); border-radius:4px; padding:1px 5px;
  background:var(--sh-surf-2); pointer-events:none;
}
.qsh .search input:focus ~ kbd,
.qsh .search input:not(:placeholder-shown) ~ kbd { display:none; }
.qsh .sortwrap { display:flex; align-items:center; gap:7px; }
.qsh .sortwrap label {
  font-family:var(--mono); font-size:10px; letter-spacing:.14em;
  text-transform:uppercase; color:var(--sh-ink-3);
}
.qsh .sortwrap select {
  height:38px; border-radius:var(--sh-r-sm); border:1px solid var(--sh-line);
  background:var(--surface); color:var(--ink-mid); font-family:var(--sans);
  font-size:13.5px; padding:0 8px; cursor:pointer;
}

/* ── the result bar: count + removable filter chips ───────────────────────*/
.qsh .resbar { display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin:0 0 20px; min-height:24px; }
.qsh .resbar .n {
  font-family:var(--mono); font-size:11px; letter-spacing:.1em;
  text-transform:uppercase; color:var(--sh-ink-3); margin:0;
}
.qsh .chip {
  display:inline-flex; align-items:center; gap:6px; font-size:12px; font-weight:600;
  padding:3px 6px 3px 10px; border-radius:99px; background:var(--sh-amber-soft);
  color:var(--amber-text); border:1px solid var(--sh-line-2);
}
.qsh .chip button {
  background:none; border:0; cursor:pointer; color:inherit; line-height:1;
  padding:2px; border-radius:99px; opacity:.75;
}
.qsh .chip button:hover { opacity:1; background:color-mix(in srgb, var(--amber) 20%, transparent); }
.qsh .chip svg { width:11px; height:11px; display:block; }
.qsh .clear-all {
  background:none; border:0; padding:3px 4px; cursor:pointer; font:inherit;
  font-size:12.5px; color:var(--sh-ink-3); text-decoration:underline; text-underline-offset:3px;
}
.qsh .clear-all:hover { color:var(--ink); }

/* ── the featured pull-out ────────────────────────────────────────────────*/
.qsh .feat {
  display:grid; grid-template-columns:1fr; gap:0; background:var(--surface);
  border:1px solid var(--sh-line); border-left:3px solid var(--acc);
  border-radius:var(--sh-r-lg); overflow:hidden; box-shadow:var(--sh-sh-2); margin:0 0 26px;
}
@media (min-width:640px) { .qsh .feat { grid-template-columns:198px 1fr; } }
/* THE SAME PULL-OUT, FOR A SHELF WHOSE ITEMS HAVE NO COVER. 2026-08-29.
   Concepts, Guides and Reference hold web pages, and the row card below already
   records why they get no cover: inventing one -- a screenshot, a gradient, a
   glyph in a box -- is decoration standing in for information. The hero has the
   same problem one size up, so it drops the 198px cover column rather than
   filling it. A variant, not a fork: every other declaration on .feat (the
   surface, the border, the accent rail, the radius, the shadow, the margin)
   still applies, and CLAUDE.md's hardest rule, clause 3 -- "different chrome is
   a variant INSIDE the component". Same specificity as the rule above, so this
   wins on source order; it is written immediately after it for that reason. */
@media (min-width:640px) { .qsh .feat-plain { grid-template-columns:1fr; } }
.qsh .feat-cv {
  position:relative; background:var(--sh-cover); border:0; padding:0; cursor:pointer;
  display:block; width:100%; min-height:150px;
}
.qsh .feat-cv img { display:block; width:100%; height:100%; object-fit:cover; object-position:center top; }
.qsh .feat-bd { padding:22px 24px 20px; min-width:0; }
.qsh .feat-bd h3 {
  margin:0 0 8px; font-family:var(--serif); font-weight:600; font-size:26px;
  line-height:1.12; letter-spacing:-.015em; color:var(--ink);
}
.qsh .feat-bd .bl { font-size:14px; color:var(--ink-mid); margin:0 0 14px; max-width:56ch; }
.qsh .feat-meta {
  font-family:var(--mono); font-size:10.5px; letter-spacing:.1em;
  text-transform:uppercase; color:var(--sh-ink-3); margin:0 0 16px;
}

/* ── the card grid ────────────────────────────────────────────────────────
   Viewport breakpoints, mirrored by cols() in quorum-shelf.js. They are
   viewport-based rather than container-based because the shelf's own width is
   now a function of the viewport (see .qsh above), so the two agree — and
   because a container query cannot be read back by the tail's span maths
   without a browser. */
.qsh .grid { display:grid; grid-template-columns:1fr; gap:18px; list-style:none; margin:0; padding:0; }
@media (min-width:700px)  { .qsh .grid { grid-template-columns:repeat(2,1fr); } }
@media (min-width:1140px) { .qsh .grid { grid-template-columns:repeat(3,1fr); } }

.qsh .pc {
  display:flex; flex-direction:column; min-width:0; background:var(--surface);
  border:1px solid var(--sh-line); border-radius:var(--sh-r-lg); overflow:hidden;
  box-shadow:var(--sh-sh-1); container-type:inline-size;
  transition:transform .18s ease, box-shadow .18s ease, border-color .18s;
}
.qsh .pc:hover { transform:translateY(-3px); box-shadow:var(--sh-sh-3); border-color:var(--sh-line-2); }
.qsh .pc[hidden] { display:none; }
/* The featured paper keeps a card in the grid so a filter can show it. While
   it is BEING featured that card would be the same paper twice on one screen,
   so it stands down — and comes straight back the moment a facet is on. */
.qsh[data-filtered="0"] .pc[data-featured] { display:none; }
.qsh[data-filtered="1"] .feat { display:none; }

/* THE COVER IS A 3:2 CROP. That is the whole reason quick look has something
   to show: the card gives you the head of an 800×1035 sheet, the overlay gives
   you the sheet. */
.qsh .pc-thumb {
  position:relative; display:block; width:100%; aspect-ratio:3/2; padding:0; border:0;
  border-bottom:1px solid var(--sh-line); background:var(--sh-cover); cursor:pointer; overflow:hidden;
}
.qsh .pc-thumb img { display:block; width:100%; height:100%; object-fit:cover; object-position:center top; }
/* Seven near-identical dark covers. The wash is what tells them apart at a
   glance — screen-blend so it lifts the artwork rather than veiling it. */
.qsh .pc-wash {
  position:absolute; inset:0; pointer-events:none; mix-blend-mode:screen; opacity:.34;
  background:linear-gradient(112deg, transparent 38%, var(--acc) 128%);
}
.qsh .pc-bar { position:absolute; left:0; right:0; bottom:0; height:3px; background:var(--acc); }
/* The paper's own cover line, lifted off the artwork. Warm off-white, not
   gold: the artwork already has a gold and amber now means "you can act on
   this". 15.94:1 on its ground.

   THE COVER LINE CARRIES ITS OWN GROUND, 2026-08-29. This was two elements: a
   `.pc-scrim` of a FIXED 64px, and the line floating 13px off the bottom. The
   scrim's height was typed in px while everything behind it scales with the
   card, and the crop is 3:2 from the top of an 800x1035 sheet, so the paper's
   printed title is inside the band on all seven cards. At the 234.9px card the
   three-column grid actually ships, the printed title ended 23.9px off the
   floor and the cover line's box started at 28.0px: every card crossed its own
   title, at roughly half scrim strength. VS Code is the only prose cover line,
   so it is the only one that WRAPS there -- two lines, box top 44.2px, clean
   through "& VS Code" -- and both layers went unreadable.

   One rule for seven cards: the cover line IS the band. Its ground is its own
   box, so the ground can never be shorter than the text it backs, at any card
   width and any number of lines. Opaque to the top of the text, then it fades
   out across the 30px above it -- which is where the artwork comes back. */
.qsh .pc-sig {
  position:absolute; left:0; right:0; bottom:0; text-align:left; color:#EFE9DC;
  padding:30px 14px 13px; font-size:11.5px; line-height:1.3; letter-spacing:.01em;
  pointer-events:none;
  background:linear-gradient(to top, rgba(15,13,9,.93) 0%,
    rgba(15,13,9,.93) calc(100% - 30px), rgba(15,13,9,0) 100%);
}
.qsh .pc-sig.mono { font-family:var(--mono); }
.qsh .pc-sig.sans { font-family:var(--sans); font-weight:600; font-size:12px; }
.qsh .pc-peek {
  position:absolute; top:10px; right:10px; display:inline-flex; align-items:center; gap:5px;
  font-family:var(--mono); font-size:10px; letter-spacing:.1em; text-transform:uppercase;
  color:#F3EEE3; background:rgba(10,9,6,.72); border:1px solid rgba(243,238,227,.22);
  border-radius:99px; padding:4px 9px; opacity:0; transform:translateY(-3px);
  transition:opacity .18s ease, transform .18s ease;
}
.qsh .pc-peek svg { width:11px; height:11px; }
.qsh .pc:hover .pc-peek, .qsh .pc-thumb:focus-visible .pc-peek { opacity:1; transform:translateY(0); }
/* Both cards are overflow:hidden with the cover flush to the edge, so the
   global outline-offset:2px focus ring would be clipped away. Draw it inside. */
.qsh .pc-thumb:focus-visible, .qsh .feat-cv:focus-visible { outline-offset:-3px; border-radius:0; }

.qsh .pc-bd { padding:16px 17px 0; flex:1; min-width:0; }
.qsh .pc-kick {
  display:flex; align-items:center; gap:6px; font-family:var(--mono); font-size:9.5px;
  letter-spacing:.15em; text-transform:uppercase; color:var(--acc-ink); margin:0 0 8px;
}
.qsh .pc-kick i { width:6px; height:6px; border-radius:50%; background:var(--acc); flex:none; }
.qsh .pc h3 { margin:0 0 8px; font-family:var(--sans); font-size:16px; font-weight:700; line-height:1.25; letter-spacing:-.015em; color:var(--ink); }
.qsh .pc .bl { font-size:13px; line-height:1.55; color:var(--ink-mid); margin:0 0 14px; max-width:none; }
/* Inline code literals in running copy are CODE: mono, upright, full-strength
   ink — never amber. Amber means "actionable" and a literal is not clickable.
   `em` and `code` both land here because the manifest uses both. */
.qsh .pc .bl em, .qsh .pc .bl code, .qsh .feat-bd .bl em, .qsh .feat-bd .bl code,
#qlRoot .ql-info .bl em, #qlRoot .ql-info .bl code {
  font-family:var(--mono); font-style:normal; font-size:.94em; color:var(--ink); background:none;
}
.qsh .pc-ft { padding:0 17px 17px; border-top:1px solid var(--sh-line); margin-top:auto; padding-top:13px; }
.qsh .pc-meta {
  font-family:var(--mono); font-size:10px; letter-spacing:.11em;
  text-transform:uppercase; color:var(--sh-ink-3); margin:0 0 11px;
}
.qsh .pc-pair { font-size:12px; color:var(--sh-ink-3); margin:0 0 11px; max-width:none; }
.qsh .pc-pair a { color:var(--amber-text); }

/* ══ THE ACTION CONTROL — ONE segmented control ═══════════════════════════
   Rebuilt in the prototype specifically because two stacked buttons were
   rejected: one pill, one border, a filled primary and a quiet secondary
   divided by a hairline. It must never wrap, so `white-space:nowrap` is on
   both halves and not on the wrapper alone. */
.qsh .split {
  display:inline-flex; align-items:stretch; max-width:100%; border-radius:10px;
  border:1px solid var(--amber-text); background:var(--surface); box-shadow:var(--sh-sh-1);
}
.qsh .sp-a, .qsh .sp-b {
  display:inline-flex; align-items:center; justify-content:center; gap:6px;
  font-family:var(--sans); font-size:12.5px; font-weight:600; line-height:1;
  letter-spacing:.002em; white-space:nowrap; cursor:pointer; padding:0 11px;
  height:34px; border:0; text-decoration:none; position:relative;
}
.qsh .sp-a { background:var(--amber-text); color:#FFFFFF; border-radius:9px 0 0 9px; }  /* 5.48:1 */
.qsh .sp-a:hover { filter:brightness(1.12); color:#FFFFFF; }
.qsh .sp-b {
  background:transparent; color:var(--ink-mid); border-radius:0 9px 9px 0;
  border-left:1px solid var(--amber-text); transition:background .14s, color .14s;
}
.qsh .sp-b:hover { background:var(--sh-amber-soft); color:var(--amber-text); }   /* 4.97:1 */
.qsh .sp-a:focus-visible, .qsh .sp-b:focus-visible { z-index:2; outline-offset:1px; }
.qsh .split svg { width:13px; height:13px; flex:none; }
.qsh .split-lg { border-radius:11px; }
.qsh .split-lg .sp-a, .qsh .split-lg .sp-b { height:40px; font-size:13.5px; padding:0 15px; gap:7px; }
.qsh .split-lg .sp-a { border-radius:10px 0 0 10px; }
.qsh .split-lg .sp-b { border-radius:0 10px 10px 0; }
/* Safety net only. At the declared breakpoints the narrowest card is ~259px,
   so this should never fire — but if it ever does the secondary degrades to
   its icon and KEEPS its accessible name rather than overflowing the card. */
@container (max-width:246px) {
  .qsh .pc .sp-b span {
    position:absolute; width:1px; height:1px; margin:-1px; overflow:hidden;
    clip-path:inset(50%); white-space:nowrap;
  }
  .qsh .pc .sp-b { padding:0 10px; }
}

/* ── the tail card: the grid is always a complete rectangle ───────────────
   Spans whatever is left of the final row, recomputed on render and on
   resize, so there is no orphan cell at any paper count and any width.
   The static default is a full row, which is exactly right for the cold
   default view (six cards, three columns, nothing left over) and therefore
   also right for a reader with JavaScript off. */
/* order:999 IS NOT DECORATION. Sorting is done by writing `order: 0..n` onto
   the cards, and a grid item with no order declared is order 0 -- so the tail
   would tie with the first card and, ties being resolved in document order,
   land BETWEEN card one and card two the instant anything is sorted. Found by
   reading the sort back rather than by looking at the default view, where it
   is invisible because every card is already order 0. */
.qsh .tail-cell { grid-column:1/-1; order:999; }
.qsh .tail {
  display:flex; flex-direction:column; justify-content:center; gap:6px; width:100%;
  align-items:flex-start; background:var(--sh-surf-2); border:1px dashed var(--sh-line-2);
  border-radius:var(--sh-r-lg); padding:22px 24px; text-align:left; cursor:pointer;
  font:inherit; font-family:var(--sans); color:inherit; min-height:110px;
  text-decoration:none; transition:border-color .15s, background .15s;
}
/* The tail is an <a>, and the donor stylesheet turns every link amber on
   hover. Left alone that repaints the whole card's copy amber the moment the
   pointer touches it. Each colour is stated rather than left to `inherit` --
   inherit would have inherited exactly the amber being avoided. */
.qsh .tail:hover { border-color:var(--amber-text); background:var(--sh-amber-soft); color:var(--ink); }
.qsh .tail:hover b { color:var(--ink); }            /* 16.06:1 on amber-soft */
.qsh .tail:hover span { color:var(--ink-mid); }     /*  6.95:1 on amber-soft */
.qsh .tail:hover .ar { color:var(--amber-text); }
.qsh .tail b { font-size:15px; font-weight:600; color:var(--ink); }
.qsh .tail span { font-size:13px; color:var(--ink-mid); max-width:52ch; }   /* 6.68:1 */
.qsh .tail .ar { color:var(--amber-text); }

.qsh .empty {
  border:1px dashed var(--sh-line-2); border-radius:var(--sh-r-lg); padding:48px 28px;
  text-align:center; color:var(--ink-mid);
}
.qsh .empty b { display:block; font-size:16px; color:var(--ink); margin-bottom:6px; }
.qsh .empty p { max-width:none; margin:0; }

/* ── shelf furniture, shared with /docs ───────────────────────────────────*/
.qsh .shelf { margin:0 0 34px; }
.qsh .shelf-hd {
  display:flex; align-items:baseline; justify-content:space-between; gap:16px;
  padding:0 0 12px; border-bottom:1px solid var(--sh-line); margin:0 0 16px;
}
.qsh .shelf-hd h2 { margin:0; }
.qsh .shelf-hd .cnt {
  font-family:var(--mono); font-size:11px; letter-spacing:.1em;
  color:var(--sh-ink-3); text-transform:uppercase; margin:0;
}
.qsh .link-btn, #qlRoot .link-btn {
  background:none; border:0; padding:0; cursor:pointer; font:inherit; font-family:var(--sans);
  font-size:13px; font-weight:600; color:var(--amber-text); white-space:nowrap; text-decoration:none;
}
.qsh .link-btn:hover, #qlRoot .link-btn:hover { text-decoration:underline; text-underline-offset:3px; }

/* ── THE ROW CARD: the same shelf, for a shelf of WEB PAGES ───────────────
   THE MINI CARD STOOD HERE AND IS GONE, 2026-08-28. It existed for exactly one
   mount -- /docs previewing three papers -- and /docs is now a 301 onto /help
   (Tom: "/help IS the library. Kill the duplicate."). Left in place it would
   have been a component nothing mounts, which this repo's own rule calls
   "config nobody exercises". Its markup left build-library-index.py in the same
   commit; the cover strip on the /help door is what shows the artwork now.

   WHY A ROW AND NOT A .pc CARD. A .pc leads with a COVER, and the cover is the
   entire reason it beats a list: seven near-identical dark PDFs are told apart
   by their artwork faster than by any sentence. Concepts, Guides, Reference and
   Evidence hold WEB PAGES. They have no cover, and inventing one -- a
   screenshot, a generated gradient, a glyph in a box -- would be decoration
   standing in for information, which is worse than the list it replaced.

   SO THE COVER'S JOB IS DONE BY THREE REAL THINGS INSTEAD, none of them a
   blank rectangle:
     1. an ACCENT RAIL, the 3px --acc edge the featured card already uses, so a
        card carries its collection's colour the way every card on the shelf
        does;
     2. an EYEBROW that is the page's own ROUTE, in mono with the collection
        dot -- the same .pc-kick device, and the same datum the old .idx list
        already printed under every row. It is not decoration: it is the thing
        you copy when you want to link the page;
     3. the ARROW, which is the affordance. These cards NAVIGATE. There is no
        quick look and no download, because there is no artifact -- a preview
        overlay here would show the reader a smaller copy of the page they were
        one click from opening.

   CONTRAST, every pair, on --surface #FFFFFF:
     b   #1A1814 17.73:1 · .d #5A5248 7.68:1 · .r #6F6960 5.43:1
     .ar, on the text-safe accent — amber 5.48:1 · slate 6.81:1 · coral 7.03:1
     (the token is not spelled with its colon here on purpose:
      tests/shelf-design counts declaration SITES by that string, and a comment
      that looks like a declaration is a fifth accent nobody wrote)
   Worst pair 5.43:1, above the 4.73:1 this file already measures as its floor.

   HOVER RESTATES EVERY COLOUR, and that is not redundancy. The donor stylesheet
   sets `a:hover{color:var(--amber-text)}` globally, and the whole card is one
   <a>, so without these three lines the entire body copy repaints amber the
   moment the pointer lands. The tail card above documents the identical trap. */
/* ── THE ROW CARD CARRIES NO BRAND HUE. 2026-08-29 ─────────────────────────
   Until today tools/build-library-index.py put the SECTION's accent class on
   this list -- a-amber on Concepts, a-slate on Guides, a-coral on Reference and
   Evidence -- so each of the four shelves flew a different one of the three
   brand colours. That is colour owning a category of content, which is the
   convention Tom killed ("it should go"), and doc 31 s7.1 is stricter again: a
   Tier 3 docs page gets the header mark and no other tri-colour at all.
   The generator stopped emitting the class. THAT ALONE WOULD NOT HAVE FIXED IT,
   and this is the part worth reading: the default pair near the top of this
   file sets --acc to the donor's gold, so a list with no accent class does not
   lose its hue, it INHERITS one. Four shelves in four colours would have become
   four shelves in one -- still a brand hue on a Tier 3 page, on every rail and
   every dot. So the neutral is declared here, on the row list itself.
   SCOPED TO .rows AND NOTHING ELSE. The whitepaper cards each carry their
   COLLECTION's accent on their own element and are untouched by this rule; the
   quick-look overlay reads its accent from the paper it opened. Redefining the
   .qsh default instead would have drained the colour out of both of those,
   which is degrading the whitepapers shelf in order to fix the other four.
   CONTRAST, on --surface #FFFFFF: --acc-ink resolves to #6F6960 at 5.43:1, the
   same ink the route eyebrow and the small print on this card already use, and
   above the 4.73:1 floor this file measures for itself. The 3px rail and the
   6px dot are decorative and carry no letterform, so the border ink is legal
   there -- it reads as a rule, which is what an unhued rail should read as.
   AND IT IS THE WHOLE SHELF NOW, NOT ONLY THE LIST. 2026-08-29. Scoping the
   neutral to `.rows` was right while the list was the only thing on the page,
   and it stopped being right the moment those shelves grew a featured pull-out
   and a facet rail. Both sit OUTSIDE the list: `.feat` takes `border-left:3px
   solid var(--acc)` and every `.fac .dot` takes `background:var(--acc)`, so
   both would have resolved through the default pair at the top of this file and
   flown the donor's gold -- a brand hue on a Tier 3 docs page, on the most
   prominent block of it, while the list underneath stayed grey. `.qsh.qsh-rows`
   is (0,2,0) and beats that default on specificity rather than on source order.
   `.qsh .rows` STAYS IN THE SAME SELECTOR, because the "Not written yet" list
   at the foot of each shelf is a bare `.qsh` with no `.qsh-rows` root.
   ONE DECLARATION, NOT TWO: the count of places a hue can enter this file stays
   at EIGHT (the number tests/shelf-design G5 asserts), and a second neutral
   rule would have been nine sites for one statement. */
.qsh .rows, .qsh.qsh-rows { --acc:var(--sh-line-2); --acc-ink:var(--sh-ink-3); }
/* `max-width` HERE IS AN OVERRIDE, NOT A DEFAULT. 2026-08-29. Every page that
   mounts this shelf carries `ul{margin:0 0 14px 20px;max-width:68ch}` in its
   own inline stylesheet -- a PROSE measure, written for the bulleted lists in
   the copy around the shelf. `.rows` is a <ul>, so it inherited a cap it never
   chose; the featured pull-out above it is a <div>/<section> and escapes the
   identical rule. Measured in headless Chrome at the parent commit, on a shared
   left edge: /docs/concepts and /docs/reference put .feat at 760.00px against
   ul.rows at 746.50px (13.5px of ragged right edge), and /docs/guides at 1280
   and 1440 put it at 874.01px against the same 746.50px (127.5px). The hero and
   the card column now take one width source -- the column they both sit in.
   NOT ALSO ON .grid, and that is a decision rather than an oversight. The paper
   grid carries the same inherited cap, but its cover line and 3:2 crop were
   tuned at the 234.9px card a 746.5px three-column grid produces (.pc-sig
   above says so in those words); widening it is a design change that wants
   eyes on it, not a side effect of a mobile overflow fix. Reported, not done. */
.qsh .rows { list-style:none; margin:0; padding:0; display:grid; gap:10px; max-width:none; }
/* ── the row shelf's own two states ───────────────────────────────────────
   THE FEATURED ROW KEEPS ITS CARD IN THE LIST, exactly as the featured paper
   does: hidden while it is being featured, back the instant a facet is on, so
   it never vanishes from its own collection. Written as its own rule and NOT
   folded into the .pc line above -- that one is pinned by
   tests/shelf-design G4 as a literal, and extending its selector would have
   been a passing assertion quietly rewritten to accommodate new markup.
   THE [hidden] RESET IS A SAFETY NET AND IS DECLARED RATHER THAN ASSUMED. The
   UA sheet hides [hidden] and nothing here sets `display` on these <li>s, so it
   should be redundant -- but filtering is the one thing on this shelf a reader
   can watch fail silently, and the .pc card carries the identical line for the
   identical reason (there it is load-bearing, because .pc sets display:flex). */
.qsh[data-filtered="0"] .rows > li[data-featured] { display:none; }
.qsh .rows > li[hidden] { display:none; }
/* THE EMPTY SHELF NEEDS NO RULE HERE, and the absence is deliberate rather
   than an omission. /docs/evidence keeps its URL, its sub-nav slot and its door
   tile while its material is re-measured (Tom, doc 76 s1.8), but a cleared
   shelf renders its one line as the page LEDE -- which the donor stylesheet
   already sets -- and emits no shelf markup at all: no search box over an empty
   corpus, no facet rail reading zero four times, no sort control with nothing
   to sort, and no empty container either. Three controls that can only ever do
   nothing look exactly like three that work, and an empty shell reads to the
   next person grepping for it as a shelf that failed to fill.
   A `.shelf-empty` rule was written here first, with a matching paragraph in
   the generator, and the built page then said the same sentence twice eighteen
   pixels apart. Both came out. */
/* THE `li` RESET IS LOAD-BEARING, NOT TIDINESS. The donor stylesheet on every
   library page declares a bare `li{margin-bottom:7px}` for its prose lists.
   Grid `gap` does not replace a child's margin, it adds to it, so without this
   the shelf's 10px gap renders as 17px -- a number nothing in this file says
   and nobody chose. Measured on the same pages that already ship `.qsh .grid`:
   the paper cards are subject to the identical rule (18px gap + 7px margin =
   25px). That is NOT corrected here. Those cards shipped and were reviewed at
   that spacing, and silently tightening an approved grid under cover of a
   different unit is the kind of change doc 48 s2.9 is about. It is reported
   instead. */
.qsh .rows > li { margin:0; }
.qsh .row {
  position:relative; overflow:hidden; display:flex; align-items:center; gap:14px;
  background:var(--surface); border:1px solid var(--sh-line); border-radius:var(--sh-r);
  padding:15px 18px 15px 20px; box-shadow:var(--sh-sh-1);
  font-family:var(--sans); color:inherit; text-decoration:none;
  transition:border-color .15s, transform .15s, box-shadow .15s;
}
.qsh .row::before {
  content:""; position:absolute; left:0; top:0; bottom:0; width:3px;
  background:var(--acc);
}
.qsh .row:hover {
  border-color:var(--sh-line-2); transform:translateY(-1px);
  box-shadow:var(--sh-sh-2); color:inherit;
}
.qsh .row .rw { flex:1; min-width:0; }
/* NO text-transform HERE, and that is the one place this eyebrow deliberately
   departs from .pc-kick. .pc-kick uppercases a LABEL ("Editors & IDEs"); this
   one carries a ROUTE, and `/DOCS/GUIDES/MCP` is not a path that resolves. It
   is also the string a reader copies, so it is set a shade larger and with
   less tracking than the uppercase kicker, which is tuned for caps. */
/* THE ROUTE MUST BE ALLOWED TO BREAK, OR IT SETS A FLOOR UNDER THE DOCUMENT.
   2026-08-29. A route is one unbroken monospace token -- no space, no hyphen,
   and Chrome does not break after `/` -- so its min-content size is the WHOLE
   string, and min-content is what an auto grid track is floored at. The two
   things that look like they already handle this do not: `.rw` carries
   min-width:0, which lets the item shrink once the container has a size but
   leaves the intrinsic size the track is built from untouched; and
   `overflow:hidden` on the card clips the paint without narrowing the box.
   Measured in headless Chrome on /docs/guides at the parent commit:
   `/docs/guides/{researching,building,testing}-a-mode` gave .r a 313.2px
   min-content, the three .row-off cards each laid out at 352.7px from left:20,
   and document.scrollWidth was 372 at every viewport below it -- +52px at 320,
   +12px at 360, none at 375. The only page in the set that scrolled sideways.
   `anywhere` and not `break-word`: only `anywhere` is counted in min-content
   sizing, and min-content is the number that was wrong. It introduces no break
   at any width where the route already fits, so nothing wider moves. */
.qsh .row .r, .qsh .row:hover .r {
  display:flex; align-items:center; gap:7px; font-family:var(--mono);
  font-size:10.5px; letter-spacing:.05em; color:var(--acc-ink); margin:0 0 6px;
  overflow-wrap:anywhere;
}
.qsh .row .r i { width:6px; height:6px; border-radius:50%; background:var(--acc); flex:none; }
.qsh .row b, .qsh .row:hover b {
  display:block; font-size:15px; font-weight:600; line-height:1.3;
  letter-spacing:-.012em; color:var(--ink); margin:0 0 3px;
}
.qsh .row .d, .qsh .row:hover .d {
  display:block; font-size:13px; line-height:1.55; color:var(--ink-mid); max-width:62ch;
}
/* Inline literals in a row description are CODE, never amber — amber means
   actionable and a literal is not clickable. Same ruling as .pc .bl above. */
.qsh .row .d em, .qsh .row .d code {
  font-family:var(--mono); font-style:normal; font-size:.94em; color:var(--ink); background:none;
}
/* ── the companion line: the paper that sits beside this guide ────────────
   The whitepaper card has carried "Setup guide: <name>" since it shipped, and
   the pairing was only ever readable from that side -- a reader on the guides
   shelf could not see that a paper existed for the page they were looking at.
   The manifest now carries the reciprocal, so this is the other half of one
   fact rather than a new one.
   IT IS TEXT AND NOT A LINK, AND THAT IS A CONSTRAINT RATHER THAN A CHOICE.
   The whole row card is a single <a>, and an <a> inside an <a> is not a thing
   HTML has: the parser closes the outer anchor early and the card comes apart.
   The paper card gets away with a real link because it is a <div> with a
   footer. Making this one a link means the row card stops being one anchor,
   which is a visible redesign of five shelves and wants eyes on it first.
   Same ink and nearly the same size as .pc-pair, which is the line this
   mirrors: --sh-ink-3 is 5.43:1 on --surface in light and 5.39:1 in dark. */
.qsh .row .pair, .qsh .row:hover .pair {
  display:block; font-size:12px; line-height:1.5; color:var(--sh-ink-3);
  margin:7px 0 0;
}
.qsh .row .ar, .qsh .row:hover .ar {
  flex:none; color:var(--acc-ink); transition:transform .15s;
}
.qsh .row:hover .ar { transform:translateX(3px); }

/* ── .row-off: the same card for a page that DOES NOT EXIST YET ───────────
   The library has always named its own holes ("Not written yet"), and that
   list moved out of /docs onto the four shelves when /docs stopped being an
   index -- each shelf now names only its own. A hole still has to look like it
   belongs on the shelf, and it must not look clickable for even one frame.
   So: the same box and the same rail, but a <span>, no arrow, and the DASHED
   border this file already uses for "there is nothing here" (.tail, .empty).
   The route stays, because it is the only thing on the card that is a fact.
   Doc 63's ruling one rung down: an unreachable destination is never shipped
   as something that looks reachable. */
.qsh .row-off {
  box-shadow:none; border-style:dashed; background:var(--sh-surf-2); cursor:default;
}
.qsh .row-off:hover { transform:none; box-shadow:none; border-color:var(--sh-line); }
.qsh .row-off b { color:var(--ink-mid); }              /* 6.68:1 on --sh-surf-2 */
/* 4.73:1 on --sh-surf-2 -- the same pair this file already records as its
   worst, and still above the 4.5:1 AA floor. Computed, not carried over. */
.qsh .row-off .r { color:var(--sh-ink-3); }
.qsh .row-off .r i { background:var(--sh-line-2); }
.qsh .shelf-more { margin:12px 0 0; max-width:none; }

/* ══ QUICK LOOK — an in-page overlay. The page is never lost. ═════════════*/
#qlScrim {
  position:fixed; inset:0; z-index:60; background:var(--sh-wash);
  backdrop-filter:blur(3px); opacity:0; pointer-events:none; transition:opacity .2s ease;
}
#qlScrim.on { opacity:1; pointer-events:auto; }
#qlRoot {
  position:fixed; inset:0; z-index:61; display:flex; align-items:center; justify-content:center;
  padding:24px; pointer-events:none; opacity:0; transition:opacity .2s ease;
  font-family:var(--sans); color:var(--ink);
}
#qlRoot.on { opacity:1; pointer-events:auto; }
#qlRoot .ql-box {
  width:min(880px,100%); max-height:min(88vh,760px); display:flex; flex-direction:column;
  background:var(--surface); border:1px solid var(--sh-line-2); border-radius:18px;
  box-shadow:var(--sh-sh-4); overflow:hidden; transform:translateY(10px) scale(.985);
  transition:transform .24s cubic-bezier(.32,.72,0,1);
}
#qlRoot.on .ql-box { transform:none; }
#qlRoot .ql-bar {
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  padding:12px 12px 12px 20px; border-bottom:1px solid var(--sh-line); background:var(--sh-surf-2);
}
#qlRoot .ql-kick {
  font-family:var(--mono); font-size:10px; letter-spacing:.16em;
  text-transform:uppercase; color:var(--sh-ink-3); margin:0;
}
#qlRoot .ql-nav { display:flex; align-items:center; gap:6px; }
#qlRoot .icon-btn {
  width:32px; height:32px; border:1px solid var(--sh-line); border-radius:var(--sh-r-sm);
  background:var(--surface); cursor:pointer; display:inline-flex; align-items:center;
  justify-content:center; color:var(--ink-mid); padding:0; transition:border-color .15s, color .15s;
}
#qlRoot .icon-btn:hover:not(:disabled) { border-color:var(--sh-line-2); color:var(--ink); }
#qlRoot .icon-btn svg { width:16px; height:16px; }
#qlRoot .icon-btn:disabled { opacity:.35; cursor:default; }
#qlRoot .ql-sep { width:1px; height:20px; background:var(--sh-line-2); margin:0 3px; }
#qlRoot .ql-body { display:grid; grid-template-columns:1fr; gap:0; overflow-y:auto; overscroll-behavior:contain; }
@media (min-width:760px) { #qlRoot .ql-body { grid-template-columns:328px 1fr; } }
#qlRoot .ql-cover {
  margin:0; background:var(--sh-cover); padding:24px; display:flex; align-items:flex-start;
  justify-content:center; border-bottom:1px solid var(--sh-line); position:relative;
}
@media (min-width:760px) { #qlRoot .ql-cover { border-bottom:0; border-right:1px solid var(--sh-line); } }
#qlRoot .ql-cover img {
  display:block; width:100%; max-width:240px; height:auto; border-radius:5px;
  box-shadow:0 10px 34px rgba(0,0,0,.55); border:1px solid rgba(243,238,227,.10);
}
@media (min-width:760px) { #qlRoot .ql-cover img { max-width:280px; } }
#qlRoot .ql-glow {
  position:absolute; inset:0; pointer-events:none; mix-blend-mode:screen; opacity:.26;
  background:radial-gradient(120% 80% at 100% 0%, var(--acc), transparent 62%);
}
#qlRoot .ql-info { padding:24px 26px 26px; min-width:0; }
#qlRoot .ql-info .kick {
  display:flex; align-items:center; gap:7px; font-family:var(--mono); font-size:9.5px;
  letter-spacing:.16em; text-transform:uppercase; color:var(--acc-ink); margin:0 0 10px;
}
#qlRoot .ql-info .kick i { width:7px; height:7px; border-radius:50%; background:var(--acc); }
#qlRoot .ql-info h2 {
  font-family:var(--serif); font-weight:600; font-size:29px; line-height:1.12;
  letter-spacing:-.015em; margin:0 0 12px; color:var(--ink);
}
#qlRoot .ql-info .bl { font-size:14.5px; line-height:1.6; color:var(--ink-mid); margin:0 0 20px; max-width:52ch; }
#qlRoot .facts {
  display:grid; grid-template-columns:auto 1fr; gap:7px 18px; margin:0 0 22px;
  padding:14px 0; border-top:1px solid var(--sh-line); border-bottom:1px solid var(--sh-line);
}
#qlRoot .facts dt {
  font-family:var(--mono); font-size:10px; letter-spacing:.13em;
  text-transform:uppercase; color:var(--sh-ink-3); padding-top:2px;
}
#qlRoot .facts dd { margin:0; font-size:13.5px; color:var(--ink-mid); min-width:0; overflow-wrap:anywhere; }
#qlRoot .facts dd.mono { font-family:var(--mono); }
#qlRoot .facts dd .link-btn { font-size:13.5px; }
/* The overlay's own action row. #qlRoot carries the `.qsh` class, so the
   segmented control's rules already reach it -- the only difference here is
   that quick look has nowhere further to go, so the secondary is "Open full
   PDF" as a quiet link instead of a second segment. */
#qlRoot .ql-act { display:flex; flex-wrap:wrap; align-items:center; gap:14px; }
/* ...and the row shelves' hero, for the same reason on the other side: a page
   is read, and there is no second thing to offer beside reading it. One
   segment, so it is a pill and not a pill with a flat right edge. Added to the
   existing selector rather than given a rule of its own -- it is the identical
   statement ("this split has one half") and two copies of it would be two
   places to fix a radius. */
#qlRoot .ql-act .split-lg .sp-a, .qsh .split-solo .sp-a { border-radius:10px; }
#qlRoot .ql-act > .link-btn { display:inline-flex; align-items:center; gap:6px; }
#qlRoot .ql-act svg { width:13px; height:13px; flex:none; }
#qlRoot .ql-hint {
  font-family:var(--mono); font-size:10.5px; letter-spacing:.08em;
  color:var(--sh-ink-3); margin:16px 0 0;
}
#qlRoot .ql-hint kbd {
  border:1px solid var(--sh-line-2); border-radius:4px; padding:1px 5px;
  background:var(--sh-surf-2); font-family:inherit; font-size:10px;
}
@media (max-width:759px) {
  #qlRoot { padding:0; align-items:flex-end; }
  #qlRoot .ql-box { max-height:92vh; border-radius:18px 18px 0 0; border-bottom:0; }
  #qlRoot .ql-info h2 { font-size:24px; }
}

@media (prefers-reduced-motion:reduce) {
  .qsh *, .qsh *::before, .qsh *::after,
  #qlRoot, #qlRoot *, #qlScrim {
    animation-duration:.01ms !important; transition-duration:.01ms !important;
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   THE DARK THEME. 2026-08-29.

   ONLY LITERALS ARE REDECLARED HERE. Everything above that reads a token
   (--cream, --surface, --ink, --ink-mid, --amber, --amber-text, --cream-border)
   already follows the theme, because analytics.js redefines those tokens on
   :root[data-theme="dark"] before first paint. Restating them here would be a
   second place they live, which is the whole failure this file's header is
   about. What is left is the --sh-* block near the top and the three accent
   inks: those were chosen as fixed hexes against white, and a hex cannot
   follow a token.

   ONE SELECTOR, MATCHING THE TOKENS' OWN. `:root[data-theme="dark"]` is
   (0,2,0) and beats the plain `.qsh` / `#qlRoot` declarations above by
   specificity rather than by document order.

   Measured on the dark grounds (--surface #1F1C14 / --sh-surf-2 #262218):
     --sh-ink-3   #9A907C   5.39 / 5.02   AA
     amber ink    #D4A24C   7.35 / 6.85   AA
     slate ink    #8FA3BB   6.58 / 6.13   AA
     coral ink    #E2705A   5.43 / 5.06   AA
   Worst text pair 5.02:1, above the 4.5:1 floor and above the 4.73:1 this file
   already measures as its own light-mode floor.

   THE FILLED BUTTON IS THE ONE REAL BUG THIS BLOCK FIXES, and it is worth
   naming because nothing else would have caught it. `.qsh .sp-a` is
   `background:var(--amber-text); color:#FFFFFF`, and it is correct in light:
   --amber-text is #8A6206 there, and white on it is 5.48:1. In dark,
   --amber-text becomes #D4A24C -- a LIGHT gold -- and the hardcoded white
   collapses to 2.31:1. The token followed the theme and the literal beside it
   did not, so the primary action on every whitepaper card would have shipped
   as unreadable. Ink #17150F on that fill is 7.88:1.
   THE COVER FIELD IS DELIBERATELY NOT THEMED. --sh-cover #17150F is the real
   printed artwork's own ground, and a cover does not restyle itself for a
   reader's theme. It is already dark and it stays exactly as it is.
   ══════════════════════════════════════════════════════════════════════════ */
:root[data-theme="dark"] .qsh,
:root[data-theme="dark"] #qlRoot,
:root[data-theme="dark"] #qlScrim {
  --sh-line-2: #443E2C;                    /* one step down, borders only */
  --sh-surf-2: #262218;
  --sh-ink-3: #9A907C;                     /* 5.39:1 on --surface — see above */
  --sh-amber-soft: rgba(212,162,76,.12);
  /* Shadows go to true black on a dark ground. rgba(26,24,20,…) is the ink
     colour of the LIGHT theme; layered over #1F1C14 it is very nearly the same
     value as the surface, so every card would have lost its elevation. */
  --sh-sh-1: 0 1px 2px rgba(0,0,0,.40);
  --sh-sh-2: 0 1px 2px rgba(0,0,0,.40), 0 6px 16px -6px rgba(0,0,0,.55);
  --sh-sh-3: 0 2px 4px rgba(0,0,0,.50), 0 16px 40px -12px rgba(0,0,0,.65);
  --sh-sh-4: 0 24px 70px -18px rgba(0,0,0,.80), 0 4px 12px rgba(0,0,0,.50);
  --sh-wash: rgba(4,3,2,.72);
}
/* The three collection accents. --acc is decorative (a 3px rail, a 6px dot, a
   wash) and --acc-ink is the only one that goes near a letterform, which is why
   they are separate and why only the ink had to clear AA above. */
:root[data-theme="dark"] .a-amber { --acc:#D4A24C; --acc-ink:#D4A24C; }
:root[data-theme="dark"] .a-slate { --acc:#8FA3BB; --acc-ink:#8FA3BB; }
:root[data-theme="dark"] .a-coral { --acc:#E2705A; --acc-ink:#E2705A; }
/* See the note above: the fill follows the theme, so the ink on it must too. */
:root[data-theme="dark"] .qsh .sp-a,
:root[data-theme="dark"] .qsh .sp-a:hover { color:#17150F; }   /* 7.88:1 */
