/* cheatsheet.css - the API card. Loads only on /docs/cheatsheet, AFTER tokens.css,
   base.css and components.css, and CONSUMES their tokens: nothing here redefines one.

   Where a token name could not be confirmed from the build contract (the radius and
   motion scales are specified by value, not by name) it is used with its contract value
   as the fallback - var(--r-md) - so this file is correct either way.

   SYNTAX COLOURS ARE NOT DEFINED HERE. The site has one token vocabulary
   (.cm .pp .kw .ty .rc .fn .st .nu .pn .op) and components.css section 8.1 is the only
   file that colours it, shared by this page's server-rendered signatures and by
   assets/js/codeblock.js. This file lays the signatures out; it never inks them.

   Gold is deliberately NOT used for text: #8a6a00 on --bg-raised measures 4.28:1 in
   the light theme and fails. It appears here only as a rule, a bar or a focus ring. */

/* --------------------------------------------------------------------- page frame -- */
/* The page sits inside the site's own .wrap, so the gutter and the measure come from
   base.css; this adds only the vertical rhythm and the width of the rail. */
.cs {
  --cs-rail: 13.5rem;
  padding-block: var(--sp-6) var(--sp-16);
}

/* This page is the one route on the site that is a REFERENCE TABLE rather than prose,
   and it is the only one whose readability is a function of available width.

   It used to widen EVERY .wrap to 94rem to buy the signature column more room. That
   also widened the shared header and footer, so navigating to this page visibly shifted
   the whole navbar sideways - the site appeared to change shape underneath the reader.
   Reported from production on 2026-08-21, and it was right: a chrome that moves between
   routes reads as broken, and no amount of extra column width is worth it.

   The shell now matches every other route exactly. Long signatures are not clipped -
   the ROW, `.entries dt`, is the overflow-x:auto box, so they scroll where they sit.
   (This said `.sig`, which has no overflow property at all; so did the note further
   down. A comment naming a selector it does not sit on is what drifted, and anyone
   acting on either would have moved overflow onto the wrong element.) */
.cs-head .prose,
.cs-head .lede,
.cs-source { max-width: var(--measure); }   /* prose keeps its measure regardless */

/* Block, not flex+gap: a row hidden by the filter with hidden="until-found" is still
   in the box tree (that is the point - Ctrl+F must reach it), so a flex gap would
   still be drawn between two invisible sections. A margin can be zeroed; a gap cannot. */
.cs-body { min-width: 0; }
.cs-section + .cs-section,
.cs-body > .doc-license { margin-top: var(--sp-10); }

.cs-head { min-width: 0; margin-bottom: var(--sp-8); }
.cs-head h1 {
  font-size: var(--step-5); font-weight: 900; letter-spacing: var(--track-display);
  line-height: var(--lh-display); margin: 0 0 var(--sp-4);
}
.cs-head .prose { max-width: var(--measure); }
.cs-head .lede { font-size: var(--step-2); color: var(--fg); }

.pagemeta {
  display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-6);
  list-style: none; margin: var(--sp-6) 0 var(--sp-3); padding: 0;
}
.pagemeta li { display: flex; align-items: baseline; gap: var(--sp-2); }
.pagemeta span {
  font-size: var(--step--1); font-weight: 600; text-transform: uppercase;
  letter-spacing: var(--track-eyebrow); color: var(--fg-dim);
}
.pagemeta b {
  font-family: var(--font-mono); font-size: var(--step-0); font-weight: 600;
  color: var(--fg-strong); font-variant-numeric: tabular-nums;
}
.cs-source {
  max-width: var(--measure); margin: 0; color: var(--fg-muted); font-size: var(--step--1);
}
.cs-source code { font-size: inherit; }

/* The filter is progressive enhancement: the attribute stays until cheatsheet.js
   removes it, so a reader with JS off is never shown a control that does nothing.
   The hiding itself is base.css's `[hidden]:not([hidden="until-found" i])` rule,
   which is `!important`; a local `.cs-filter[hidden] { display: none }` used to sit
   here and could never change the outcome in either direction.
   The filter ships inside the nav rail, which is sticky from 1100px up, so it stays
   reachable instead of scrolling away 810px above the fold on a 31,000px page. */
