/* design/kit/_page.css - the stand's own look, and NOTHING the product ships.

   The split this file enforces: design/system/ is the code a product loads,
   design/kit/ is how a person looks at it. A product class in here, or a stand
   style in a component file, is a defect either way.

   It is also the home of the reviewer's chrome. `.layout`, `.stage`, `.sidebar`
   and the eight --c-* variables lived in kit.css alongside the product and were
   counted as an organism on 55 pages. They never travelled to a build and never
   should: stage 08 takes them out of the system and puts them here.

   ISOLATION RULE. base.css comes onto every stand page on purpose: the stand
   must show the product's own reset and reading defaults, not a private set.
   Where the product frame and the stand disagree, the stand yields. Nothing in
   design/system/ is ever bent to make this page look better; the material is
   shown inside a scene of the stand's own instead. */

:root {
  --kn-surface: #ffffff;
  --kn-border: #e8e4dc;
  --kn-border-light: #f0ede6;
  --kn-text: #2a2724;
  --kn-text-secondary: #68625d;
  --kn-text-muted: #6f6a63;
  --kn-accent: #4e6a5c;
  --kn-accent-light: #eef4f1;
  --kn-control-edge: #8f887e;   /* 3.51:1 on --kn-surface. See the dark half below */
  --kn-radius: 6px;
}
[data-theme="dark"] {
  --kn-surface: #171d1f;
  --kn-border: #2b3538;
  --kn-border-light: #222b2e;
  --kn-text: #e6edee;
  --kn-text-secondary: #b1bdc0;
  --kn-text-muted: #94a1a4;
  --kn-accent: #8fc2ac;
  --kn-accent-light: #1b2a25;
  --kn-control-edge: #6b7578;   /* 3.60:1 on --kn-surface */
}

/* --kn-control-edge, added 2026-08-12, and the same split design/_screen.css
   makes for the product's panel. --kn-border outlines the panel and rules between
   its parts, which is decoration at 1.27:1 light and 1.35:1 dark. The theme
   switch's unpressed button is a CONTROL whose edge is the only thing marking
   where it begins, and WCAG 1.4.11 asks 3:1 of that. It had been reading the
   decorative value in both, so the one control this whole step depends on was the
   one below the threshold. */

.kit-body { min-height: 100vh; }

/* THE STAND'S PROSE LINKS, and the reason this rule is dated 2026-08-12 rather
   than written on the day the stand was built is the whole argument for a dark
   stress test. 993 classless anchors across 56 pages of this folder were taking
   the BROWSER's default #0000EE, frozen: a value that belongs to no theme, no
   token and no file we own. On white it measures 8.4:1 and reads as an ordinary
   link, which is why it survived four steps of review. On the dark surface the
   same anchor measures 1.96:1 and the sentence loses its verb.

   `a:not([class])` and not `.kit-body a`. Every product specimen on these pages
   wears a class, so the negation cannot reach into a stage and repaint a `.row`,
   a `.btn` or a `.cut` no matter what the specificity works out to. It selects
   exactly the finding and nothing next to it.

   It reads --text-action, the system's own link ink, and NOT a --kn- chrome
   value: these links sit in the reading column, which is drawn with the product's
   reading roles throughout this file, and --text-action is measured in both
   themes (6.23:1 light, 6.82:1 dark). The chrome palette belongs to the panel.

   NOT IN base.css, and the isolation rule is why. The product carries zero
   classless anchors on all 28 coloured screens; this is the stand's own defect
   and bending the shipped package to cure it is the exact move that file forbids. */
.kit-body a:not([class]) { color: var(--text-action); }
.kit-body a:not([class]):hover { color: var(--text-primary); }

/* ---------- the panel ---------- */

.kit-panel {
  position: fixed; top: 0; left: 0; bottom: 0; width: 268px; z-index: 200;
  display: flex; flex-direction: column;
  background: var(--kn-surface); border-right: 1px solid var(--kn-border);
  transform: translateX(-100%); visibility: hidden;
  transition: transform 0.22s ease, visibility 0.22s;
}
.kit-panel.open { transform: translateX(0); visibility: visible; }
.kit-panel-head { padding: 16px 16px 12px; border-bottom: 1px solid var(--kn-border-light); }
.kit-panel-head .r1 { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.kit-badge {
  font-size: 9.5px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--kn-accent); background: var(--kn-accent-light);
  border-radius: 999px; padding: 3px 9px;
}
.kit-sub { margin: 6px 0 0; font-size: 12px; color: var(--kn-text-muted); }
.kit-panel-close, .kit-toggle {
  background: var(--kn-surface); border: 1px solid var(--kn-border); border-radius: var(--kn-radius);
  color: var(--kn-text); font: inherit; cursor: pointer;
}
.kit-panel-close { border: 0; background: none; font-size: 18px; line-height: 1; color: var(--kn-text-muted); padding: 4px 6px; }
.kit-toggle { position: fixed; top: 12px; left: 12px; z-index: 210; display: flex; align-items: center; gap: 8px; height: 36px; padding: 0 12px; font-size: 13px; }
.kit-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.25); z-index: 190; }
.kit-overlay.open { display: block; }

#kit-nav { flex: 1; overflow-y: auto; padding: 10px 10px 20px; }
.kn-group { margin: 0 0 2px; }
.kn-head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%;
  padding: 8px 10px; border: 0; background: none; border-radius: var(--kn-radius);
  font: inherit; font-size: 10.5px; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--kn-text-muted); cursor: pointer; text-align: left;
}
.kn-head:hover { background: var(--kn-border-light); }
.kn-head .kn-count { font-weight: 600; letter-spacing: 0; }
.kn-head .kn-caret { transition: transform 0.18s ease; }
.kn-group.is-open .kn-head .kn-caret { transform: rotate(90deg); }
.kn-list { list-style: none; margin: 0 0 8px; padding: 0; display: none; }
.kn-group.is-open .kn-list { display: block; }
.kn-list a, .kn-list .kn-pending {
  display: block; padding: 6px 10px 6px 20px; border-radius: var(--kn-radius);
  font-size: 13px; text-decoration: none; color: var(--kn-text-secondary);
}
.kn-list a:hover { background: var(--kn-border-light); color: var(--kn-text); }
.kn-list a.current { background: var(--kn-accent-light); color: var(--kn-accent); font-weight: 600; }
.kn-pending { color: var(--kn-text-muted); opacity: 0.6; cursor: default; }

/* ---- the page you are on, and the project it belongs to, 2026-08-13 --------
   Two blocks the panel did not have until the stand absorbed its own stage pages.
   `.kn-sections` is the list of sections of the current page, which the project
   roadmap used to show and this panel now shows in its place; `.kn-project` is the
   thirteen stages, at the foot, so the swap costs nothing. Both are quieter than
   the material list they stand with: they say where you are, not what you can
   build with. */
.kn-sections { list-style: none; margin: 2px 0 6px; padding: 0; }
.kn-section {
  display: block; padding: 4px 10px 4px 34px; border-radius: var(--kn-radius);
  font-size: 12px; text-decoration: none; color: var(--kn-text-muted);
}
.kn-section:hover { color: var(--kn-text); background: var(--kn-border-light); }
.kn-section.is-current { color: var(--kn-accent); }

.kn-project-head {
  display: block; padding: 0 10px 6px;
  font-size: 10px; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--kn-text-muted);
}
.kn-project-list { list-style: none; margin: 0; padding: 0; }
.kn-project { margin: 14px 0 0; padding: 12px 0 0; border-top: 1px solid var(--kn-border-light); }
.kn-project-link {
  display: block; padding: 5px 10px; border-radius: var(--kn-radius);
  font-size: 12.5px; text-decoration: none; color: var(--kn-text-secondary);
}
a.kn-project-link:hover { background: var(--kn-border-light); color: var(--kn-text); }
.kn-project-link.is-here { color: var(--kn-accent); font-weight: 600; background: var(--kn-accent-light); }
span.kn-project-link { color: var(--kn-text-muted); opacity: 0.6; }
.kn-project-soon {
  margin-left: 6px; font-size: 9.5px; font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--kn-text-muted);
}

.kit-panel-foot { border-top: 1px solid var(--kn-border-light); padding: 12px 10px 14px; }
.kit-panel-foot a {
  display: block; padding: 6px 10px; border-radius: var(--kn-radius);
  font-size: 12.5px; text-decoration: none; color: var(--kn-text-muted);
}
.kit-panel-foot a:hover { background: var(--kn-border-light); color: var(--kn-text); }

.kit-theme { display: flex; gap: 6px; margin: 10px 0 0; padding: 0 10px; }
.kit-theme button {
  flex: 1; min-height: 32px; border: 1px solid var(--kn-control-edge); background: none;
  border-radius: var(--kn-radius); font: inherit; font-size: 12px; color: var(--kn-text-secondary); cursor: pointer;
}
.kit-theme button[aria-pressed="true"] { background: var(--kn-accent-light); border-color: var(--kn-accent); color: var(--kn-accent); font-weight: 600; }

/* THE FLOATING TOGGLE SAT ON THE TITLE BELOW 1100, and it had since the panel was
   built. Measured on color.html at 360, 900 and 1000, all three identical: the
   toggle is a fixed box at 12,12 to 99,48 and the h1 starts at 28,40, so the button
   covered the first 8px of the heading and the first 71px of its line. It went
   unseen because the three pages the founder opens most carry an eyebrow above the
   h1, which pushes the title clear of it; every component page has none.

   THE PAGE MOVES DOWN AND THE BUTTON STAYS PUT. A static toggle in normal flow is
   the other answer, and it is the one the project chrome takes, but this one is
   worth keeping fixed: the panel is the navigation of the whole stand and a way to
   open it that scrolls away is a way to lose it. 64 is the toggle's own bottom
   edge, 48, plus 16 of air, so the number moves if the button ever does.

   Same subject and same guard as the padding-left below, `:has(aside.kit-panel)`,
   because the other chrome in this file puts its toggle in the flow and needs no
   room reserved for it. From 1100 the panel is docked, the toggle is gone and the
   40 comes back. */