.cs-filter { margin: 0 0 var(--sp-5); max-width: 34rem; }
.cs-filter input {
  /* --fg-dim, not a surface rule: this is the boundary of a control, and
     --border-strong measures 1.94:1 against the nav surface behind it. Same
     substitution, same reason, as .copy-btn and .btn-ghost in components.css. */
  width: 100%; min-height: var(--tap); padding: var(--sp-2) var(--sp-4);
  font: inherit; font-size: var(--step-0);
  color: var(--fg-strong); background: var(--bg-well);
  border: 1px solid var(--fg-dim); border-radius: var(--r-md);
}
/* --fg-muted, not --fg-dim: on --bg-well the dim token has always been the closest
   of the ink tokens to the 4.5:1 this site holds itself to for anything read at body
   size, and a placeholder in a search box is read. --fg-muted has clear room on the
   same surface. Run `python3 tools/contrast.py` for the figures; it is the authority
   and these comments are not. */
.cs-filter input::placeholder { color: var(--fg-muted); }
.cs-filter-status {
  margin: var(--sp-2) 0 0; min-height: 1.2em;
  font-size: var(--step--1); color: var(--fg-muted);
}

/* --------------------------------------------------------------------------- nav -- */
.cs-nav {
  background: var(--bg-raised); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: var(--sp-4);
  margin-bottom: var(--sp-8);
}
.cs-nav-label {
  margin: 0 0 var(--sp-2); font-size: var(--step--1); font-weight: 600;
  text-transform: uppercase; letter-spacing: var(--track-eyebrow); color: var(--fg-dim);
}
.cs-nav-label + .cs-nav-list { margin-bottom: var(--sp-5); }
.cs-nav-list:last-child { margin-bottom: 0; }
.cs-nav-list { display: flex; flex-wrap: wrap; gap: var(--sp-1); list-style: none;
  margin: 0; padding: 0; }
.cs-nav-list a {
  display: block; padding: var(--sp-2) var(--sp-3); min-height: 36px;
  font-family: var(--font-mono); font-size: var(--step--1); line-height: 1.4;
  color: var(--fg-muted); text-decoration: none;
  border-radius: var(--r-sm); border-left: 2px solid transparent;
}
.cs-nav-list a:hover { color: var(--fg-strong); background: var(--bg-well); }
/* scroll-spy sets aria-current; without JS nothing does, and the list still works. */
.cs-nav-list a[aria-current="true"] {
  color: var(--fg-strong); background: var(--bg-well); border-left-color: var(--gold);
}

/* ---------------------------------------------------------------------- sections -- */
.cs-section { min-width: 0; scroll-margin-top: calc(var(--header-h) + var(--sp-4)); }
.sec-head {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--sp-2);
  margin: 0; font-size: var(--step-3); font-weight: 800; letter-spacing: var(--track-head);
  line-height: var(--lh-head);
}
.cs-section.sub .sec-head { font-size: var(--step-2); font-weight: 700; }
.sec-kind {
  font-family: var(--font-mono); font-size: var(--step--1); font-weight: 500;
  letter-spacing: 0; color: var(--fg-dim);
}
.sec-name { color: var(--fg-strong); }
.sec-tagline {
  margin: var(--sp-2) 0 0; max-width: var(--measure);
  color: var(--fg-muted); font-size: var(--step-0);
}
.sec-count {
  margin: var(--sp-1) 0 var(--sp-4); font-family: var(--font-mono);
  font-size: var(--step--1); color: var(--fg-dim); font-variant-numeric: tabular-nums;
}
/* The layer pill is the motley: L1 teal, L2 violet, L3 gold - the two-line app. */
.layer {
  display: inline-block; padding: 0 var(--sp-2);
  font-family: var(--font-mono); font-size: var(--step--1); font-weight: 600;
  color: var(--fg-strong); background: var(--bg-well);
  border-left: 3px solid var(--border-strong);
  border-radius: var(--r-sm);
}
.layer-l1 { border-left-color: var(--teal); }
.layer-l2 { border-left-color: var(--violet); }
.layer-l3 { border-left-color: var(--gold); }