.kit-body:has(aside.kit-panel) .kit-wrap { padding-top: 64px; }

@media (min-width: 1100px) {
  .kit-panel { transform: translateX(0); visibility: visible; }
  .kit-toggle, .kit-panel-close { display: none; }
  .kit-overlay { display: none !important; }
  /* THE PADDING FOLLOWS THE PANEL THAT IS ACTUALLY ON THE PAGE, and :has() is
     what makes that possible. Two different chromes live in this file and both
     are fixed to the left edge: this one, the component kit's panel at 268px
     docking at 1100, and `.sidebar`, the roadmap's at 220px docking at 840. No
     page carries both, and until 2026-08-11 neither rule knew that.
     `body.kit-body { padding-left: 220px }` from the roadmap block reached
     every component page, because its subject is the BODY and not the sidebar,
     and at 0-1-1 it also outranked the 0-1-0 written here. The result was
     invisible to every check that looked for horizontal scroll, because there
     was none: between 840 and 1099 a component page reserved 220px for a panel
     that was off-canvas, and from 1100 up the 268px panel covered the first
     48px of its own content. Found by the founder, on screen, at a width the
     sweep never ran. */
  body.kit-body:has(aside.kit-panel) { padding-left: 268px; }
  /* the toggle is gone up here, so the room reserved for it goes too */
  .kit-body:has(aside.kit-panel) .kit-wrap { padding-top: 40px; }
}

/* ---------- the page ---------- */

.kit-wrap { max-width: 1000px; margin: 0 auto; padding: 40px 28px 80px; }

.kit-head h1 { margin: 0 0 8px; font-size: 30px; font-weight: 800; letter-spacing: -0.02em; }
.kit-head .lead { margin: 0 0 18px; max-width: 66ch; font-size: 15px; color: var(--text-muted); }
.kit-meta { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 26px; }
.kit-meta span {
  font-size: 12px; color: var(--text-body); background: var(--bg-surface);
  border: 1px solid var(--line-container); border-radius: 999px; padding: 4px 11px;
}
.kit-meta span strong { color: var(--text-action); font-weight: 700; }

.kit-sec { margin: 36px 0 0; }
.kit-sec > h2 {
  margin: 0 0 4px; padding: 0 0 8px; border-bottom: 1px solid var(--line-container);
  font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-muted);
}
.kit-sec > .note { margin: 12px 0 18px; max-width: 74ch; font-size: 13.5px; color: var(--text-muted); }
.kit-sec > .note b { color: var(--text-primary); }
/* "updated after publishing". The project rule says a page that cannot hold new
   material must SAY so visibly rather than absorb it, so the amendment is marked
   at the paragraph and not folded into the argument it corrects. A rule of the
   stand, in the stand's own file. */
.kit-sec > .note.kit-updated {
  border-left: 2px solid var(--line-container); padding-left: 12px;
  background: var(--bg-recessed);
  padding-top: 8px; padding-bottom: 8px; padding-right: 12px; border-radius: var(--radius-xs);
}
.kit-sub-h { margin: 26px 0 10px; font-size: 13px; font-weight: 600; color: var(--text-primary); }

/* cards, for the hub */
.kit-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 12px; }
.kit-card { background: var(--bg-surface); border: 1px solid var(--line-container); border-radius: var(--radius-sm); padding: 14px; }
.kit-card.is-pending { background: var(--bg-recessed); border-style: dashed; }
.kit-card .r1 { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin: 0 0 6px; }
.kit-card .nm { font-size: 14.5px; font-weight: 600; color: var(--text-primary); text-decoration: none; }
.kit-card a.nm:hover { color: var(--text-action); }
.kit-card .cls { font-size: 11.5px; color: var(--text-muted); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.kit-card .ax { margin: 0; font-size: 12px; color: var(--text-muted); }
.kit-card .was { margin: 6px 0 0; font-size: 11.5px; color: var(--text-muted); }
.kit-card .was b { color: var(--text-body); font-weight: 600; }
.kit-card .cnt { flex: none; font-size: 11px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.kit-thumb { display: flex; align-items: center; gap: 4px; height: 34px; margin: 10px 0 0; overflow: hidden; }
.kit-thumb i { display: block; flex: 1; height: 100%; border-radius: 3px; }

/* tables */
.kit-tab { overflow-x: auto; margin: 0 0 20px; }
.kit-tab table { border-collapse: collapse; width: 100%; min-width: 600px; font-size: 13px; }
.kit-tab th, .kit-tab td { text-align: left; vertical-align: top; padding: 9px 14px 9px 0; border-bottom: 1px solid var(--line-divider); }
.kit-tab th { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); font-weight: 600; border-bottom-color: var(--line-container); }
.kit-tab td:last-child { color: var(--text-muted); }
.kit-tab code, .kit-code { font-size: 11.5px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text-body); }

/* ---- A PATH IS ONE WORD AND A PHONE IS 320 WIDE, 2026-08-18 ---------------
   Found by the stage 13 sweep, which is the first one to walk the pages that
   TALK about the product rather than the product: `width12.cjs` reports "no
   document scrolls sideways at any stop" over all 110 product screens, and it is
   true, and the 127 pages around them had never been swept at all.

   Six stand pages scrolled sideways at 320 for one reason: a `<code>` in a note,
   holding a path like `design/system/components/subscription-row.css`, is a
   single unbreakable token. Measured: 316px of code in a 264px column on
   `interruption`, 308 on `subscription-row`, 300 on `checkbox`, 292 on
   `eyebrow`, `readout` and `summary`.

   `anywhere` and not `break-word`: both break the token, and only `anywhere`
   also lets the browser count the broken form when it measures min-content, so
   the COLUMN stops being 316 wide in the first place. Scoped to the stand's own
   wrap, which is belt and braces - `_page.css` never loads on a product screen -
   and to every `code` inside it: a command block is `white-space: pre-wrap`
   already, so `anywhere` only ever fires there on a token that would otherwise
   leave the page, and the tables keep their own scrollers regardless. Written as
   one selector after the first cut scoped it to `p`, `li` and `td` and left the
   same path in a `footer` pushing `subscription-row` out by 4px. */
.kit-wrap code { overflow-wrap: anywhere; }
.kit-tab .n { font-variant-numeric: tabular-nums; white-space: nowrap; }
.kit-tab .ok { color: var(--text-action); font-weight: 600; }
.kit-tab .dec { color: var(--text-muted); }

/* colour: primitive tiles */
.kit-swatches { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
.kit-sw { border: 1px solid var(--line-container); border-radius: var(--radius-sm); overflow: hidden; background: var(--bg-surface); }
/* --line-control, not --line-container, and the reason is the same one the role
   chips carry below. This one line separates a swatch from its own caption plate
   across forty-three colours, two of which ARE the container hairline: measured
   on 2026-08-12, the --hairline-dark swatch drew rgb(42,53,56) on rgb(42,53,56),
   1.00:1, so the primitive that IS the divider had no divider. A frame around a
   specimen has to hold against every value it might be asked to frame, which is
   what makes it a control edge rather than a decorative one. */
.kit-sw i { display: block; height: 56px; border-bottom: 1px solid var(--line-control); }
.kit-sw b { display: block; padding: 8px 10px 2px; font-size: 12px; font-weight: 600; color: var(--text-primary); }
.kit-sw span { display: block; padding: 0 10px 9px; font-size: 11px; color: var(--text-muted); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

/* colour: a role shown in both themes side by side, whatever the page theme is */
.kit-roles { display: grid; grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); gap: 12px; }
.kit-role { border: 1px solid var(--line-container); border-radius: var(--radius-sm); padding: 12px; background: var(--bg-surface); }
.kit-role .rn { display: block; font-size: 12.5px; font-weight: 600; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text-primary); }
.kit-role .rs { display: block; margin: 2px 0 8px; font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-muted); }
.kit-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
/* A CHIP STANDS ON THE GROUND OF THE THEME IT DEMONSTRATES, and that is what the
   `background` line buys. Each chip pins `data-theme` on itself so the pair can be
   read side by side, which makes every var() inside it resolve in the pinned theme
   including this one: the light chip paints itself white, the dark chip paints
   itself #161f21, whatever the page around them is doing.

   Without it the chip was transparent and the specimens borrowed the PAGE's ground.
   Measured on 2026-08-12 with the page in dark: the light chip's `--text-primary`
   sample rendered #141b1d on #161f21 at 1.04:1, and its `--bg-surface` sample
   merged into the page at 1.000. The one page that exists to prove both halves of
   every pair showed only one of them, and only in the theme nobody opens it in.

   `color` AS WELL AS `background`, and that is the second half of the same lesson.
   Pinning `data-theme` changes what var() RESOLVES TO inside the chip; it does not
   change what `color` INHERITS, and inheritance walks the DOM, which runs out of
   the chip and into the page. A ground without an ink made it worse for one round:
   the label and the value went on inheriting the page's dark --text-primary and
   landed at 1.12:1 on the white they now stood on. A pinned subtree needs both
   halves stated or it is pinned in name only. */
.kit-chip { background: var(--bg-surface); color: var(--text-primary); border: 1px solid var(--line-divider); border-radius: var(--radius-xs); padding: 8px; font-size: 11px; }
.kit-chip .lbl { display: block; margin: 0 0 6px; font-size: 9.5px; letter-spacing: 0.08em; text-transform: uppercase; opacity: 0.7; }
.kit-chip .val { display: block; margin-top: 6px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10.5px; opacity: 0.75; }
.kit-chip .demo { display: flex; align-items: center; justify-content: center; height: 34px; border-radius: 4px; font-size: 12px; font-weight: 600; }

/* THE SWATCH IS FRAMED, and the two rules below fix two faults the chip's own
   ground uncovered on 2026-08-12.

   ONE, the fill swatches. Now that a chip paints itself, a swatch whose role IS
   the chip's own ground has nothing left to stand against: `f--bg-surface` inside
   the dark chip measured 1.000 against it. A specimen cannot prove a value by
   being invisible, so every fill and ink swatch gets an edge of its own. It reads
   --line-control and not --line-container because the frame has to hold against
   twenty-two different fills in two themes, and --line-control is the one line
   role the system owes 3:1 in both (3.46:1 light, 5.15:1 dark); the container
   hairline is declared decorative at 1.23:1 and would disappear behind half of
   them. The stand borrowing the system's strongest line role for a specimen frame
   is a line drawn as a line, which is what the surface axis asks.

   TWO, the line swatches, and this one was never a dark defect at all: it was
   simply never visible. `.b--line-control` and its seven siblings set
   `border-color` and nothing sets a width or a style, so the initial
   `border-style: none` stood and all eight LINE roles on this page have rendered
   as an empty box since the page was written. Measured, not read: border-top-width
   0px, border-top-style none, border-top-color rgb(123,141,145). The colour was
   there the whole time with nothing to paint. The width goes on the attribute
   selector so that `border-color` stays with the .b-- class it belongs to, which
   is why this rule names style and width and no colour. */
.kit-chip .demo:not([class*="b--"]) { border: 1px solid var(--line-control); }
.kit-chip .demo[class*="b--"] { border-style: solid; border-width: 3px; }
.kit-role .why { margin: 9px 0 0; font-size: 11.5px; color: var(--text-muted); }
.kit-role .why b { color: var(--text-body); font-weight: 600; }