.group { margin-top: var(--sp-6); }
.group:first-child { margin-top: 0; }
.group-head {
  display: flex; align-items: baseline; gap: var(--sp-2);
  margin: 0 0 var(--sp-2);
  font-size: var(--step-1); font-weight: 700; letter-spacing: var(--track-sub);
  color: var(--fg-strong);
}
.group-gloss {
  margin: 0 0 var(--sp-3); max-width: var(--measure);
  color: var(--fg-muted); font-size: var(--step--1);
}

/* ---------------------------------------------------------------------- entries --
   One function, one line, one row. Alignment is a two-column grid, never literal
   spaces: the columns then size themselves per group and the comment is a box of its
   own, which is what makes the narrow-screen stack possible at all. */
.listing {
  background: var(--bg-raised); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: var(--sp-5);
}
.entries {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(24ch, 1fr);
  margin: 0;
}
.entries dt, .entries dd { min-width: 0; padding: 1px 0; }
.entries dt {
  --row-bg: var(--bg-raised);
  position: relative;   /* a containing block: see .visually-hidden in base.css */
  overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: thin;
  /* 100 of these 280 rows scroll at 375px. A UA scrollbar is drawn in light chrome
     whatever the theme is, so that was 100 near-white bars down a dark page - see
     components.css section 8 for the measurement and for why the ::-webkit half is
     deliberately not written here (it would cost every row 8px of layout). */
  scrollbar-color: var(--fg-dim) transparent;
  scroll-margin-top: calc(var(--header-h) + var(--sp-4));

  /* The scroll shadow itself is drawn by components.css section 8.0, which names
     .entries dt directly. All this row owes it is the colour it is painted on, and
     --row-bg re-resolves on hover and :target, so the covers follow the row. */
  --scroll-cover: var(--row-bg);
}
/* The gutter belongs to the COMMENT column, not the scrolling one: padding inside a
   scroll container scrolls away with the content, and a clipped signature would then
   butt straight into the description with no gap at all. */
.entries dd { margin: 0; padding-left: var(--sp-5); }
/* The hovered row is cut OUT of the slab rather than sunk into it: --bg-well is the
   tightest surface in the light theme for the RC_ type names (teal), and the row a
   reader is pointing at is the last place to spend contrast. --bg has clear room.
   Figures: `python3 tools/contrast.py`. */
/* --row-bg, not `background`, so the scroll-shadow covers stay the same colour as the
   row they sit on. Setting `background` here would replace the whole shorthand and take
   the four shadow layers components.css section 8.0 draws with it. */
.entries dt:hover, .entries dt:target, .entries dt:has(.alias:target) { --row-bg: var(--bg); }
.entries dt:hover + dd, .entries dt:target + dd,
.entries dt:has(.alias:target) + dd { background: var(--bg); }
.entries dt:target, .entries dt:has(.alias:target) { box-shadow: inset 3px 0 0 var(--gold); }
.alias { position: absolute; left: 0; top: 0; }

/* A palette chip is one of 25 identical squares; landing on #sym-RC_ROSE with no mark
   on it tells the reader nothing about which one they asked for. */
.palette li:target { box-shadow: inset 0 0 0 2px var(--gold); border-radius: var(--r-sm); }

.sig {
  display: block; white-space: pre; font-family: var(--font-mono);
  font-size: var(--step--1); line-height: 1.55; padding-right: var(--sp-2);
}
/* The token colours are NOT here. .fn .rc .ty .pn .op .cm are the site's one syntax
   vocabulary and components.css section 8.1 is the only place any of them is given a
   colour - this page, codeblock.js and the hand-written samples all consume the same
   set. Two definitions is how the cheatsheet and the home page drifted apart in the
   first place. Nothing on this page may re-colour a token; change it there. */