/* typography: live specimens */
.kit-spec { padding: 14px 0; border-bottom: 1px solid var(--line-divider); }
.kit-spec:last-child { border-bottom: 0; }
.kit-spec .meta { display: flex; flex-wrap: wrap; gap: 10px; margin: 0 0 8px; font-size: 11px; color: var(--text-muted); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.kit-spec .sample { margin: 0; color: var(--text-primary); }

/* geometry: spacing and radius shown as themselves */
.kit-scale { display: flex; flex-direction: column; gap: 8px; }
.kit-bar { display: flex; align-items: center; gap: 12px; }
.kit-bar .val { flex: none; width: 62px; font-size: 11.5px; color: var(--text-muted); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.kit-bar i { display: block; height: 14px; border-radius: 2px; background: var(--bg-action); opacity: 0.7; }
.kit-bar .use { font-size: 11.5px; color: var(--text-muted); }
.kit-radii { display: flex; flex-wrap: wrap; gap: 14px; }
.kit-radii figure { margin: 0; text-align: center; }
.kit-radii i { display: block; width: 76px; height: 76px; background: var(--bg-selected); border: 1px solid var(--line-control); }
.kit-radii figcaption { margin: 6px 0 0; font-size: 11.5px; color: var(--text-muted); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

/* icons */
.kit-icons { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 10px; }
.kit-icon { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 14px 8px; border: 1px solid var(--line-container); border-radius: var(--radius-sm); background: var(--bg-surface); }
.kit-icon .glyph { display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; color: var(--text-body); }
.kit-icon .glyph img, .kit-icon .glyph svg { width: 100%; height: 100%; }
.kit-icon .nm { font-size: 11px; color: var(--text-muted); text-align: center; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.kit-icon .mask { width: 24px; height: 24px; background: currentColor; }

.kit-foot { margin: 44px 0 0; padding: 16px 0 0; border-top: 1px solid var(--line-container); font-size: 13px; color: var(--text-muted); }
.kit-foot a { color: var(--text-action); }

/* ---------- generated from design/system/tokens.css ----------
   One class per primitive and one per role, so a swatch can be painted from the
   stand stylesheet rather than from a style attribute on the tile. Regenerate these
   whenever tokens.css gains or loses a name; nothing else in this file is generated. */
.c--paper { background: var(--paper); }
.c--canvas { background: var(--canvas); }
.c--panel { background: var(--panel); }
.c--ink { background: var(--ink); }
.c--slate { background: var(--slate); }
.c--muted { background: var(--muted); }
.c--hairline { background: var(--hairline); }
.c--hairline-soft { background: var(--hairline-soft); }
.c--edge { background: var(--edge); }
.c--skeleton { background: var(--skeleton); }
.c--petrol { background: var(--petrol); }
.c--petrol-deep { background: var(--petrol-deep); }
.c--petrol-tint { background: var(--petrol-tint); }
.c--amber { background: var(--amber); }
.c--amber-wash { background: var(--amber-wash); }
.c--clay { background: var(--clay); }
.c--clay-wash { background: var(--clay-wash); }
.c--stone { background: var(--stone); }
.c--stone-ink { background: var(--stone-ink); }
.c--paper-dark { background: var(--paper-dark); }
.c--canvas-dark { background: var(--canvas-dark); }
.c--panel-dark { background: var(--panel-dark); }
.c--ink-dark { background: var(--ink-dark); }
.c--slate-dark { background: var(--slate-dark); }
.c--muted-dark { background: var(--muted-dark); }
.c--hairline-dark { background: var(--hairline-dark); }
.c--hairline-soft-dark { background: var(--hairline-soft-dark); }
/* Added 2026-08-12. `--hairline-strong-dark` was declared at the molecule round
   alongside --line-container-hover and neither this class nor its swatch on
   color.html followed, so one of the twenty-two dark primitives had no visible
   place and the page's own count said twenty-one. Found by counting the file
   against the page rather than by looking, which is the only way this class of
   gap is ever found: a swatch that is missing looks exactly like a palette that
   is complete. */
.c--edge-dark { background: var(--edge-dark); }
.c--skeleton-dark { background: var(--skeleton-dark); }
.c--petrol-dark { background: var(--petrol-dark); }
.c--petrol-deep-dark { background: var(--petrol-deep-dark); }
.c--petrol-tint-dark { background: var(--petrol-tint-dark); }
.c--amber-dark { background: var(--amber-dark); }
.c--amber-wash-dark { background: var(--amber-wash-dark); }
.c--clay-dark { background: var(--clay-dark); }
.c--clay-wash-dark { background: var(--clay-wash-dark); }
.c--stone-dark { background: var(--stone-dark); }
.c--stone-ink-dark { background: var(--stone-ink-dark); }
.f--bg-page { background: var(--bg-page); }
.t--bg-page { color: var(--bg-page); }
.b--bg-page { border-color: var(--bg-page); }
.f--bg-surface { background: var(--bg-surface); }
.t--bg-surface { color: var(--bg-surface); }
.b--bg-surface { border-color: var(--bg-surface); }
.f--bg-recessed { background: var(--bg-recessed); }
.t--bg-recessed { color: var(--bg-recessed); }
.b--bg-recessed { border-color: var(--bg-recessed); }
.f--bg-callout { background: var(--bg-callout); }
.t--bg-callout { color: var(--bg-callout); }
.b--bg-callout { border-color: var(--bg-callout); }
.f--bg-selected { background: var(--bg-selected); }
.t--bg-selected { color: var(--bg-selected); }
.b--bg-selected { border-color: var(--bg-selected); }
.f--bg-attention { background: var(--bg-attention); }
.t--bg-attention { color: var(--bg-attention); }
.b--bg-attention { border-color: var(--bg-attention); }
.f--bg-failure { background: var(--bg-failure); }
.t--bg-failure { color: var(--bg-failure); }
.b--bg-failure { border-color: var(--bg-failure); }
.f--bg-status { background: var(--bg-status); }
.t--bg-status { color: var(--bg-status); }
.b--bg-status { border-color: var(--bg-status); }
.t--bg-trial { color: var(--bg-trial); }
.b--bg-trial { border-color: var(--bg-trial); }
.f--bg-placeholder { background: var(--bg-placeholder); }
.f--plot-area { background: var(--plot-area); }
.f--plot-line { background: var(--plot-line); }
.f--bg-measure { background: var(--bg-measure); }
.t--bg-placeholder { color: var(--bg-placeholder); }
.b--bg-placeholder { border-color: var(--bg-placeholder); }
.f--bg-action { background: var(--bg-action); }
.t--bg-action { color: var(--bg-action); }
.b--bg-action { border-color: var(--bg-action); }
/* The three component tokens, added here 2026-08-13. They were declared in
   tokens.css during the component rounds and had no plate on this stand at all,
   which is how the role count on color.html could read 31 while the file held
   34. Their order mirrors tokens.css, between the action fill they were split
   from and its pressed state. */
.f--control-accent { background: var(--control-accent); }
.t--control-accent { color: var(--control-accent); }
.b--control-accent { border-color: var(--control-accent); }
.f--bg-chip-accent { background: var(--bg-chip-accent); }
.t--bg-chip-accent { color: var(--bg-chip-accent); }
.b--bg-chip-accent { border-color: var(--bg-chip-accent); }
.f--text-chip-accent { background: var(--text-chip-accent); }
.t--text-chip-accent { color: var(--text-chip-accent); }
.b--text-chip-accent { border-color: var(--text-chip-accent); }
.f--bg-action-strong { background: var(--bg-action-strong); }
.t--bg-action-strong { color: var(--bg-action-strong); }
.b--bg-action-strong { border-color: var(--bg-action-strong); }
.f--text-primary { background: var(--text-primary); }
.t--text-primary { color: var(--text-primary); }
.b--text-primary { border-color: var(--text-primary); }
.f--text-body { background: var(--text-body); }
.t--text-body { color: var(--text-body); }
.b--text-body { border-color: var(--text-body); }
.f--text-muted { background: var(--text-muted); }
.t--text-muted { color: var(--text-muted); }
.b--text-muted { border-color: var(--text-muted); }
.f--text-action { background: var(--text-action); }
.t--text-action { color: var(--text-action); }
.b--text-action { border-color: var(--text-action); }
.f--text-on-action { background: var(--text-on-action); }
.t--text-on-action { color: var(--text-on-action); }
.b--text-on-action { border-color: var(--text-on-action); }
.f--text-attention { background: var(--text-attention); }
.t--text-attention { color: var(--text-attention); }
.b--text-attention { border-color: var(--text-attention); }
.f--text-failure { background: var(--text-failure); }
.t--text-failure { color: var(--text-failure); }
.b--text-failure { border-color: var(--text-failure); }
.f--text-status { background: var(--text-status); }
.t--text-status { color: var(--text-status); }
.b--text-status { border-color: var(--text-status); }
.f--text-trial { background: var(--text-trial); }
.b--text-trial { border-color: var(--text-trial); }
.f--line-control { background: var(--line-control); }
.t--line-control { color: var(--line-control); }
.b--line-control { border-color: var(--line-control); }
.f--line-container { background: var(--line-container); }
.t--line-container { color: var(--line-container); }
.b--line-container { border-color: var(--line-container); }
.f--line-divider { background: var(--line-divider); }
.t--line-divider { color: var(--line-divider); }
.b--line-divider { border-color: var(--line-divider); }
.f--line-selected { background: var(--line-selected); }
.t--line-selected { color: var(--line-selected); }
.b--line-selected { border-color: var(--line-selected); }

/* The four roles and states the foundations page had to grow a swatch for. Two
   were declared after step 4 built this block (--line-failure at the atom round,
   --line-action-soft at the molecule round) and three had been declared since
   step 3 and were never drawn: --bg-hover, --line-control-hover, --color-focus.
   A token nobody can see on the foundations page is a token nobody can check. */
.b--line-failure { border-color: var(--line-failure); }
.b--line-action-soft { border-color: var(--line-action-soft); }
.f--bg-hover { background: var(--bg-hover); }
.b--line-control-hover { border-color: var(--line-control-hover); }
.b--color-focus { border-color: var(--color-focus); }

/* ---------- the five frozen half steps ----------
   What is left of the generated ladder. Twenty two of the twenty seven classes went
   when the eight step scale was adopted: the ladder they drew is now drawn by
   var(--type-*), which is the point of a scale.

   These five stay, and they stay LITERAL on purpose. They are the half steps the
   founder removed, and typography.html shows each one beside the step it folded
   into so a reader can see that 13.5 next to 14 is not a difference. A record of
   what a value WAS must not follow the token that replaced it, or the evidence
   quietly rewrites itself. */
.kit-spec .sample.sp-17 { font-size:14.5px; font-weight:600; }
.kit-spec .sample.sp-19 { font-size:13.5px; font-weight:400; }
.kit-spec .sample.sp-21 { font-size:12.5px; font-weight:400; }
.kit-spec .sample.sp-23 { font-size:11.5px; font-weight:400; }
.kit-spec .sample.sp-25 { font-size:10.5px; font-weight:600; line-height:1.6; }

/* ============================================================================
   THE REVIEWER CHROME, moved here at step 4 and not before.

   These rules lived in design/kit/kit.css beside the product, where the stage 07
   inventory counted them as an organism called "Review page frame" standing on 55
   pages. They travel to no build. Step 2 decided they leave the system; this is the
   move itself, and it was owed the moment the hub stopped loading kit.css: the hub
   went on declaring .sidebar and nothing styled it, so the roadmap rendered as a
   plain stack above the page. Documenting a move is not making one.

   kit.css keeps its copy until step 8, because the 28 coloured product screens still
   load it and still need this chrome. That file is deleted at step 8 and the
   duplicate dies with it, so the two copies never outlive the migration.

   Written against the stand's own --kn-* variables rather than the product's roles:
   chrome must not change when the product's palette does.
   ============================================================================ */

.sidebar {
  /* ONE NUMBER FOR THE DRAWER, and it is the roadmap's own. --nav-w lives in
     /_nav.css, which owns the roadmap's look project-wide, and it is 240px.
     This file used to hardcode 220 and that disagreement was 20px wide: the
     injected #sidebar takes `width: 100%` at an ID's specificity, so the drawer
     rendered 240 while the body reserved 220 and the panel covered the first
     20px of the page under it. /_nav.css already carried a comment about
     exactly those 20px, describing them as hosts built to 220 that a fixed 240
     overflowed, and the workaround was to cap the nav rather than agree on the
     number. Two owners for one measurement is the fault; the cap was the patch.
     The fallback is the old value, for any page that shows this drawer without
     loading /_nav.css. */
  position: fixed; top: 0; left: 0; bottom: 0; width: var(--nav-w, 220px); z-index: 200;
  overflow-y: auto; padding-bottom: 32px;
  background: var(--kn-surface); border-right: 1px solid var(--kn-border);
  /* visibility, not transform alone: off-canvas by transform leaves every chrome
     link in the tab order, so on a phone a keyboard user tabs through an invisible
     panel before reaching the page. visibility still animates when it is paired
     with a duration. */
  transform: translateX(-100%); visibility: hidden;
  transition: transform 0.22s ease, visibility 0.22s;
}
.sidebar.open { transform: translateX(0); visibility: visible; }
.sidebar-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.25); z-index: 150; }
.sidebar-overlay.open { display: block; }
.sidebar-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 16px 14px; border-bottom: 1px solid var(--kn-border-light);
}
.nav-brand { font-weight: 600; font-size: 13.5px; color: var(--kn-text); letter-spacing: -0.01em; }
.sidebar-close {
  background: none; border: none; font-size: 18px; line-height: 1;
  color: var(--kn-text-muted); cursor: pointer; padding: 4px 6px; border-radius: var(--kn-radius);
}
/* In normal flow above the page rather than floating over it. Floated, it covers
   something at every position that was tried. Hidden entirely from 840. */
.nav-toggle {
  position: static; display: flex; align-items: center; justify-content: flex-start;
  width: 100%; height: 40px; padding: 0 14px; z-index: 100;
  background: var(--kn-surface); border: 0; border-bottom: 1px solid var(--kn-border);
  border-radius: 0; box-shadow: none; cursor: pointer;
  color: var(--kn-text-secondary); font-size: 13px; text-align: left;
}
@media (min-width: 840px) {
  .sidebar { transform: translateX(0); visibility: visible; }
  .sidebar-close, .nav-toggle { display: none; }
  .sidebar-overlay { display: none !important; }
  body.kit-body:has(.sidebar) { padding-left: var(--nav-w, 220px); }
}
/* The hub carries BOTH panels, so its theme switch belongs to the roadmap panel and
   has to sit inside the drawer rather than stretching across the page. */
.sidebar .kit-theme { padding: 12px 12px 0; }

/* The roadmap panel in the dark theme.

   /_nav.css is a PROJECT-WIDE file: it draws the roadmap on every page of every
   stage and it has one palette, designed for a light ground. The stand is the only
   place in the project with a theme switch, so it is the stand's job to make the
   chrome survive it, not that file's. Forking a shared stylesheet for one page
   would be the wrong repair, and leaving it alone would leave the roadmap at
   roughly 1.5:1 on the dark ground: present, and unreadable.

   Only the four variables /_nav.css already reads are redefined. No selector of
   its own is overridden, so it stays the single owner of how the roadmap looks. */
[data-theme="dark"] .sidebar {
  --nav-fg: var(--kn-text);
  --nav-muted: var(--kn-text-muted);
  --nav-active: var(--kn-accent);
  --nav-rule: var(--kn-border);
  --nav-badge: var(--kn-accent);
}
[data-theme="dark"] .nav-badge-next { color: var(--kn-surface); }

/* ---------- the stage account on overview.html ----------
   The roadmap leads here, so this page answers "what did this stage do" before it
   answers "what is in the system". Long measure, dense prose, a step reference on
   every section: the reader is a person catching up, not a person browsing. */

.kit-eyebrow {
  margin: 0 0 10px; font-size: 12px; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-muted);
}
.kit-head h1.big { font-size: 44px; line-height: 1.04; letter-spacing: -0.03em; margin: 0 0 20px; }
.kit-prose { max-width: 68ch; }
.kit-prose p { margin: 0 0 18px; font-size: 15.5px; line-height: 1.62; color: var(--text-body); }
.kit-prose p.first { font-size: 17px; line-height: 1.55; }
.kit-prose b, .kit-prose strong { color: var(--text-primary); font-weight: 700; }
.kit-prose code { font-size: 14px; }

.kit-step { margin: 40px 0 0; padding: 26px 0 0; border-top: 1px solid var(--line-container); }
.kit-step > h2 {
  margin: 0 0 16px; font-size: 12px; font-weight: 700;
  letter-spacing: 0.09em; text-transform: uppercase; color: var(--text-muted);
  border: 0; padding: 0;
}
.kit-step > h2 .no { color: var(--text-action); }
.kit-step .kit-tab { margin: 20px 0; }

.kit-open { border-left: 3px solid var(--line-selected); padding: 2px 0 2px 16px; margin: 0 0 18px; max-width: 68ch; }
.kit-open p { margin: 0 0 6px; font-size: 14.5px; line-height: 1.55; color: var(--text-body); }
.kit-open b { color: var(--text-primary); }

/* ---------- a component page: the five blocks ----------
   Anatomy, variants and sizes, when to use, rule and anti-rule, states.
   The stand gives the component a scene to stand on and never a style: the
   component is drawn by its own file in design/system/components/. */

/* THE STAGE SHOWS A COMPONENT AT THE WIDTH THE PRODUCT GIVES IT, and that is a
   correction rather than a preference. Measured at a 1440 viewport on the real
   coloured screens: a subscription row renders at 425px on Home, a charge list
   at 428px on Subscription detail, a nav row at 620px on Settings, an attention
   wash at 490px on Home error. The stage was handing all of them 898px.

   At more than double its real width a row stops being a row: the merchant name
   and the amount sit 800px apart, four dates in a column read as a sparse table
   rather than as evidence, and a hover fill spans a distance the product never
   asks it to span. Every one of those looks like a dull component and is in fact
   a stand measuring wrong. This is the same class of error as the reading measure
   that came unbound past 900px on two accepted screens: nobody widens the window
   to check, so the mistake is quiet.

   --kit-measure carries the number. The default is the product's row column;
   .wide and .bleed are the two exceptions, each used where a component genuinely
   spans further, and each says so on its page.

   THE STAGE IS THE QUERY CONTAINER, AND ON A STAND THAT IS THE WHOLE POINT.

   Ten components in this system change FORM and not size at a container query:
   the tab bar, the app bar, the dialog sheet, the card, the grid, the chart
   placeholder and the shell itself at 760, the dashboard head, the category
   group and the shell again at 900, and the groups column set at 900 and again
   at 1340. base.css puts `container-type: inline-size` on `body`, which is right
   for the product and is not changed here: a screen is handed the window and
   asks the window its width.

   Its consequence on the STAND was missed. Until 2026-08-12 this rule
   deliberately declared no container so that `body` stayed the nearest one, and
   the comment that stood here argued the choice by name. So every specimen asked
   the BROWSER WINDOW its width instead of the box it was standing in, and at a
   1280 window every specimen rendered its desktop form however narrow its stage
   was. Measured on tab-bar.html before the change, at a 1280 window: the 944px
   stage rendered `position: static`, `flex-direction: column`, 898x208 with
   `border-right: 1px`, which is the rail and is correct; the 435px stage
   rendered the same rail at 389x208. A page built to show a component's two
   forms was showing one of them twice.

   The founder read that as three separate complaints in one sitting: "this does
   not look like a tab bar", "the logo jumps between the two specimens and the
   panel is very wide" on the app bar, and "boring" on the dialog sheet, which
   was its desktop card four times over with its mobile full-bleed form nowhere
   on the page.

   RE-POINTING THE QUERY AT THE STAGE IS NOT A SIDE EFFECT TO BE AVOIDED HERE, IT
   IS THE INSTRUMENT. The stage is the box the reviewer sizes, so the stage is
   the box the specimen has to ask. The same declaration on `.app` in the PRODUCT
   would still be wrong, for the reason app-shell.css and tab-bar.css both state
   at their own lines, and nothing in design/system/ moves for this.

   Measured after, same page, same window: the 435px stage flipped to
   `position: fixed`, `flex-direction: row`, 433x63 with `border-top: 1px` and a
   --bg-surface ground, which is the real phone bottom bar.

   `contain: layout` IS STILL NEEDED AND `container-type` DOES NOT REPLACE IT,
   and that was measured rather than read off a spec. With `container-type:
   inline-size` alone the 435px stage fired the query correctly and the fixed bar
   still landed at x 0, width 1280, at the foot of the browser window: in
   Chromium today the query container and the containing block for a fixed
   descendant are two different things, and only `contain: layout` gives the
   second. With both declared the same bar measured x 318, width 433, inside its
   own stage. Neither declaration is decoration and neither covers for the other.

   ON THE BASE RULE AND NOT ON A MODIFIER. `.kit-stage.pinned` carried
   `contain: layout` from an earlier round and was never written onto a single
   stage: 63 pages, zero uses, and tab-bar.html carried a paragraph asking for
   the class rather than using it. A stand has no case where a specimen SHOULD
   escape to the window, so the containing block belongs to every stage, and the
   modifier keeps only the part that is a choice, which is the room. */
.kit-stage {
  --kit-measure: 440px;
  container-type: inline-size;
  contain: layout;
  display: flex; flex-wrap: wrap; align-items: center; gap: 16px;
  padding: 28px 22px; margin: 0 0 14px;
  background: var(--bg-surface); border: 1px solid var(--line-container); border-radius: var(--radius);
}
.kit-stage.wide  { --kit-measure: 620px; }   /* nav rows, forms, plan cards */
.kit-stage.bleed { --kit-measure: none; }    /* bands that really do span: a shell, a rail */

/* THE RAIL IS 220px IN THE PRODUCT AND THE STAGE HAS TO SAY SO.

   app-shell.css gives the desktop shell `grid-template-columns: 220px minmax(0,
   1fr)`, so the app bar and the tab bar in their rail form are 220px wide on
   every coloured screen. The stand has no shell grid, so the rail simply took
   the stage: measured at 898px on tab-bar.html and 898px on app-bar.html at a
   1280 window. That is the founder's "the panel is very wide", and it is also
   why the app bar's wordmark sat in a different place in each of its two
   specimens: at 898px the column had room the product never gives it.

   IT WAS 240 UNTIL 2026-08-12 AND IT IS MIRRORED, NOT DECIDED, HERE. The product
   narrowed the track on the founder's word and this line follows it, because the
   whole job of this rule is that the specimen is the width the product gives the
   component. Two owners for one measurement is the fault the drawer above already
   records; this one has a single owner, app-shell.css, and the stand reads it. A
   number that disagrees with the product turns the stand into a nice drawing.

   THE BOUND GOES ON THE CHILD AND THE STAGE STAYS WIDE, and the order matters
   now that the stage is the query container: narrowing the stage to 220 would
   drop the container under 760 and the component would render its MOBILE form
   instead. Wide stage, narrow specimen, and the query still fires.

   Inside the 760 query because the same stage renders the mobile form at a phone
   width, where both chromes span the full width. A 220px cap centred in the box
   down there would be a third form the product never draws. It rides
   --kit-measure rather than writing its own max-width, so a stage still has one
   lever for how wide its specimen is allowed to be. */
@container (min-width: 760px) {
  .kit-stage.rail.full > * { --kit-measure: 220px; }
}

/* THE STAND'S SCOPE IS NOT THE SHELL, and from this round it has to say so.

   `.app` is two things at once: the class every product screen carries as its
   scope, and the App shell ORGANISM. The stand puts the scope on the stage
   element itself, `class="kit-stage app"`, on 403 stages across 50 pages. The
   moment app-shell.css is imported, every one of them inherits the shell's
   `min-height: 100dvh`, its column flow and, past container 760, `height: 100dvh`.
   Measured before this line: button.html's first stage 900px tall at 1280 by 900
   with its specimens stacked in a column instead of a row, subscription-row.html
   900 against a correct 174, divided-list.html 900 against 330.

   This is the `.empty` collision again, exactly: a stand class and a product
   class sharing a name, harmless until the product class is written. That one
   was renamed ahead of this round and this one was not checked, because I looked
   for cascade collisions and a NAME is the thing to look for. */
.kit-stage.app { flex-direction: row; min-height: 0; height: auto; }

/* A STAGE THAT HOLDS A PINNED SPECIMEN NEEDS ROOM, and giving it room is all
   this modifier does now. A fixed child leaves the flow, so its stage collapses
   onto its own padding: measured at 58px on tab-bar.html with a 63px bar in it,
   which left the bar 7px proud of the box that is supposed to be holding it.
   Three --row floors is 168px, so the bar gets its own 56px row and two more
   above it and the stage reads as the foot of a screen, which is the only place
   this form of the component ever stands. --row is the product's row floor and
   the tab bar's own minimum, so the height is the component's number rather than
   the stand's taste.

   IT IS WRITTEN AFTER `.kit-stage.app` AND NOT BEFORE IT, and that is not
   tidiness. Both selectors are 0-2-0, `.kit-stage.app` sets `min-height: 0` to
   undo the shell's `100dvh`, and source order is the only thing separating them.
   Written above it the modifier lost and measured 58px, which is the exact bug
   it exists to fix. */
.kit-stage.pinned { min-height: calc(var(--row) * 3); }

/* A STAGE THAT HAS TO CROSS THE PRODUCT'S SECOND STEP, 900.

   Now that the stage is the query container, a query only fires if the STAGE is
   wide enough, and the stand's widest stage is not. Measured across all 63 pages
   at a 1280 window, the stage inner widths this stand produces are 228, 258,
   389, 578, 882 and 898. 760 is crossed on every page that has stages, so the
   six components that change form there can show both. 900 is crossed nowhere,
   by two pixels.

   The cost was real and it was measured: dashboard-head.html lost its two-track
   head, 364x493 becoming one 898px block, and groups-column-set.html lost its
   two columns, 425 each becoming one 898px column. Both had been rendering their
   wide form only because they were borrowing the window, which is the same
   borrowing that made every narrow stage lie.

   The room comes out of the stage's own side padding, 22px becoming 8px, which
   takes the inner width from 898 to 926. The column itself is not widened: at a
   1280 window the wrap already has 1000px of the 1012px the body gives it beside
   the panel, so widening it would move the reading measure of all 63 pages to
   buy 12px, and this buys 28px without touching a page that does not ask.

   THE 26px OF HEADROOM IS LOAD-BEARING, not slack. A classic 15px scrollbar
   takes the body to 1265 and the inner width to 923, still over. At the old 22px
   padding the same window would have measured 895 and the step would have fired
   on one machine and not on another, which is the quietest possible defect.

   1340 IS OUT OF REACH AND IS NOT REACHED HERE. The wrap is capped at 1000, so
   the widest stage this stand can build is 926 at any window size, and the
   groups column set's three-column form cannot be shown on its page. Before this
   round it appeared at a window of about 1608 and wider, which is outside the
   two widths this stage accepts at. Reaching it would need a stage allowed to
   leave the reading column entirely, and that is a decision about the stand's
   layout rather than a modifier. Reported, not smuggled in. */