.doc {
  font-size: var(--step--1); line-height: 1.55; color: var(--fg-muted);
  padding-right: var(--sp-2);
}
/* The descriptions carry unbreakable runs - RC_CURSOR_DEFAULT/POINTER/TEXT/GRAB/...,
   and one 435-character line listing 119 key tokens. Without this the page itself
   scrolls sideways at 375px, which is the exact failure this design is fixing.
   Only `.entries dt` is allowed a horizontal scrollbar, and only inside its own box.
   See the rule at `.entries dt` above, which is where the scroll box is declared. */
.doc, .cnote, .note, .fields li, .callout-body, .prose, .see-also,
.enumtable dd, .sec-tagline, .group-gloss, .example figcaption, .cs-source,
.cs-filter-status, .apiref { overflow-wrap: anywhere; }
/* 1em, not 0.95em. The em multipliers COMPOUND: a <code> inside .doc (13px) inside an
   .apiref that was itself 0.9em computed to 11.1px, well under the 13px mono floor the
   build contract sets in section 8.10 - and these are the cross-reference chips and the
   whole enumerations table, not incidental text. Mono at the same px as its host reads
   slightly larger anyway; the background and the family already separate it. */
/* base.css sets code to .875em, which is right at the site's 17px body size and wrong
   inside every 13px region on this page: 13 x .875 = 11.4px, under the contract's 13px
   mono floor (section 8.10). Reset it to 1em wherever the host text is already at
   --step--1. Written through :where() so the whole selector carries ZERO specificity -
   it must beat base.css's own :where(code, pre, ...) on source order alone and lose to
   every class below, including .sig. Without that it out-specifies .sig and every
   signature on the page renders at the body's 17px. */
:where(.listing, .freeform, .enumtable, .palette, .see-also, .note, .sec-tagline,
       .cs-source, .apiref) code { font-size: 1em; }

.doc code, .cnote code, .group-gloss code, .sec-tagline code, .apiref code {
  font-family: var(--font-mono); font-size: 1em;
  color: var(--fg); background: var(--bg-well);
  padding: 0 4px; border-radius: var(--r-xs);
}

.permalink {
  float: right; margin-left: var(--sp-2);
  color: var(--fg-dim); text-decoration: none; font-family: var(--font-mono);
  opacity: 0; transition: opacity var(--dur-fast) var(--ease);
}
.entries dt:hover + dd .permalink, .doc:hover .permalink,
.group-head:hover .permalink, .sec-head:hover .permalink,
.palette li:hover .permalink { opacity: 1; }
.permalink:focus-visible { opacity: 1; }
.group-head .permalink, .sec-head .permalink { float: none; }