.kit-stage.past-900 { padding-inline: 8px; }

/* The four destination squares in a line on the icons page. A stand class and
   not a product one: nothing in the product ever puts the four side by side
   outside the tab bar, and the tab bar is an organism that draws its own row. */
.kit-icrow { display: flex; align-items: center; gap: 28px; color: var(--text-muted); }    /* bands that really do span: the trust line, a screen shell */

/* The bound goes on the CHILD and not on the stage, so the stage keeps drawing
   the full-width surface a component stands on while the component itself takes
   the product's measure. Centred, because a specimen off to one side reads as a
   layout bug rather than as a measurement. */
.kit-stage.full > * { max-width: var(--kit-measure); margin-inline: auto; }

/* A component's own bottom margin belongs to its place in a screen's flow, not
   to the component: standing alone in a stage it renders as dead air under the
   specimen, on top of the stage's own 28px. Suppressed on the last child only,
   so a stage holding two specimens still shows the gap BETWEEN them, which is
   real. The callouts still name the margin where it matters. */
.kit-stage > :last-child { margin-bottom: 0; }
.kit-stage.on-page { background: var(--bg-page); }
.kit-stage.on-accent { background: var(--bg-action); }
.kit-callouts { list-style: none; margin: 0 0 8px; padding: 0; display: grid; gap: 8px; }
.kit-callouts li { display: grid; grid-template-columns: 128px 1fr; gap: 12px; align-items: baseline; font-size: 13px; color: var(--text-muted); }
.kit-callouts code { color: var(--text-action); }
/* A CALLOUT ANCHOR THAT IS NOT A SELECTOR, 2026-08-13. Four of the 257 callouts on
   this stand name a dimension ("26 by 26"), a part with no class of its own ("the
   sentence") or an absence ("no atoms"), and all four were bare text in the anchor
   column. Two things missed them: the petrol above, and `.kit-callouts li > *`
   below, which cannot reach a text node, so a long anchor could push the 128px
   column off a narrow screen. Declared once here rather than repaired four times in
   the markup, and deliberately NOT by wrapping them in `code`: marking prose as
   code would be a second untruth put in to hide the first. */
.kit-callouts .kit-part { color: var(--text-action); }

.kit-matrix { overflow-x: auto; margin: 0 0 8px; }
.kit-matrix table { border-collapse: collapse; width: 100%; min-width: 640px; }
.kit-matrix th, .kit-matrix td { text-align: left; vertical-align: top; padding: 14px 16px 14px 0; border-bottom: 1px solid var(--line-divider); }
.kit-matrix th { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); font-weight: 600; border-bottom-color: var(--line-container); }
/* The second line under a cell's subject: the axis rule in a variants matrix,
   the data line under an attribute on why.html. One declaration serving both
   tables, generalised rather than copied: the two are the same object in two
   containers, and a second copy would be a second value to keep in step. */
.kit-matrix .axis-rule, .kit-tab .axis-rule { margin: 4px 0 0; font-size: 12px; color: var(--text-muted); font-weight: 400; text-transform: none; letter-spacing: 0; }
.kit-matrix td.val { font-size: 12.5px; color: var(--text-muted); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; white-space: nowrap; }
/* RENAMED FROM .empty, 2026-08-11. The Empty block organism is registered on the
   class `.empty`, and at the time of the rename it was not built yet; it is now.
   The collision stayed harmless, because no stand cell sits inside an `.app` scope,
   but "the code and the stand do not mix" is
   a rule about NAMES and not only about cascade: the moment the organism lands, a
   product rule starts matching a reviewer chrome cell and nothing errors. Removed
   before the organism round rather than debugged during it. */
.kit-matrix td.kit-empty { color: var(--text-muted); font-style: italic; }

.kit-states { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 14px; }
.kit-state { border: 1px solid var(--line-container); border-radius: var(--radius-sm); overflow: hidden; background: var(--bg-surface); }
.kit-state .cap { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; padding: 9px 12px; border-bottom: 1px solid var(--line-divider); }
.kit-state .cap b { font-size: 12.5px; color: var(--text-primary); }
.kit-state .cap span { font-size: 10.5px; color: var(--text-muted); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.kit-state .live { display: flex; align-items: center; justify-content: center; gap: 10px; padding: 20px 14px; }
.kit-state img { display: block; width: 100%; }
.kit-state .tok { padding: 8px 12px 11px; font-size: 11.5px; color: var(--text-muted); }
.kit-state .tok code { color: var(--text-action); }

/* THE COPYABLE MARKUP BLOCK, and `white-space: pre` is not a nicety here.
   Without it every multi-line sample on every component page collapsed its
   newlines and indentation into one run-on line: the block that exists so a
   person can copy the markup was showing markup nobody could read, on 55 pages,
   since the day the stand was built. Found on 2026-08-12 by an agent measuring
   something else. `pre` and not `pre-wrap`, because `overflow-x: auto` is
   already declared and a sample that wraps mid-attribute is the same defect
   with softer edges. */
.kit-cmd { display: block; padding: 10px 12px; margin: 12px 0 0; font-size: 11.5px;
  background: var(--bg-recessed); border: 1px solid var(--line-divider); border-radius: var(--radius-xs);
  color: var(--text-muted); font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: pre; overflow-x: auto; }

.kit-do { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 14px; margin: 0 0 8px; }
.kit-do > div { border: 1px solid var(--line-container); border-radius: var(--radius-sm); padding: 14px; background: var(--bg-surface); }
.kit-do .hd { display: block; margin: 0 0 10px; font-size: 11px; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; }
.kit-do .yes .hd { color: var(--text-action); }
.kit-do .no .hd { color: var(--text-failure); }
.kit-do p { margin: 10px 0 0; font-size: 13px; color: var(--text-muted); }

/* A stage that gives its content the full column.

   .kit-stage is a content-width flex row, which is right for a button and wrong
   for a form control: an input declares width:100% and renders at its ~180px
   intrinsic width inside a shrink-to-fit row, so the stand would show it narrower
   than any screen ever does. Reported by the atom round rather than worked around
   on the page, which is the correct order: a missing stand class is the stand's
   problem and never the component's. */
.kit-stage.full { display: block; }
.kit-stage.full > * { width: 100%; }
.kit-stage.col { flex-direction: column; align-items: stretch; }

/* The callout list at a narrow width.

   `.kit-callouts li` is a two column grid, 128px and the rest. A grid item's
   default min-width is auto, so a long label refuses to shrink below its content
   and pushes the row past the viewport: the chart placeholder page overflowed by
   26px at 360 and nothing in its markup was wrong. Two fixes, both here in the
   stand's own stylesheet: let the second column shrink, and drop to one column
   where 128px plus a sentence stops fitting. */
.kit-callouts li > * { min-width: 0; overflow-wrap: anywhere; }
@media (max-width: 560px) {
  .kit-callouts li { grid-template-columns: 1fr; gap: 2px; }
}

/* ============================================================================
   icons.html: the module grid, the anatomy cell and the measured audit.

   Everything below is prefixed .kit-icg. It draws ONE thing the rest of the
   stand cannot draw: a glyph standing on its own 24 by 24 module grid, with the
   safe area and the measured painted bounds laid over it, so a rule about the
   set can be read off the picture instead of taken on trust.

   Colour comes from the product's semantic roles rather than the stand's --kn-*
   set, because these marks ARE the product and the diagram has to survive the
   theme switch the same way they do. Geometry stays on the markup as SVG
   presentation attributes (stroke-width, d, transform), which is how every other
   inline mark on that page is already written; only the paint is class-driven,
   so a role rename reaches the diagram.
   ============================================================================ */

/* the anatomy block: the drawn cell beside the numbered rules */
.kit-icg-lay {
  display: grid; grid-template-columns: minmax(0, 320px) minmax(0, 1fr);
  gap: 24px; align-items: start; margin: 0 0 20px;
}
@media (max-width: 720px) { .kit-icg-lay { grid-template-columns: 1fr; } }

/* the cell itself. width:100% with height:auto, so it shrinks with the column
   and never becomes the reason the page scrolls sideways at 360. */
.kit-icg {
  display: block; width: 100%; height: auto;
  background: var(--bg-surface);
  border: 1px solid var(--line-container); border-radius: var(--radius-sm);
}
.kit-icg .mod   { fill: none; stroke: var(--line-divider); }
.kit-icg .cell  { fill: none; stroke: var(--line-container); }
.kit-icg .safe  { fill: none; stroke: var(--text-action); }
.kit-icg .box   { fill: none; stroke: var(--text-muted); }
.kit-icg .paint { fill: none; stroke: var(--text-body); }
/* the centreline, drawn in the surface colour ON TOP of the paint: the hairline
   cut through the middle of a stroke is the whole of rule 3 in one picture. */
.kit-icg .curve { fill: none; stroke: var(--bg-surface); }
.kit-icg .node  { fill: var(--text-action); stroke: none; }
/* one shape in the set is a FILL rather than a stroke (the dot of the unmatched
   merchant mark), and it sits inside a group the rules above have set to
   fill:none, so it needs to say so on itself. */
.kit-icg .kit-icg-solid { fill: var(--text-body); stroke: none; }

.kit-icg-key { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 8px 0 0; font-size: 11.5px; color: var(--text-muted); }
.kit-icg-key span { display: inline-flex; align-items: center; gap: 6px; }
.kit-icg-key i { display: block; width: 14px; height: 0; border-top: 2px solid currentColor; }
.kit-icg-key .k-safe  { color: var(--text-action); }
.kit-icg-key .k-box   { color: var(--text-muted); }
.kit-icg-key .k-paint { color: var(--text-body); }

.kit-icg-cap { margin: 8px 0 0; font-size: 11.5px; line-height: 1.55; color: var(--text-muted); }
.kit-icg-cap b { color: var(--text-body); font-weight: 600; }

.kit-icg-rules { margin: 0; padding: 0 0 0 22px; display: grid; gap: 14px; }
.kit-icg-rules li { font-size: 13.5px; line-height: 1.55; color: var(--text-muted); overflow-wrap: anywhere; }
.kit-icg-rules li b { color: var(--text-primary); }
.kit-icg-rules li code { color: var(--text-action); }

/* the measured set: one figure per glyph, the numbers under the picture */
.kit-icg-set { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 14px; margin: 0 0 18px; }
/* On a phone auto-fill drops to ONE column, and one cell per row turns a
   comparison into a slideshow: the whole point of this block is that the five
   glyphs are seen next to each other. Two forced columns at 137px still render
   the module grid and the two rectangles legibly, and they keep the section on
   one screen and a half instead of five. */
@media (max-width: 560px) { .kit-icg-set { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; } }
.kit-icg-fig { margin: 0; min-width: 0; }
.kit-icg-fig figcaption { margin: 8px 0 0; font-size: 11px; line-height: 1.55; color: var(--text-muted); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.kit-icg-fig figcaption b { display: block; margin: 0 0 2px; font-size: 11.5px; font-weight: 600; color: var(--text-primary); }
.kit-icg-fig figcaption em { display: block; margin: 2px 0 0; font-style: normal; color: var(--text-attention); }

/* ink weight, as a bar per glyph */
.kit-icg-bar { display: grid; grid-template-columns: 104px minmax(0, 1fr) 128px; gap: 12px; align-items: center; margin: 0 0 8px; font-size: 12px; }
.kit-icg-bar .nm { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text-body); }
.kit-icg-bar .tr { height: 12px; border-radius: 2px; background: var(--bg-recessed); }
.kit-icg-bar .tr i { display: block; height: 100%; border-radius: 2px; background: var(--bg-action); }
.kit-icg-bar .v { text-align: right; color: var(--text-muted); font-variant-numeric: tabular-nums; }
@media (max-width: 480px) {
  .kit-icg-bar { grid-template-columns: 1fr; gap: 3px; margin: 0 0 12px; }
  .kit-icg-bar .v { text-align: left; }
}
/* One class per measured value, exactly as the type ladder above does it: the
   number is a MEASUREMENT and it belongs in the stylesheet with its arithmetic
   beside it, not in a style attribute on the markup. Ink coverage of the 24x24
   cell, normalised to the heaviest glyph in the set. RE-MEASURED 2026-08-21, when
   the set changed under it twice in three days: .ic-trends joined on the 18th and
   .ic-save left with its destination on the 21st, so the heaviest is now
   .ic-home at 21.12%. Method, named because the old numbers did not carry one:
   each mask rasterised at 40 samples per module of the 24 cell and every subpixel
   with alpha over 10 counted. It reproduces the retired figures within 0.2 of a
   point, so the two runs are the same measurement and not two opinions.

   RE-NORMALISED AGAIN 2026-08-21, when the founder chose candidate B and the chart
   glyph was redrawn: `.ic-trends` gave up its frame for an axis corner and went
   23.82 to 14.71, which moved the heaviest mark in the set from the chart to the
   house and took the spread from 81 per cent to 61. */
.kit-icg-bar i.g-home   { width: 100%; }   /* 21.12 / 21.12 */
.kit-icg-bar i.g-alerts { width: 74.0%; }  /* 15.62 / 21.12 */
.kit-icg-bar i.g-trends { width: 69.7%; }  /* 14.71 / 21.12 */
.kit-icg-bar i.g-you    { width: 62.2%; }  /* 13.14 / 21.12 */

/* ---------- the brand plate, why.html ----------
   One wide image with its reading under it. It is a RECORD of the locked
   language and not a specimen of it, so it is shown as a figure with a border
   and a caption rather than as a hero: a picture that fills the top of a page
   reads as the source of the values, and the source is tokens.css. */
.kit-plate { margin: 0 0 10px; border: 1px solid var(--line-container); border-radius: var(--radius-sm); overflow: hidden; background: var(--bg-surface); }
.kit-plate img { display: block; width: 100%; }
.kit-plate figcaption { padding: 12px 14px; border-top: 1px solid var(--line-divider); font-size: 12.5px; line-height: 1.55; color: var(--text-muted); }
.kit-plate figcaption b { color: var(--text-primary); font-weight: 600; }

/* the carried finding: a closed defect, stated once, not a running list */
.kit-icg-carry { border-left: 3px solid var(--line-selected); padding: 2px 0 2px 16px; margin: 0 0 18px; max-width: 74ch; }
.kit-icg-carry p { margin: 0 0 8px; font-size: 13.5px; line-height: 1.55; color: var(--text-muted); }
.kit-icg-carry p:last-child { margin-bottom: 0; }
.kit-icg-carry b { color: var(--text-primary); }

/* ---------- the type ladder, driven by the tokens themselves ----------
   The eight specimens on typography.html were rendering through the generated
   .sp-N classes, which carry the size as a literal. That page documents the
   scale, so a page whose ladder does not READ the scale can quietly disagree
   with it: change --type-body in tokens.css and the ladder would go on showing
   14px forever. Same principle as a component page loading the component's own
   css rather than a copy of it. */
.kit-spec .sample.sp-display { font-size: var(--type-display); font-weight: 700; line-height: 1.02; letter-spacing: -0.025em; }
.kit-spec .sample.sp-figure  { font-size: var(--type-figure);  font-weight: 700; line-height: 1.08; letter-spacing: -0.02em; }
.kit-spec .sample.sp-head    { font-size: var(--type-head);    font-weight: 700; line-height: 1.18; letter-spacing: -0.015em; }
.kit-spec .sample.sp-title   { font-size: var(--type-title);   font-weight: 700; line-height: 1.2; }
.kit-spec .sample.sp-sub     { font-size: var(--type-sub);     font-weight: 600; line-height: 1.3; }
.kit-spec .sample.sp-body    { font-size: var(--type-body);    font-weight: 400; line-height: 1.5; }
.kit-spec .sample.sp-meta    { font-size: var(--type-meta);    font-weight: 400; line-height: 1.5; }
.kit-spec .sample.sp-label   { font-size: var(--type-label);   font-weight: 600; line-height: 1.4; }

/* ---------- the width demos, stage 10 --------------------------------------
   THE STAND'S OWN, AND DELIBERATELY NOT THE PRODUCT'S. These classes exist so
   that the three methods can be DRAGGED on responsive.html instead of described
   in a paragraph, which is the difference between a page that documents the
   adaptation and a page that asserts it. They ship nowhere: _page.css is the
   stand, and the census in docs/responsive.md lists the stand as a corpus whose
   width rules stay put.

   The two thresholds used below are the registry's own, --bp-tablet and
   --bp-desktop, written as the literals the query needs. A demo that switched at
   some other number would teach the wrong thing on the one page whose job is to
   teach the register. */

/* the draggable place. `resize` needs a non-visible overflow to appear at all,
   and `container-type: inline-size` is what makes the children below query the
   BOX rather than the window: this is the same declaration a pattern makes when
   it puts a component somewhere, said out loud on a page where it can be seen. */
.kit-rsp-live {
  container-type: inline-size;
  resize: horizontal;
  overflow: auto;
  min-width: 240px;
  max-width: 100%;
  width: 100%;
  padding: 16px;
  border: 1px dashed var(--line-container-hover);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  margin: 0 0 8px;
}
.kit-rsp-hint { margin: 0 0 18px; font-size: 12.5px; color: var(--text-muted); }
.kit-rsp-hint b { color: var(--text-primary); font-weight: 600; }
.kit-rsp-hint .w { font-variant-numeric: tabular-nums; }

/* 1. FLUID. No query anywhere in this rule: the count comes off the floor. */
.kit-rsp-fluid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--grid-col-min), 1fr));
  gap: var(--grid-gap);
}
.kit-rsp-fluid span {
  min-height: 56px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--line-container);
  border-radius: var(--radius-sm);
  background: var(--bg-recessed);
  font-size: 12.5px; color: var(--text-muted);
}

/* 2. CONTAINER. The same paragraph twice, once inside the measure and once not. */
.kit-rsp-measure p { margin: 0 0 6px; font-size: 14px; line-height: 1.5; color: var(--text-body); }
.kit-rsp-measure.held p { max-width: var(--container-text); }
.kit-rsp-measure .tag { display: block; margin: 0 0 4px; font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-muted); }
.kit-rsp-measure + .kit-rsp-measure { margin-top: 18px; }

/* 3. POINT. One named change of behaviour, and it reads the place, not the
   window, so the same block can be dragged narrow inside a wide monitor. */
.kit-rsp-point {
  display: flex; flex-direction: column; gap: 8px;
}
.kit-rsp-point .slot {
  padding: 12px 14px;
  border: 1px solid var(--line-container);
  border-radius: var(--radius-sm);
  background: var(--bg-recessed);
  font-size: 12.5px; color: var(--text-muted);
}
.kit-rsp-point .slot b { color: var(--text-primary); font-weight: 600; }
@container (min-width: 47.5rem) {
  .kit-rsp-point { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 8px; }
}

/* the band readout: which side of each point the WINDOW is on right now. Three
   spans, one visible at a time, so the page answers the question it is about. */