/* Continuation comments: the author's own annotations, kept with their entry. */
.cnote { margin: var(--sp-1) 0 var(--sp-2); color: var(--fg-muted); }
.doc .cnote:last-child { margin-bottom: var(--sp-3); }
.fields {
  display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-3);
  list-style: none; margin: var(--sp-1) 0 var(--sp-2); padding: 0;
}
.fields li { color: var(--fg-muted); }
.fields code { background: none; padding: 0; color: var(--fg); font-weight: 600; }
.fieldrow { display: flex; gap: var(--sp-3); margin: var(--sp-1) 0; }
.fieldkey {
  flex: none; min-width: 6.5rem; font-family: var(--font-mono);
  font-size: var(--step--1); font-weight: 600; color: var(--fg-dim);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.fieldbody { min-width: 0; }
.fieldbody > :first-child { margin-top: 0; }

.note {
  margin: var(--sp-3) 0; padding: var(--sp-3) var(--sp-4);
  background: var(--bg-well); border-radius: var(--r-md);
  font-size: var(--step--1); color: var(--fg);
}
.note > :first-child { margin-top: 0; }
.note > :last-child { margin-bottom: 0; }

/* ------------------------------------------------------------- editorial signals --
   The contract reserves clay for the problem and gold for RayClay, so a warning is not
   a fifth colour: it is a change of surface, a rule and a labelled glyph. */
/* .warn, .stop and .glyph match nothing in the page as it stands, and neither does
   any .callout* rule below. That is a property of the PIN, not dead weight: the
   vendored content/cheatsheet.md carries no pictograph at all, so the badge machinery
   in gen_cheatsheet.py emits none. The generator turns every pictograph upstream
   writes into a visible text badge, and CLAUDE.md guarantees that path, so deleting
   these would ship unstyled badges at the next re-vendor. Kept deliberately. */
.warn, .stop {
  display: inline; padding: 1px var(--sp-2);
  background: var(--bg-well); color: var(--fg-strong);
  box-shadow: inset 3px 0 0 var(--border-strong);
  border-radius: var(--r-xs);
}
.stop { box-shadow: inset 3px 0 0 var(--fg-strong); font-weight: 500; }
.warn .glyph, .stop .glyph { margin-right: 4px; }

.badge {
  display: inline-block; padding: 0 var(--sp-2); vertical-align: baseline;
  font-family: var(--font-mono); font-size: 0.85em; font-weight: 600;
  color: var(--fg); background: var(--bg-well);
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
}
/* All five callout kinds the generator can emit wear the same pill. Only
   `warn` had a rule here, stripping its border and background, so a bulleted
   Warning would have rendered as bare text beside a Critical pill on the same
   list. None of the five is reachable in the current vendored markdown, which is
   why nobody could see the asymmetry; the pill is the documented look, so the
   exception goes rather than being extended to four more selectors. */

/* A cross-reference into docs/api-notes.md. Every one of the 89 names a heading in
   THAT document, so none of them is a link while it is unpublished: the generator does
   not invent an anchor it cannot check, and resolving them against this page's own
   symbols produced 52 links whose destination was the row the reader was standing on.
   Given --api-notes the whole chip becomes one <a>, hence the rule on .apiref itself.
   1em, not 0.9em: 0.9em of an already-13px parent computes to 11.7px. */
.apiref { font-size: 1em; color: var(--fg-dim); white-space: normal; }
/* U+203A, not U+25B8. The small right triangle is not in Rubik at all - not in the
   subset this site ships and not in the upstream variable font either - so all 85 of
   these markers were drawn by whatever system face the reader happens to have, on a
   page whose whole typography is otherwise one self-hosted file. Confirmed three ways
   on 2026-08-27: the shipped cmap, a canvas advance-width test against a fallback-only
   stack, and the source TTF. The single right angle quote is in the face, does the
   same job, and cannot fall back. */
/* The `/ ""` is the alt-text form: it keeps the marker visual and takes it OUT of
   the accessible name. Chromium reads generated content into the name, so all 85 of
   these chips were announced with a stray glyph before the label, and changing which
   glyph did not stop that. Two characters the vendored markdown itself writes still
   fall back on this page; see ATTRIBUTIONS.md. */
.apiref::before { content: "\203A" / ""; margin: 0 4px 0 2px; color: var(--fg-dim); }
/* --fg-dim is the tightest ink on a well, and the only text on this page that has
   ever measured under 4.5:1 in either theme was a chip that had wandered onto one.
   Those two surfaces are the wells; on them the chip takes the next ink up,
   --fg-muted. Same rule that keeps code off --bg-well. Figures live in
   `tools/contrast.py`, not here.
   The :not(a) is not decoration: with --api-notes the whole chip becomes an <a> and
   this rule would quietly strip a link of its link colour. */
.note .apiref:not(a), .note .apiref:not(a)::before,
.callout .apiref:not(a), .callout .apiref:not(a)::before { color: var(--fg-muted); }
a.apiref { color: var(--teal); text-decoration-color: color-mix(in srgb, var(--teal) 40%, transparent); }
a.apiref code { background: none; padding: 0; color: inherit; }
.apiref-or { color: var(--fg-dim); }

.callout {
  display: flex; gap: var(--sp-3); align-items: flex-start;
  margin: var(--sp-4) 0; padding: var(--sp-3) var(--sp-4);
  background: var(--bg-well); border-radius: var(--r-md);
  border-left: 3px solid var(--border-strong);
}
.callout-body { min-width: 0; max-width: var(--measure); color: var(--fg); }
.callout-glyph { flex: none; line-height: var(--lh-body); }
.callout-critical, .callout-stop { border-left-color: var(--fg-strong); }
.callout-rule { border-left-color: var(--teal); }
.callout-info { border-left-color: var(--violet); }

.see-also {
  margin: var(--sp-3) 0; padding-left: var(--sp-3);
  border-left: 2px solid var(--border-strong);
  font-size: var(--step--1); color: var(--fg-muted);
}

/* ----------------------------------------------------------- examples + tables -- */
/* Cut OUT of the slab, not sunk into it - the same ruling as .entries dt:hover, for
   the same reason: --bg-well is the tightest light-theme surface for the RayClay teal
   and for --fg-dim punctuation, and a code block is only ever allowed on --bg or
   --bg-raised. Figures: `python3 tools/contrast.py`. */
.example {
  margin: var(--sp-4) 0; background: var(--bg);
  border: 1px solid var(--border); border-radius: var(--r-md);
  overflow: hidden;
}
.example figcaption {
  padding: var(--sp-2) var(--sp-4); color: var(--fg-muted);
  font-size: var(--step--1); border-bottom: 1px solid var(--border);
}
/* position: relative makes the <pre> the containing block for any absolutely
   positioned descendant - the .visually-hidden label inside an .apiref chip. Without
   it that 1px span escapes this box's clip, reports a position measured from the
   page's left edge, and drags the whole document's scrollWidth out to 984px at a
   360px viewport. base.css anchors .visually-hidden as well; this is the local half
   of the same fix, and it also contains the .alias anchors inside .entries dt. */
.example pre {
  position: relative;
  margin: 0; padding: var(--sp-4); overflow-x: auto; scrollbar-width: thin;
  /* components.css section 8 explains the ink and why there is no ::-webkit half. */
  scrollbar-color: var(--fg-dim) transparent;
}
/* The <pre> is a keyboard-focusable scroll region (the generator gives it tabindex,
   role and a name; 8 of these 12 blocks overflow at 1440px and every one of them
   does at 375px). Its focus ring is drawn OUTSIDE its border box, and .example is an
   `overflow: hidden` box whose padding box the <pre> fills exactly - so at the
   global outline-offset of 2px every pixel of the ring fell outside the clip and the
   block took focus with no indicator at all. Drawn inside instead: same 2px gold, on
   the block's own padding, where nothing can clip it. The offset is negative rather
   than zero so the ring reads as a ring and not as a border on the code. */
.example pre:focus-visible { outline-offset: -3px; }
/* 1.7 is components.css's .code, not a second number: an example here and a sample on
   /docs/getting-started are the same kind of thing - several lines of C you read top
   to bottom - and they were set 1.65 and 1.7. (.sig keeps its own 1.55: that is one
   line per row in a 280-row list, where the leading is the rhythm of the LIST.) */
.example code {
  font-family: var(--font-mono); font-size: var(--step--1); line-height: 1.7;
  color: var(--fg); background: none; padding: 0;
}
/* Inside a code block a reference stays on its own line of code and scrolls with it,
   rather than wrapping and breaking the alignment of the example. */
.example .apiref { white-space: pre; }

/* components.css takes the copy button out of the overlay and drops it into the flow
   under the block in two cases: on a coarse pointer (it cannot be revealed on hover,
   and a permanently visible overlay would cover the comment, which on this page is
   the payload) and in the fail state (an instruction, nearly twice as wide, that
   stays until the next attempt). In here that flow ends flush against the figure's
   own border, so the button borrows the inset the <pre> already has and lands on the
   same right edge as the code. The <pre>'s bottom padding is the gap, so the top
   margin components.css sets for the standalone case is taken back off.

   Both selectors name a state in which the button is STATIC. A margin on the overlay
   would push it 16px off the corner it is pinned to and onto more of the code. */
@media (hover: none) {
  .example .copy-btn { margin: 0 var(--sp-4) var(--sp-4); }
}
.example .copy-btn[data-state="fail"] { margin: 0 var(--sp-4) var(--sp-4); }
/* The hero sits on the page itself, so it lifts instead of cutting in. --bg-raised
   is the other legal code surface; components.css section 8.1 carries the table. */
.cs-head .example { background: var(--bg-raised); }
/* components.css section 8 paints the clipped-edge cue with --scroll-cover; in
   here the example sits on the raised surface, so the cover has to match it or
   the fade shows as a pale band over the code. */
.cs-head .example pre { --scroll-cover: var(--bg-raised); }

.palette {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: var(--sp-1) var(--sp-3); list-style: none; margin: var(--sp-2) 0; padding: 0;
}
.palette li { display: flex; align-items: baseline; justify-content: space-between; }
.palette code {
  font-family: var(--font-mono); font-size: var(--step--1); color: var(--teal);
  background: none; padding: 0;
}
.palette .permalink { float: none; }

.freeform { margin: var(--sp-4) 0; }
.freeform-lead {
  margin: 0 0 var(--sp-2); font-family: var(--font-mono);
  font-size: var(--step--1); color: var(--fg-strong);
}
.enumtable {
  display: grid; grid-template-columns: minmax(0, max-content) minmax(24ch, 1fr);
  gap: 2px var(--sp-5); margin: 0 0 var(--sp-3);
  font-size: var(--step--1);
}
.enumtable dt { min-width: 0; }
.enumtable dt code {
  font-family: var(--font-mono); color: var(--teal); background: none; padding: 0;
}
.enumtable dd { margin: 0; color: var(--fg-muted); }
.freeform-ref { margin-top: var(--sp-2); }

/* ------------------------------------------------------------------------ prose -- */
.prose { max-width: var(--measure); }
.prose p { margin: 0 0 var(--sp-4); }
.prose > :last-child { margin-bottom: 0; }
.prose-list { margin: 0 0 var(--sp-4); padding-left: var(--sp-5); }
.prose-list li { margin-bottom: var(--sp-2); }
.listing .prose, .listing .callout { max-width: none; }

/* -------------------------------------------------------------- the stacked row --
   Signature on line 1, description on line 2. Used below 900px for every group, and at
   EVERY width for a group the generator marked .wide - one whose longest signature
   cannot fit beside a description at any viewport size (contract section 8.4). */
.entries.wide { display: block; }
.entries.wide dt { padding-right: 0; padding-top: var(--sp-2); }
.entries.wide dd {
  padding: 0 0 var(--sp-2) var(--sp-3);
  border-left: 2px solid var(--border);
  max-width: var(--measure);
}
.entries.wide dt:hover { --row-bg: var(--bg-raised); }
.entries.wide dt:hover + dd { background: none; }

/* ------------------------------------------------------------------- responsive -- */
/* Below this width every row stacks. raylib keeps the two-column line at all widths
   and 82% of the comment goes off-screen. */
@media (max-width: 900px) {
  .entries { display: block; }
  .entries dt { padding-right: 0; padding-top: var(--sp-2); }
  .entries dd {
    padding: 0 0 var(--sp-2) var(--sp-3);
    border-left: 2px solid var(--border);
  }
  .entries dt:hover { --row-bg: var(--bg-raised); }
  .entries dt:hover + dd { background: none; }
  .listing { padding: var(--sp-4) var(--sp-3); }
  .permalink { opacity: 1; }
  .fieldrow { display: block; }
  .fieldkey { min-width: 0; }
  .enumtable { display: block; }
  .enumtable dd { margin: 0 0 var(--sp-2) var(--sp-3); }
}

/* The rail. One number, 1100px, used only here.
   It runs the FULL height of the page, beside the head rather than under it. With the
   head spanning both columns the rail began ~900px down, which put the filter box -
   the page's main affordance - exactly at the fold on a 1440x900 screen, on a document
   26,000px tall. Alongside, it is the first thing in the viewport and stays there. */
@media (min-width: 1100px) {
  .cs {
    display: grid; grid-template-columns: var(--cs-rail) minmax(0, 1fr);
    column-gap: var(--sp-8); align-items: start;
  }
  .cs-head { grid-column: 2; grid-row: 1; }
  .cs-nav { grid-column: 1; grid-row: 1 / -1; }
  .cs-body { grid-column: 2; grid-row: 2; }
  .cs-nav {
    position: sticky; top: calc(var(--header-h) + var(--sp-4)); margin-bottom: 0;
    max-height: calc(100vh - var(--header-h) - 2 * var(--sp-4));
    overflow-y: auto;
    /* The rail scrolls independently, so it gets its own scrollbar. Left at the OS
       default it renders as a wide pale track sitting inside a rounded card, which
       looks like a rendering fault rather than a control. Thin, theme-coloured, on a
       transparent track, with a reserved gutter so the links do not shift by the
       scrollbar's width when the list becomes scrollable. */
    scrollbar-width: thin;
    scrollbar-color: var(--border-strong) transparent;
    scrollbar-gutter: stable;
    /* Reaching the end of the rail must not start scrolling the 26,000px page behind it. */
    overscroll-behavior: contain;
  }
  /* WebKit/Blink still needs the pseudo-elements; scrollbar-width is not honoured there
     on every platform. Same visual result, expressed twice. */
  .cs-nav::-webkit-scrollbar { width: 8px; }
  .cs-nav::-webkit-scrollbar-track { background: transparent; }
  .cs-nav::-webkit-scrollbar-thumb {
    background: var(--border-strong); border-radius: var(--r-full);
    border: 2px solid transparent; background-clip: content-box;
  }
  .cs-nav::-webkit-scrollbar-thumb:hover { background-color: var(--fg-dim); background-clip: content-box; }
  .cs-nav-list { display: block; }
}

/* Touch. Nav items reach the 44px target size. The 308 per-ROW permalinks do not
   appear at all on a coarse pointer: there is no hover to reveal them, 44x44 each
   would add roughly 8,000px to the page, and shipping them at 24x24 was an
   undeclared deviation from the quality floor (contract section 11) rather than a
   decision. Every row is still addressable - the id is on the element, and the 61
   heading permalinks that do survive here are full-size. */
@media (pointer: coarse) {
  .cs-nav-list a { display: flex; align-items: center; min-height: var(--tap); }
  .entries .permalink, .palette .permalink { display: none; }
  .group-head .permalink, .sec-head .permalink {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: var(--tap); min-height: var(--tap); opacity: 1;
  }
}

/* -------------------------------------------------------------- filtered rows --
   cheatsheet.js hides non-matching rows with hidden="until-found" so find-in-page can
   still reach them (contract section 8.8). base.css deliberately leaves that attribute
   alone, so the element keeps a box - and a box with padding, margins and a flex gap
   would leave 280 slivers of empty page behind. Zero them: content-visibility already
   makes the CONTENT weigh nothing, this makes the box weigh nothing too. */
[hidden="until-found"] {
  padding: 0 !important;
  margin: 0 !important;
  border-width: 0 !important;
  min-height: 0 !important;
}

/* A 31,000px document with smooth scrolling means a deep link animates ~20,000px past
   the reader for 1.3-1.9s before it settles. Anchors on a reference page are
   navigation, not a transition. Off here, unchanged for the rest of the site. */
html { scroll-behavior: auto; }


/* The shared shell's own print rules are NOT here any more: .site-header and
   .site-footer moved to components.css and .skip-link to base.css, beside the
   components that define them. This was the site's only @media print block and it
   reached into three shared components, so the other seven pages printed the sticky
   header, the whole nav and the theme button while this one did not. A page
   stylesheet holding the print behaviour of the shared shell is the exact thing
   components.css warns against for .section. */
@media print {
  .cs-nav, .cs-filter, .permalink { display: none; }
  .cs { display: block; max-width: none; padding: 0; }
  /* The palette is scoped to `screen` in tokens.css, so paper gets the light
     values and this can use the token instead of the literal it reached for
     when the dark tokens were still in play. */
  .listing, .example, .note, .callout { border: 1px solid var(--border-strong); background: none; }
  .entries dt, .entries dd { break-inside: avoid; }
}