.kit-rsp-band { display: inline-flex; gap: 6px; align-items: center; font-size: 12.5px; color: var(--text-muted); }
.kit-rsp-band i { font-style: normal; padding: 2px 8px; border-radius: 999px; background: var(--bg-recessed); color: var(--text-muted); border: 1px solid var(--line-container); }
.kit-rsp-band i.on { background: var(--bg-selected); border-color: var(--line-selected); color: var(--text-action); font-weight: 600; }
.kit-rsp-band i[data-band="base"] { background: var(--bg-selected); border-color: var(--line-selected); color: var(--text-action); font-weight: 600; }
@media (min-width: 47.5rem) {
  .kit-rsp-band i[data-band="base"] { background: var(--bg-recessed); border-color: var(--line-container); color: var(--text-muted); font-weight: 400; }
  .kit-rsp-band i[data-band="tablet"] { background: var(--bg-selected); border-color: var(--line-selected); color: var(--text-action); font-weight: 600; }
}
@media (min-width: 56.25rem) {
  .kit-rsp-band i[data-band="tablet"] { background: var(--bg-recessed); border-color: var(--line-container); color: var(--text-muted); font-weight: 400; }
  .kit-rsp-band i[data-band="desktop"] { background: var(--bg-selected); border-color: var(--line-selected); color: var(--text-action); font-weight: 600; }
}

/* a section that is honestly not written yet, rather than an empty div */
.kit-rsp-todo {
  border: 1px dashed var(--line-container-hover);
  border-radius: var(--radius-sm);
  padding: 14px 16px;
  font-size: 13.5px; line-height: 1.55; color: var(--text-muted);
  background: var(--bg-recessed);
}
.kit-rsp-todo b { color: var(--text-primary); font-weight: 600; }

/* ---------- the shell shown in both forms, stage 10 step 3 ------------------
   TWO DECLARATIONS DO THE WORK HERE AND THEY ARE NOT THE SAME ONE.

   `container-type: inline-size` is what makes the box a QUERY TARGET, so the
   product's own `@container (min-width: 47.5rem)` reads the specimen instead of
   the window and the two forms can stand on one page.

   `contain: layout` is what makes the box a CONTAINING BLOCK for the narrow
   form's `position: fixed` tab bar, so the bar sits on the foot of the SPECIMEN
   rather than on the foot of the browser window. It was written here after
   measuring rather than before: `container-type` alone did not do it. With the
   container declared and nothing else, the bar computed 1425px wide and pinned
   itself to the bottom of the viewport, straight across the page. `contain:
   layout`, `transform` and `filter` all repair it and all three were tried;
   `contain: layout` is kept because it is the one that says what is meant.

   That is the gap tab-bar.html reported in its own anatomy note, "the stand has
   no class for holding a fixed specimen inside a stage". It is closed here for
   the shell and deliberately NOT retrofitted onto `.kit-stage.pinned`: a
   container on the generic stage would re-point every product query inside every
   component page at the stage's box, and half the specimens would quietly start
   rendering in their wide form. The tab bar's own page keeps the open note.

   THE PAIR STACKS RATHER THAN SITTING SIDE BY SIDE, and the arithmetic is the
   reason. The stand's content column is about 944px, so a 320px specimen beside
   its pair leaves the second one 604px, which is under the tablet point: the
   "wide" form would have rendered narrow and the page would have shown the same
   thing twice while claiming to show two. Measured before the layout was chosen.

   Everything else is the REAL shell: the same markup a screen carries and the
   same rules out of design/system/. The only thing the stand adds is a height,
   because `.app` asks for the whole viewport in both forms and a specimen has to
   fit on a page. `_screen.css` already does exactly this for the reviewed
   screens, and for the same reason. */
.kit-shell-pair { margin: 0 0 10px; }
.kit-shell-demo {
  container-type: inline-size;
  contain: layout;
  overflow: hidden;
  position: relative;
  height: 420px;
  margin: 0 0 10px;
  border: 1px solid var(--line-container);
  border-radius: var(--radius-sm);
  background: var(--bg-page);
}
.kit-shell-demo.narrow { width: 320px; max-width: 100%; }
.kit-shell-demo.wide   { width: 100%; }
.kit-shell-demo .app { min-height: 100%; height: 100%; }
.kit-shell-demo .app > .screen { overflow: auto; }
.kit-shell-cap { margin: 0 0 18px; font-size: 12.5px; color: var(--text-muted); }
.kit-shell-cap b { color: var(--text-primary); font-weight: 600; }

/* the filler inside a specimen: enough shape to show where the content sits,
   and no product copy, because this is the shell and not a screen */
.kit-shell-demo .kit-shell-fill { display: grid; gap: 10px; }
.kit-shell-demo .kit-shell-fill i {
  display: block; height: 14px; border-radius: 999px;
  background: var(--bg-placeholder);
}
.kit-shell-demo .kit-shell-fill i.w1 { width: 62%; }
.kit-shell-demo .kit-shell-fill i.w2 { width: 100%; }
.kit-shell-demo .kit-shell-fill i.w3 { width: 84%; }
.kit-shell-demo .kit-shell-fill i.tall { height: 56px; border-radius: var(--radius-sm); }

/* ============================================================================
   THE CURVE STAND, stage 11, 2026-08-16, and it is the reason this page exists
   rather than a table of four cubic-beziers. A curve is the one value in this
   system that cannot be read: nobody can look at `cubic-bezier(0.16, 1, 0.3, 1)`
   and know that it lands, and nobody can tell it from `(0.45, 0, 0.22, 1)` on
   paper at all. So the four run side by side against a shared clock and against
   `linear`, which is the fifth track and is there as the CONTROL: it is what the
   whole landing ran on until the founder said "make it soft", and seeing it beside
   the other four is the entire argument in one second.

   THE STAND RUNS AT 900ms AND THE PRODUCT RUNS AT 150, deliberately. A curve is a
   shape and a shape needs time to be seen; at the product's own duration all five
   tracks arrive together and the demo shows nothing. The number is written on the
   page so nobody reads it as the scale.

   It is the STAND's chrome, not the product's: these classes live here with the
   panel and the swatches, and design/system/ has never heard of them. */
.kit-curves { display: grid; gap: 10px; margin: 0 0 14px; }
.kit-curve {
  display: grid;
  grid-template-columns: 8.5rem minmax(0, 1fr);
  align-items: center;
  gap: 12px;
}
.kit-curve > .lab { font-size: 12px; color: var(--text-muted); }
.kit-curve > .lab b { display: block; font-size: 12.5px; color: var(--text-primary); font-weight: 600; }
.kit-curve-track {
  position: relative;
  height: 28px;
  border-radius: 999px;
  background: var(--bg-recessed);
  border: 1px solid var(--line-divider);
}
.kit-curve-track > i {
  position: absolute;
  top: 50%;
  left: 3px;
  width: 20px; height: 20px;
  margin-top: -10px;
  border-radius: 50%;
  background: var(--bg-action);
  translate: 0 0;
  transition: translate 900ms var(--e, linear);
}
/* the run state is a class on the CONTAINER, so all five start on one frame and
   the comparison is honest: five separate triggers would drift by a frame each */
.kit-curves.is-run .kit-curve-track > i { translate: calc(100cqw - 26px) 0; }
.kit-curve-track { container-type: inline-size; }
.kit-curve.c1 .kit-curve-track > i { --e: var(--ease-arrive); }
.kit-curve.c2 .kit-curve-track > i { --e: var(--ease-leave); }
.kit-curve.c3 .kit-curve-track > i { --e: var(--ease-settle); }
.kit-curve.c4 .kit-curve-track > i { --e: var(--ease-travel); }
.kit-curve.c5 .kit-curve-track > i { --e: linear; }
.kit-curve-run {
  font: inherit; font-size: 13px; font-weight: 600;
  padding: 8px 14px;
  border: 1px solid var(--line-control);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: var(--text-action);
  cursor: pointer;
}
.kit-curve-run:hover { border-color: var(--line-control-hover); background: var(--bg-hover); }

@media (prefers-reduced-motion: reduce) {
  .kit-curve-track > i { transition: none; }
}

/* ---- the signature stand, stage 11, 2026-08-16 ----------------------------
   The brand's one move happens at 22px, which is the size it ships at and too
   small to study. So the stand shows it twice: once at its real size, and once
   magnified. `scale` on the lockup and not a bigger mark, because a bigger mark
   would be a size this component does not have - brand-mark.css declares exactly
   one, and inventing a second here would make the stand a liar about the system.
   What is magnified is the RENDER, so what a person watches is the shipping
   object through a magnifying glass rather than a different object. */
.kit-stage .lockup {
  /* THE ROW, COPIED FROM THE BAR AND SAID SO. `.lockup` gets its flex row, its
     44px floor and its 8px gap from `.app .appbar .lockup` and from
     `.landing .lp-nav .lockup`, both of which need a BAR ancestor that this stand
     does not have. Without this the specimen stacks the mark above the word and
     shows a lockup the product has never rendered. Three values, all read off
     those two rules, which is why this is a stand fixing its own frame rather
     than a component quietly gaining a fourth host. */
  display: inline-flex; align-items: center;
  min-height: var(--tap);
  gap: var(--space-8);
}
/* THE ROOM RESERVES WHAT THE TRANSFORM DOES NOT. A `scale` paints large and lays
   out small, so the magnified lockup drew straight over the paragraph above it and
   the heading below. The room is a real box at the rendered height, and it clips,
   so the magnifying glass has a rim. */
.kit-sig-room {
  /* `width: 100%` IS LOAD-BEARING. Inside the stage's flex row the room's width
     would be its CONTENT's width, and the content lays out unscaled: 110px, which
     clipped the magnified mark in half and cut the word off entirely. */
  width: 100%; height: 120px; overflow: hidden;
  display: flex; align-items: center;
}
.kit-sig-big { transform: scale(4); transform-origin: left center; }
