/* site.css — nx-hbmenu Latitude project page.
 *
 * Two layers of colour, kept apart on purpose.
 *
 * 1. THE APPLICATION PALETTE. Not a web palette: THEME_DARK and THEME_LIGHT
 *    lifted verbatim out of source/theme_tokens.h:85-113, including the accent
 *    triplet from ACCENT_PRESETS[0] / ACCENT_PRESETS_LIGHT[0]. If the app's
 *    palette moves, this block is wrong and should be regenerated from that
 *    header. COL_BG is #000000 in the dark theme and stays #000000 here; it is
 *    not a stylistic near-black, handoff §5 makes it a power decision on an
 *    OLED panel.
 *
 * 2. THE PAGE'S OWN CHROME — --ground, --plate, --rule, the lifts, the bezel,
 *    the bloom. These are NOT in theme_tokens.h and must never be copied back
 *    into it. The page is a lit room that the product sits in; the product is
 *    the black object on the table. Keeping the two sets separate is what lets
 *    the room have a floor and a light source without the application growing
 *    one.
 */

:root {
  /* ---- 1. application palette, from source/theme_tokens.h ---------------- */
  --bg:          #000000;
  --surface-1:   #08090A;
  --surface-2:   #111315;
  --surface-3:   #1A1D20;
  --border:      #1C1F22;
  --border-soft: #131618;
  --border-hard: #2A2E33;
  --text-1:      #F4F5F6;
  --text-2:      #9AA0A6;
  --text-3:      #5A6066;
  --text-4:      #2A2E33;
  --ok:          #6FD08A;
  --accent:      #E2754D;
  --accent-dim:  #8E4B33;
  --accent-soft: #3F2118;

  /* ---- 2. page chrome ---------------------------------------------------- */
  --ground:      #000000;
  /* The house treatment for a dark asset meeting a page, agreed with the two
     designers producing docs/img: a screen inside a plate, ground #080A0D with
     a #1E2227 edge on dark and #F1F2F4 with #D8DCE0 on light. Every plate on
     this page is that pair, so an asset's own baked ground lands exactly on the
     plate it sits in and the two read as one object instead of as a frame.
     Pure #000000 is reserved for the product's screen — which on this page is
     both the stage inside its bezel and the page ground itself, and nothing in
     between. */
  --plate:       #080A0D;
  --plate-rule:  #1E2227;
  --code-bg:     #0B0D11;
  --rule:        #1C1F23;
  --rule-soft:   #131619;
  --rule-hard:   #2B2F34;
  --chip:        rgba(255, 255, 255, .04);
  --accent-chip: rgba(226, 117, 77, .10);
  --accent-line: #8F4B33;        /* 3.04:1 on --plate — the pill has an edge */
  --veil:        rgba(0, 0, 0, .68);

  /* PAGE INK. The application's text tiers are the product's, lifted from
     source/theme_tokens.h above, and not this page's to move. But this page is
     a document read at desk distance, and the app's quiet tier does not clear
     4.5:1 on it: --text-3 measures 3.30:1 on --ground in dark and 2.55:1 in
     light, --text-4 measures 1.54 and 1.28. So the page's own small type uses
     the page's own ink, and the product's tokens stay verbatim.
     Measured against the darkest ground each tier sits on. There is only one
     quiet tier: on the light --ground, 4.5:1 is reached at #5E646A and
     --text-2 is #5A6066, so a second compliant step below it does not exist.
     The tiers are carried by size, weight and tracking, which is where they
     already were. */
  --ink-quiet:   #7A828A;        /* 5.39:1 on --ground, 4.61:1 on --chip */
  --ink-accent:  var(--accent);  /* the dark accent already clears 4.5:1 as text */
  --edge-ctl:    #575F69;        /* 3.25:1 on --ground: a control's own boundary */

  /* The accent as bare channels, so the bloom and the rims can be alpha'd. */
  --glow: 226 117 77;
  --sky-top: rgba(255, 255, 255, .05);
  --bloom-a: .11;
  --vig: transparent;

  /* The stage is a console: its bezel stays dark in both themes. */
  --bezel-hi: #191C20;
  --bezel-lo: #08090B;
  --stage-shadow:
      0 2px 4px rgba(0, 0, 0, .8),
      0 44px 90px -40px rgba(0, 0, 0, 1),
      0 14px 40px -24px rgba(0, 0, 0, .9);

  --lift-1: 0 1px 0 rgba(255, 255, 255, .028) inset,
            0 14px 34px -22px rgba(0, 0, 0, .9);
  --lift-2: 0 1px 0 rgba(255, 255, 255, .045) inset,
            0 26px 56px -28px rgba(0, 0, 0, 1);

  /* Diagrams are drawn on plates now, so their ground is the plate. */
  --dg-bg: var(--plate);
  --dg-surface: var(--surface-2);
  --dg-line: var(--border-hard);
  --dg-ink: var(--text-1);
  --dg-ink-2: var(--text-2);
  --dg-ink-3: var(--text-3);
  --dg-accent: var(--accent);

  --sans: "Geist", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "Geist Mono", ui-monospace, "SF Mono", "Cascadia Mono", Menlo, monospace;

  /* ---- type scale -------------------------------------------------------- */
  --fs-display: clamp(2.9rem, 8.4vw, 5.5rem);
  --fs-h2:      clamp(1.8rem, 3.4vw, 2.8rem);
  --fs-h3:      clamp(1.18rem, 1.7vw, 1.45rem);
  --fs-lead:    clamp(1.16rem, 1.55vw, 1.42rem);
  --fs-body:    1.0625rem;
  --fs-small:   0.875rem;
  --fs-micro:   0.75rem;
  --fs-nano:    0.6875rem;

  /* ---- measure ----------------------------------------------------------- */
  --page:   1120px;              /* the whole composition */
  --gutter: clamp(20px, 4.5vw, 56px);
  --col:      40rem;             /* prose */
  --col-wide: 100%;              /* media: the full content box */

  /* The application's single easing, spec §motion. Everything here uses it. */
  --ease: cubic-bezier(.2, .65, .2, 1);

  color-scheme: dark;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --bg: #FAFAFB; --surface-1: #FFFFFF; --surface-2: #F1F2F4; --surface-3: #E9EAED;
    --border: #D8DCE0; --border-soft: #E4E7EA; --border-hard: #B9BFC4;
    --text-1: #1A1C1F; --text-2: #5A6066; --text-3: #8B9197; --text-4: #C9CDD0;
    --ok: #1F8F4F; --accent: #CC4F2A; --accent-dim: #7A2F19; --accent-soft: #FCE3D8;

    --ground: #E4E6EA; --plate: #F1F2F4; --plate-rule: #D8DCE0; --code-bg: #F8F9FA;
    --rule: #D3D7DC; --rule-soft: #DFE2E7; --rule-hard: #B4BAC1;
    --chip: rgba(255, 255, 255, .8); --accent-chip: #FAE9E1; --accent-line: #D07145;
    --ink-quiet: #5E646A; --ink-accent: #AF4424; --edge-ctl: #798490;
    --veil: rgba(237, 238, 240, .78);
    --glow: 204 79 42;
    --sky-top: rgba(255, 255, 255, .66);
    --bloom-a: .10;
    --vig: rgba(116, 124, 138, .17);
    --bezel-hi: #33373D; --bezel-lo: #101215;
    --stage-shadow:
        0 2px 5px rgba(20, 22, 26, .10),
        0 44px 74px -34px rgba(20, 22, 26, .46),
        0 14px 30px -18px rgba(20, 22, 26, .28);
    --lift-1: 0 1px 2px rgba(20, 22, 26, .05), 0 12px 26px -18px rgba(20, 22, 26, .28);
    --lift-2: 0 2px 6px rgba(20, 22, 26, .07), 0 28px 54px -26px rgba(20, 22, 26, .34);
    color-scheme: light;
  }
}
:root[data-theme="light"] {
  --bg: #FAFAFB; --surface-1: #FFFFFF; --surface-2: #F1F2F4; --surface-3: #E9EAED;
  --border: #D8DCE0; --border-soft: #E4E7EA; --border-hard: #B9BFC4;
  --text-1: #1A1C1F; --text-2: #5A6066; --text-3: #8B9197; --text-4: #C9CDD0;
  --ok: #1F8F4F; --accent: #CC4F2A; --accent-dim: #7A2F19; --accent-soft: #FCE3D8;

  --ground: #E4E6EA; --plate: #F1F2F4; --plate-rule: #D8DCE0; --code-bg: #F8F9FA;
  --rule: #D3D7DC; --rule-soft: #DFE2E7; --rule-hard: #B4BAC1;
  --chip: rgba(255, 255, 255, .8); --accent-chip: #FAE9E1; --accent-line: #D07145;
  --ink-quiet: #5E646A; --ink-accent: #AF4424; --edge-ctl: #798490;
  --veil: rgba(237, 238, 240, .78);
  --glow: 204 79 42;
  --sky-top: rgba(255, 255, 255, .66);
  --bloom-a: .10;
  --vig: rgba(116, 124, 138, .17);
  --bezel-hi: #33373D; --bezel-lo: #101215;
  --stage-shadow:
      0 2px 5px rgba(20, 22, 26, .10),
      0 44px 74px -34px rgba(20, 22, 26, .46),
      0 14px 30px -18px rgba(20, 22, 26, .28);
  --lift-1: 0 1px 2px rgba(20, 22, 26, .05), 0 12px 26px -18px rgba(20, 22, 26, .28);
  --lift-2: 0 2px 6px rgba(20, 22, 26, .07), 0 28px 54px -26px rgba(20, 22, 26, .34);
  color-scheme: light;
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  position: relative;              /* the ambient layer is measured from here */
  background: var(--ground);
  color: var(--text-1);
  font-family: var(--sans);
  font-size: var(--fs-body);
  line-height: 1.62;
  font-feature-settings: "ss01", "cv11";
  -webkit-font-smoothing: antialiased;
  /* clip, not hidden: `hidden` would make the body a scroll container and the
     sticky top bar would stop sticking. */
  overflow-x: clip;
}

/* ---- the ground -------------------------------------------------------- */
/* Two washes behind the masthead: the bloom is the accent at roughly the alpha
   a lit panel would actually throw onto a wall, the sky is the lift the top of
   that wall carries. Both are ABSOLUTE, not fixed — the atmosphere belongs to
   the hero and scrolls out of it, so the document below is the flat, quiet
   ground the reading wants. Neither is interactive and neither ever moves. */
.ambient {
  position: absolute; top: 0; left: 0; right: 0; height: 1500px;
  z-index: -1; pointer-events: none; overflow: hidden;
  /* A layer that stops has an edge, and in the light theme that edge was a
     visible rule straight across the page at 1500 px. Fade the whole thing out
     over its last third instead of ending it. */
  -webkit-mask-image: linear-gradient(180deg, #000 0, #000 62%, transparent 100%);
          mask-image: linear-gradient(180deg, #000 0, #000 62%, transparent 100%);
}
.ambient i { position: absolute; display: block; }
.amb-sky {
  left: 0; right: 0; top: 0; height: 900px;
  background: linear-gradient(180deg, var(--sky-top) 0%, transparent 100%);
}
.amb-bloom {
  left: 50%; top: -160px; transform: translateX(-50%);
  width: min(190vw, 2000px); height: 1200px;
  background: radial-gradient(50% 50% at 50% 50%,
      rgb(var(--glow) / var(--bloom-a)) 0%,
      rgb(var(--glow) / calc(var(--bloom-a) * .3)) 40%,
      transparent 72%);
}
/* The falloff at the edges of the lit area. Nothing in the dark theme — a
   vignette on pure black is a no-op — and in the light theme it is the whole
   difference between a surface and a sheet of paper. */
.ambient::after {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(125% 75% at 50% 16%, transparent 42%, var(--vig) 100%);
}

.wrap { max-width: var(--page); margin: 0 auto; padding: 0 var(--gutter); }

a { color: var(--text-1); text-decoration-color: var(--accent-dim); text-underline-offset: 3px;
    transition: text-decoration-color .2s var(--ease), color .2s var(--ease); }
a:hover { text-decoration-color: var(--ink-accent); }
code, .m { font-family: var(--mono); font-size: 0.875em; }
.m { color: var(--text-2); }

/* Geist Mono ships coding ligatures on by default, and they misquote the source
   this page reproduces: `==` draws as one wide glyph shifted a cell left, which
   swallows the space before it and reads as a single `=`; `->` strikes its tail
   through the character to its left. Off everywhere mono renders. */
code, .m, kbd, pre, pre code, td.n, th.n, .src { font-variant-ligatures: none; }
figcaption .m, .src .m, .kill .m, .ledger .m, td .m, th .m { color: inherit; }
code { color: var(--text-1); background: var(--surface-2); padding: 0.08em 0.36em; border-radius: 3px; }

/* ---- top bar ------------------------------------------------------------ */
/* Empty over the masthead — the title is right there — and it fills in once the
   masthead has gone past. */
.topbar { position: sticky; top: 0; z-index: 60; }
.topbar::before {
  content: ""; position: absolute; inset: 0;
  background: var(--veil);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
          backdrop-filter: blur(18px) saturate(150%);
  box-shadow: 0 1px 0 var(--rule);
  opacity: 0; transition: opacity .4s var(--ease);
}
.topbar.stuck::before { opacity: 1; }
.topbar .bar {
  position: relative; max-width: var(--page); margin: 0 auto;
  padding: 0 var(--gutter); height: 58px;
  display: flex; align-items: center; gap: 16px;
}
/* min-width:0 alone let the mark SHRINK but not CLIP: a nowrap wordmark inside a
   flex item that has no overflow of its own keeps painting past the item's box,
   so below about 382 px the lockup ran straight under the theme control — the
   mark measured 104 px wide with 166 px of content at a 320 px viewport, and
   nothing crossed the viewport edge, so a plain overflow check stayed clean
   while the bar was visibly broken. The item clips, the wordmark ellipsises,
   and the control never shrinks. */
.topbar .mark {
  display: flex; align-items: center; gap: 11px; text-decoration: none;
  min-width: 0; overflow: hidden;
  opacity: 0; visibility: hidden; transform: translateY(3px);
  transition: opacity .4s var(--ease), transform .4s var(--ease), visibility 0s .4s;
}
.topbar.stuck .mark { opacity: 1; visibility: visible; transform: none; transition-delay: 0s; }
.topbar .mark img { width: 26px; height: 26px; border-radius: 5px; display: block; flex: none; }
.topbar .wm { font-size: 0.9375rem; font-weight: 500; letter-spacing: -.01em; white-space: nowrap;
              min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.topbar .wm .o { font: 400 var(--fs-micro) var(--mono); color: var(--ink-quiet); margin-right: 5px; }

.themectl { margin-left: auto; flex: none; display: flex; gap: 2px; border: 1px solid var(--edge-ctl);
            border-radius: 999px; padding: 3px; background: var(--chip); }
.themectl button {
  font: 500 var(--fs-nano)/1 var(--mono); letter-spacing: .1em; color: var(--ink-quiet);
  background: none; border: 0; padding: 6px 10px; border-radius: 999px; cursor: pointer;
  transition: color .2s var(--ease), background-color .2s var(--ease);
}
.themectl button:hover { color: var(--text-1); }
.themectl button[aria-pressed="true"] { background: var(--surface-2); color: var(--text-1); }

/* ---- masthead ----------------------------------------------------------- */
header.masthead { padding: clamp(18px, 2.6vw, 36px) 0 0; }

.lockup { display: flex; flex-direction: column; align-items: center; text-align: center; }
/* Mark and wordmark on one line: the hero's whole job is to hand the viewport
   to the stage, so the lockup spends 28 px of height, not 90. */
.eyebrow { display: flex; align-items: center; justify-content: center; gap: 12px; margin: 0; }
.eyebrow img { width: 28px; height: 28px; display: block; border-radius: 7px; box-shadow: var(--lift-1); }
.eyebrow span {
  font: 500 var(--fs-micro)/1 var(--mono); letter-spacing: .42em; text-indent: .42em;
  text-transform: uppercase; color: var(--ink-quiet);
}
h1 {
  margin: clamp(10px, 1.4vw, 18px) 0 0;
  font-size: var(--fs-display); font-weight: 500; letter-spacing: -.045em; line-height: .94;
}
.statement {
  margin: clamp(16px, 2vw, 24px) auto 0; max-width: 40rem;
  font-size: var(--fs-lead); font-weight: 300; line-height: 1.42; color: var(--text-1);
  text-wrap: balance;
}
/* The two footnote lines under it. The joke is the type scale: the headline
   shrugs, and what it shrugs off is set in the size a disclaimer gets. Each step
   down also steps back in weight and colour, so the eye reads them in order. */
/* The gap is in rem, not em, so the two steps sit the same distance apart
   instead of shrinking along with the type. */
.statement-fine, .statement-finer {
  margin: .55rem auto 0; max-width: 40rem; text-wrap: balance;
  font-weight: 300; line-height: 1.45;
}
.statement-fine  { font-size: var(--fs-small); color: var(--text-2); }
.statement-finer { font-size: 0.58rem; color: var(--ink-quiet, var(--text-3)); }

/* ---- the stage ---------------------------------------------------------- */
/* A black panel on a surface, not a rectangle pasted onto a document: a bezel
   with a rim light, a shadow that has a contact term and a spread term, and a
   spill on the floor beneath it that only appears while the panel has the
   keyboard. */
/* Slightly inset from the content width. Two reasons: an object with air around
   it reads as photographed rather than as a band, and the 39 px it gives back
   are what put the panel's bottom edge — and the badge on it — above the fold
   of a 1440x950 window. */
.stagebox { margin: clamp(24px, 3.2vw, 42px) auto 0; max-width: min(100%, 930px); }
.stage-plinth { position: relative; }

.stage-frame {
  position: relative; z-index: 1;
  padding: clamp(6px, .8vw, 9px);
  border-radius: clamp(12px, 1.4vw, 17px);
  background: linear-gradient(175deg, var(--bezel-hi) 0%, var(--bezel-lo) 62%);
  box-shadow: var(--stage-shadow);
  transition: box-shadow .55s var(--ease);
}
.stage-frame::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .13),
              inset 0 0 0 1px rgba(255, 255, 255, .05),
              inset 0 -1px 0 rgba(0, 0, 0, .5);
}
.stagebox.is-live .stage-frame {
  box-shadow: var(--stage-shadow),
              0 0 0 1px rgb(var(--glow) / .38),
              0 0 70px -14px rgb(var(--glow) / .30);
}
.stage-host {
  position: relative; display: block; width: 100%; aspect-ratio: 1280 / 720;
  background: #000; border-radius: clamp(6px, .8vw, 10px); overflow: hidden;
}
.stage-host:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.stage-fallback {
  position: absolute; inset: 0; display: grid; place-items: center;
  color: var(--ink-quiet); font: 500 var(--fs-micro)/1.6 var(--mono); text-align: center; padding: 24px;
}
.stage-spill {
  position: absolute; left: 8%; right: 8%; top: 100%; height: 150px; z-index: 0;
  background: radial-gradient(58% 100% at 50% 0%, rgb(var(--glow) / .26) 0%, transparent 72%);
  filter: blur(14px); opacity: 0; pointer-events: none;
  transition: opacity .7s var(--ease);
}
.stagebox.is-live .stage-spill { opacity: 1; }

/* The one affordance. A badge straddling the panel's bottom edge, so it is
   part of the device rather than a note under it, and so it stays on screen
   for as long as the panel's bottom edge does. It is dark in both themes for
   the same reason the bezel is. It retires on the first input and never
   returns; being absolutely positioned, retiring costs no reflow. */
.stage-cue {
  position: absolute; left: 50%; top: 100%; z-index: 3;
  transform: translate(-50%, -50%);
  margin: 0; display: inline-flex; align-items: center; gap: 9px; white-space: nowrap;
  padding: 8px 15px 8px 12px; border-radius: 999px;
  font: 500 var(--fs-micro)/1 var(--mono); letter-spacing: .1em; text-transform: uppercase;
  color: rgba(244, 245, 246, .66);
  background: rgba(14, 16, 19, .9); border: 1px solid rgba(255, 255, 255, .13);
  -webkit-backdrop-filter: blur(12px) saturate(150%);
          backdrop-filter: blur(12px) saturate(150%);
  transition: opacity .5s var(--ease), transform .5s var(--ease),
              color .5s var(--ease), border-color .5s var(--ease);
}
.stage-cue em { font-style: normal; }
.stage-cue kbd { color: #F4F5F6; border-color: rgba(255, 255, 255, .3); }
.stage-cue .dot {
  width: 6px; height: 6px; border-radius: 50%; flex: none;
  background: rgba(244, 245, 246, .3); transition: background-color .5s var(--ease);
}
.stagebox.is-live .stage-cue { color: #F4F5F6; border-color: rgb(var(--glow) / .92); }
.stagebox.is-live .stage-cue .dot { background: var(--accent); animation: cue-pulse 2.6s ease-out infinite; }
.stagebox.had-input .stage-cue {
  opacity: 0; transform: translate(-50%, -50%) scale(.94); pointer-events: none;
}
.stage-frame:fullscreen ~ .stage-cue { display: none; }

/* Full screen keeps THIS instance. The mock scales to its host box, so the host
   just has to become the viewport. */
.stage-frame:fullscreen { padding: 0; border-radius: 0; background: #000; box-shadow: none; }
.stage-frame:fullscreen::after { display: none; }
.stage-frame:fullscreen .stage-host {
  width: 100vw; height: 100vh; aspect-ratio: auto; border-radius: 0;
}

/* ---- the stage on a phone ------------------------------------------------ */
/* Below 620 px the stage stops being a miniature and becomes a window onto one.
   Shrunk to fit a 340 px column the mock draws its own 24 px interface type at
   six pixels: the centrepiece of the page arrives as a grey smear. A still
   plate was the other option and it is the wrong one here — img/fig-library.svg
   already draws this screen one scroll down, and it draws it better than a
   shrunken still could, so the still would be the worse of two pictures of the
   same thing and the top of the page would have nothing live on it.
   The host box is held at 600 px instead and the bezel clips it. The interface
   lands at 0.47x, where the selected tile, its ring, the focus name and the
   pulse row all read, and the axis is cropped at both edges — which is what the
   axis does anyway: §1 says it has no right edge. The instance is the same live
   one, keyed and pointer-driven exactly as before, and full screen still hands
   it the whole viewport.
   :not(:fullscreen) so the rule above keeps the fullscreen case. */
@media (max-width: 620px) {
  .stage-frame:not(:fullscreen) { overflow: hidden; }
  .stage-frame:not(:fullscreen) > .stage-host {
    /* A fixed bite out of each side rather than a fixed scale: the window keeps
       the same composition all the way down to 320 px and gives up a little
       scale instead of eating further into the focus panel. It also closes
       itself as the viewport approaches the breakpoint, so nothing jumps at
       620 px. */
    width: min(600px, calc(100% + 260px));
    margin-left: 50%; transform: translateX(-50%);
  }
}

/* ---- the stage's caption row -------------------------------------------- */
.keys {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
  gap: 9px 18px; margin: clamp(28px, 3.2vw, 38px) auto 0; padding: 0;
  max-width: var(--col-wide);
  font: 500 var(--fs-micro)/1.9 var(--mono); color: var(--ink-quiet);
}
.keys .fs { color: var(--text-2); text-decoration-color: var(--rule-hard); }
.keys .fs:hover { color: var(--ink-accent); }
kbd { font-family: var(--mono); font-size: var(--fs-nano); color: var(--text-1);
      border: 1px solid var(--rule-hard); border-radius: 4px; padding: 2px 5px; margin: 0 1px; }

/* Touch pad: no hover, or a phone-width viewport. Hidden everywhere else — the
   keyboard is the real control and it needs no chrome.
   A grid, not a wrapped flex row: six 48 px targets and five 8 px gaps want
   328 px of content box, which a viewport narrower than 368 px does not have,
   and wrapping left the sixth button alone on a line of its own. Two rows of
   three below 400 px, which is also a better thumb reach. */
.pad { display: none; }
@media (hover: none), (max-width: 720px) {
  .pad { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 8px;
         margin: clamp(14px, 2vw, 18px) auto 0; max-width: min(100%, 420px); }
}
@media (max-width: 400px) {
  .pad { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.pad button {
  font: 500 13px/1 var(--mono); color: var(--text-2);
  background: var(--plate); border: 1px solid var(--edge-ctl); border-radius: 10px;
  padding: 12px 8px; min-width: 0; min-height: 44px; cursor: pointer;
}
.pad button:active { color: var(--ink-accent); border-color: var(--ink-accent); }

/* ---- hero footings ------------------------------------------------------ */
.herofoot {
  display: grid; gap: clamp(16px, 2.4vw, 34px);
  margin: clamp(34px, 4.6vw, 60px) auto 0; max-width: var(--col-wide);
}
@media (min-width: 860px) { .herofoot { grid-template-columns: 1fr 1fr; } }
.herofoot p { margin: 0; max-width: none; }
.lede { color: var(--text-1); }
.sublede { color: var(--text-2); }

.specstrip {
  list-style: none; margin: clamp(30px, 4vw, 50px) auto 0; padding: 0;
  max-width: var(--col-wide);
  display: flex; flex-wrap: wrap;
  border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule);
}
.specstrip li { flex: 1 1 190px; margin: 0; padding: clamp(16px, 1.9vw, 22px) clamp(14px, 1.8vw, 24px); }
@media (min-width: 760px) {
  .specstrip li { border-left: 1px solid var(--rule-soft); }
  .specstrip li:first-child { border-left: 0; }
}
@media (max-width: 759px) {
  .specstrip li { flex: 1 1 100%; border-top: 1px solid var(--rule-soft); }
  .specstrip li:first-child { border-top: 0; }
}
.specstrip b {
  display: block; font: 500 clamp(1.02rem, 1.5vw, 1.28rem)/1.15 var(--mono);
  color: var(--text-1); letter-spacing: -.015em; font-variant-numeric: tabular-nums;
}
.specstrip span { display: block; margin-top: 8px; font-size: var(--fs-micro); color: var(--ink-quiet); }

/* ---- contents ----------------------------------------------------------- */
nav.contents { margin: clamp(46px, 6vw, 86px) 0 0; border-top: 1px solid var(--rule); max-width: var(--col-wide); }
nav.contents ol { list-style: none; margin: 0; padding: 0; max-width: none; display: grid; }
@media (min-width: 720px) {
  nav.contents ol { grid-template-columns: 1fr 1fr; column-gap: clamp(24px, 4vw, 64px); }
}
nav.contents li { margin: 0; border-bottom: 1px solid var(--rule-soft); }
nav.contents a {
  display: flex; align-items: baseline; gap: 14px; padding: 15px 2px;
  text-decoration: none; color: var(--text-1);
}
nav.contents a:hover { color: var(--ink-accent); }
nav.contents .n { font: 500 var(--fs-micro)/1 var(--mono); letter-spacing: .1em; color: var(--ink-quiet); flex: none; }
nav.contents a:hover .n { color: var(--ink-accent); }
nav.contents .t { font-size: 1rem; font-weight: 400; letter-spacing: -.01em; }

/* ---- sections ----------------------------------------------------------- */
section { padding: clamp(62px, 8.5vw, 128px) 0 0; }
h2 {
  position: relative; margin: 0 0 clamp(26px, 3.4vw, 46px); padding-top: clamp(22px, 2.4vw, 30px);
  max-width: var(--col-wide);
}
h2::before { content: ""; position: absolute; left: 0; top: 0; width: 100%; height: 1px; background: var(--rule); }
h2::after  { content: ""; position: absolute; left: 0; top: 0; width: 58px; height: 1px; background: var(--accent); }
h2 .n { display: block; font: 500 var(--fs-micro)/1 var(--mono); letter-spacing: .28em; color: var(--ink-accent); margin-bottom: 15px; }
h2 .n::before { content: "\00A7"; opacity: .5; margin-right: .45em; }
h2 .t { display: block; font-size: var(--fs-h2); font-weight: 500; letter-spacing: -.032em; line-height: 1.04; }

h3 { font-size: var(--fs-h3); font-weight: 500; letter-spacing: -.022em; margin: 2.9em 0 .75em; }
h4 { font-size: 1rem; font-weight: 600; margin: 2em 0 .4em; }
p { margin: 0 0 1.2em; max-width: var(--col); }
ul, ol { max-width: var(--col); padding-left: 1.25em; }
li { margin: 0 0 .45em; }
hr { border: 0; border-top: 1px solid var(--rule); margin: 3em 0; max-width: var(--col); }
.dim { color: var(--text-2); }

/* Provenance notes read in a different register from the argument. */
.src {
  font: 400 var(--fs-micro)/1.75 var(--mono); color: var(--ink-quiet);
  max-width: 46rem; padding-left: 15px; border-left: 1px solid var(--rule);
}
/* A source path is one unbreakable token roughly 300 px wide in mono. Left
   alone it sets the min-content width of whatever grid cell it is in, which is
   what pushed the §4 cards past a 380 px viewport. `anywhere` (not
   `break-word`) is the one value that also lowers the min-content
   contribution, so the cards can actually be narrow. */
.src, .m { overflow-wrap: anywhere; }
.src a { color: var(--ink-quiet); text-decoration-color: var(--edge-ctl); }

/* ---- plates ------------------------------------------------------------- */
/* Everything that is an object rather than a sentence sits on one. */
.plate {
  background: var(--plate);
  border: 1px solid var(--plate-rule);
  border-radius: 14px;
  box-shadow: var(--lift-1);
}

figure { margin: clamp(28px, 3.4vw, 44px) 0; }
figure.wide { max-width: var(--col-wide); }
figure img, figure svg { display: block; width: 100%; height: auto; }
figure.wide.plate { --pad: clamp(13px, 1.7vw, 20px); padding: var(--pad); }
figure.wide.plate > img, figure.wide.plate > svg { border-radius: 8px; }
figcaption { font-size: var(--fs-small); line-height: 1.55; color: var(--text-2); max-width: 58rem; }
figure.wide.plate > figcaption {
  margin-top: var(--pad); padding-top: calc(var(--pad) - 2px);
  border-top: 1px solid var(--plate-rule);
}
figcaption b { color: var(--text-1); font-weight: 600; }
figcaption .m { color: var(--ink-quiet); }

/* The four SVG diagrams were drawn for a 380 px column — their viewBoxes are
   ~500 units wide so 13 px labels land near 10 px. Rendered at the full content
   width they came out at nearly three times that and read as posters. Each
   figure declares its design width and the plate sizes itself to it. */
figure.dia { max-width: calc(var(--dw) * 1.06px + var(--pad) * 2); }
figure.dia > svg { margin-inline: auto; }

/* The vector figures are authored 1120-1328 units wide. Scaled to a 340 px phone
   their labels land near four pixels, so they keep a floor width and scroll
   sideways inside the plate — the same bargain the tables and the rack figure
   already make on this page. */
.figscroll { overflow-x: auto; overscroll-behavior-x: contain; }
.figscroll img { min-width: 660px; border-radius: 8px; }
figure.wide.plate > .figscroll + figcaption {
  margin-top: var(--pad); padding-top: calc(var(--pad) - 2px);
  border-top: 1px solid var(--plate-rule);
}

/* Assets are shipped as a dark/light pair with baked colour, so the swap is a
   display swap rather than a src swap — which is what keeps them following the
   page's own AUTO/DARK/LIGHT control and not just prefers-color-scheme. */
.lit-light { display: none; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .lit-dark  { display: none; }
  :root:not([data-theme="dark"]) .lit-light { display: block; }
}
:root[data-theme="light"] .lit-dark  { display: none; }
:root[data-theme="light"] .lit-light { display: block; }
:root[data-theme="dark"]  .lit-dark  { display: block; }
:root[data-theme="dark"]  .lit-light { display: none; }

/* ---- tables ------------------------------------------------------------- */
.tw { overflow-x: auto; overscroll-behavior-x: contain; margin: clamp(24px, 3vw, 38px) 0;
      max-width: var(--col-wide); padding: 2px clamp(14px, 1.8vw, 22px); }
table { border-collapse: collapse; font-size: var(--fs-small); width: 100%; min-width: 620px; }
th, td { text-align: left; padding: 12px 22px 12px 0; vertical-align: top; white-space: nowrap; }
th { font: 500 var(--fs-nano)/1.5 var(--mono); letter-spacing: .12em; text-transform: uppercase;
     color: var(--ink-quiet); padding-top: 16px; border-bottom: 1px solid var(--rule-hard); }
td { border-bottom: 1px solid var(--rule-soft); }
tbody tr:last-child td { border-bottom: 0; }
td.n, th.n { font-family: var(--mono); font-size: 13px; font-variant-numeric: tabular-nums; }
td.w { white-space: normal; min-width: 15rem; }
td .u { color: var(--ink-quiet); }
tr td:first-child { color: var(--text-1); font-weight: 500; }

/* ---- code --------------------------------------------------------------- */
pre {
  font-family: var(--mono); font-size: 13px; line-height: 1.7;
  margin: clamp(24px, 3vw, 38px) 0;
  padding: clamp(16px, 2vw, 22px) clamp(16px, 2vw, 24px);
  background: var(--code-bg); border: 1px solid var(--plate-rule);
  border-radius: 12px; box-shadow: var(--lift-1);
  overflow-x: auto; max-width: var(--col-wide); color: var(--text-2);
}
pre b { color: var(--text-1); font-weight: 500; }
pre i { color: var(--ink-quiet); font-style: normal; }
pre .a { color: var(--ink-accent); }
pre code { background: none; padding: 0; font-size: inherit; }

/* ---- kill list ---------------------------------------------------------- */
.kills { display: grid; align-items: start; gap: clamp(12px, 1.5vw, 18px);
         max-width: var(--col-wide); margin-top: clamp(24px, 3vw, 36px); }
@media (min-width: 1000px) { .kills { grid-template-columns: 1fr 1fr; } }
.kill { padding: clamp(20px, 2.4vw, 28px); min-width: 0; }
.kill .verdict {
  display: inline-block; font: 500 var(--fs-nano)/1 var(--mono); letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-accent);
  background: var(--accent-chip); border: 1px solid var(--accent-line);
  border-radius: 999px; padding: 6px 11px; margin-bottom: 16px;
}
.kill h4 { margin: 0 0 10px; font-size: 1.1rem; font-weight: 600; letter-spacing: -.018em; }
.kill p { font-size: var(--fs-small); color: var(--text-2); max-width: none; }
.kill p:last-child { margin-bottom: 0; }

/* ---- the not-done ledger ------------------------------------------------ */
ul.ledger { list-style: none; margin: 0; padding: 0; max-width: var(--col-wide); }
ul.ledger li {
  position: relative; margin: 0; padding: clamp(18px, 2vw, 24px) 0 clamp(18px, 2vw, 24px) 32px;
  border-top: 1px solid var(--rule-soft);
  font-size: var(--fs-small); color: var(--text-2); max-width: 54rem;
}
ul.ledger li:first-child { border-top: 0; padding-top: 0; }
ul.ledger li::before {
  content: ""; position: absolute; left: 6px; top: calc(clamp(18px, 2vw, 24px) + .62em);
  width: 6px; height: 6px; border-radius: 50%; background: var(--accent-dim);
}
ul.ledger li:first-child::before { top: .62em; }
ul.ledger li b { color: var(--text-1); font-weight: 600; }

/* ---- drive-it buttons --------------------------------------------------- */
.drive { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 1.6em 0; }
.drive .lbl { font: 500 var(--fs-nano)/1 var(--mono); letter-spacing: .12em; text-transform: uppercase;
              color: var(--ink-quiet); margin-right: 6px; }
.drive button {
  font: 500 var(--fs-micro)/1 var(--mono); color: var(--text-2); background: var(--plate);
  border: 1px solid var(--edge-ctl); border-radius: 999px; padding: 9px 14px; cursor: pointer;
  transition: color .2s var(--ease), border-color .2s var(--ease);
}
.drive button:hover { color: var(--text-1); border-color: var(--rule-hard); }
.drive button:active { border-color: var(--accent); color: var(--ink-accent); }

/* ---- colophon ----------------------------------------------------------- */
footer { margin: clamp(70px, 10vw, 130px) 0 clamp(60px, 8vw, 100px); }
footer p, footer li { font-size: var(--fs-small); color: var(--text-2); max-width: var(--col); }
footer h2 { margin-bottom: clamp(20px, 2.4vw, 30px); }
footer h2 .t { font-size: var(--fs-h3); }
footer .src { margin-top: 2em; }

/* ---- entrance and scroll choreography ---------------------------------- */
/* One orchestrated arrival, then one gesture on scroll: figures rack into
   focus rather than sliding, because blur-to-sharp is the only thing this
   renderer is about. Every duration below is the application's own easing. */
@keyframes anim-up   { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes anim-rack { from { opacity: 0; transform: translateY(16px); filter: blur(10px); }
                       to   { opacity: 1; transform: none; filter: blur(0); } }
@keyframes anim-rise { from { opacity: 0; transform: translateY(28px) scale(.985); }
                       to   { opacity: 1; transform: none; } }
@keyframes cue-pulse { 0%   { box-shadow: 0 0 0 0 rgb(var(--glow) / .55); }
                       70%  { box-shadow: 0 0 0 8px rgb(var(--glow) / 0); }
                       100% { box-shadow: 0 0 0 0 rgb(var(--glow) / 0); } }

.anim      { animation: anim-up .8s var(--ease) both; animation-delay: var(--d, 0ms); }
.anim.rack { animation-name: anim-rack; animation-duration: 1s; }
.anim.rise { animation-name: anim-rise; animation-duration: 1.1s; }

.reveal    { opacity: 0; transform: translateY(20px); filter: blur(10px); }
.reveal.in { opacity: 1; transform: none; filter: blur(0);
             transition: opacity .8s var(--ease), transform .8s var(--ease), filter .8s var(--ease); }

/* ---- animated figures --------------------------------------------------- */
/* The focus rack (§3): a mechanism a still cannot carry, so it is built out of
 * elements rather than shipped as one.
 * Every length inside it is a 720p logical pixel: the body sets 1em to one
 * of those, so the geometry is the shipped constants unscaled — 180 px pitch,
 * 160 px tile, 448 px cursor, z = -1.5 d^4 clamped at -480, CoC = |z|/480*14.
 * The markup and the @keyframes at the end of this file are both written by
 * `python tools/make_figs.py`, which reads those constants. Change the figures
 * there, not here.
 */
.rig {
  position: relative; container-type: inline-size;
  max-width: var(--col-wide); margin: 0 auto; padding-bottom: 30px;
}
.rig-body { position: relative; line-height: 1; font-size: 0.85px; }
@supports (width: 1cqi) { .rig-body { font-size: calc(100cqi / var(--design, 1280)); } }

.rig-stop { position: absolute; width: 1px; height: 1px; opacity: 0; margin: 0; pointer-events: none; }
.rig-btn {
  position: absolute; right: 0; bottom: 0; cursor: pointer;
  font: 500 var(--fs-nano)/1 var(--mono); color: var(--ink-quiet); letter-spacing: .1em;
  text-transform: uppercase;
  background: var(--chip); border: 1px solid var(--edge-ctl);
  border-radius: 999px; padding: 6px 11px;
}
.rig-btn:hover { color: var(--text-1); border-color: var(--rule-hard); }
.rig-stop:focus-visible + .rig-btn { outline: 2px solid var(--accent); outline-offset: 2px; }
.rig-btn .b { display: none; }
.rig-stop:checked ~ .rig-btn .a { display: none; }
.rig-stop:checked ~ .rig-btn .b { display: inline; }
.rig-stop:checked ~ .rig-scroll .an { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  .rig-scroll .an { animation-play-state: paused; }
  .rig-btn .a { display: none; }
  .rig-btn .b { display: inline; }
  .rig-stop:checked ~ .rig-scroll .an { animation-play-state: running; }
  .rig-stop:checked ~ .rig-btn .a { display: inline; }
  .rig-stop:checked ~ .rig-btn .b { display: none; }
}

/* ---- the focus rack ----------------------------------------------------- */
.rig-rack .rig-body { aspect-ratio: 1280 / 358; }
.rack-plot { position: absolute; inset: 0; width: 100%; height: 100%; }
.rack-plot .rp-n { font: 500 12px var(--mono); font-variant-ligatures: none; }
.rack-plot .rp-s { font: 500 11px var(--sans); }
.rack-scene { position: absolute; inset: 0; overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 5%, #000 96%, transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 5%, #000 96%, transparent 100%); }
.rack-strip { position: absolute; inset: 0; will-change: transform;
              animation: rack-strip 7s linear infinite -2.8s; }
.rack-t { position: absolute; top: 0; left: calc(var(--i) * 180em); width: 160em; height: 100%; }

.rack-card { position: absolute; left: 0; top: 0; width: 160em; height: 216em;
             animation: rack-blur 7s linear infinite;
             animation-delay: calc((var(--i) - 5) * 0.7s - 9.8s); }
.rack-card::before {
  content: ""; position: absolute; left: 0; top: 8em; width: 160em; height: 160em;
  background: var(--dg-surface); border: 1em solid var(--dg-line); border-radius: 3em;
}
.rack-plate { position: absolute; left: 32em; top: 40em; width: 96em; height: 96em; }
.rack-plate i { position: absolute; border-radius: 2em; background: var(--dg-ink-3); opacity: .5;
  left: calc(var(--x) * 1em); top: calc(var(--y) * 1em);
  width: calc(var(--w) * 1em); height: calc(var(--h) * 1em); }
.rack-name { position: absolute; left: 32em; top: 182em; width: 96em; height: 9em;
             border-radius: 5em; background: var(--dg-ink-3); opacity: .45; }
.rack-pulse { position: absolute; left: 32em; top: 200em; width: 96em; height: 10em;
              display: flex; justify-content: space-between; }
.rack-pulse i { width: 3.4em; height: 10em; border-radius: 1em; background: var(--dg-ink-3); opacity: .3; }
.rack-pulse i.on { background: var(--dg-accent); opacity: .62; }

.rack-ring { position: absolute; left: -4em; top: 4em; width: 168em; height: 168em;
             border: 2em solid var(--dg-accent); border-radius: 6em; opacity: 0;
             animation: rack-ring 7s linear infinite;
             animation-delay: calc((var(--i) - 5) * 0.7s - 9.8s); }
.rack-dot { position: absolute; left: 76em; top: 242em; width: 8em; height: 8em;
            border-radius: 50%; background: var(--dg-accent);
            animation: rack-dot 7s linear infinite;
            animation-delay: calc((var(--i) - 5) * 0.7s - 9.8s); }


/* Narrow: the rack figure carries labels that stop being readable below about
 * 700 px, so it keeps a floor width and scrolls sideways, the way the tables on
 * this page do. */
@media (max-width: 760px) {
  .rig-scroll { overflow-x: auto; overscroll-behavior-x: contain; }
  .rig-rack .rig-body  { width: 760px; font-size: calc(760px / 1280); }
}

/* @generated by tools/make_figs.py -- do not hand-edit past this line */
@keyframes rack-strip {
  0% { transform: translateX(448em) }
  2.7143% { transform: translateX(448em) }
  3.8571% { transform: translateX(320.665em) }
  5% { transform: translateX(282.161em) }
  6.1429% { transform: translateX(270.583em) }
  7.2857% { transform: translateX(268em) }
  12.7143% { transform: translateX(268em) }
  13.8571% { transform: translateX(140.665em) }
  15% { transform: translateX(102.161em) }
  16.1429% { transform: translateX(90.583em) }
  17.2857% { transform: translateX(88em) }
  22.7143% { transform: translateX(88em) }
  23.8571% { transform: translateX(-39.335em) }
  25% { transform: translateX(-77.839em) }
  26.1429% { transform: translateX(-89.417em) }
  27.2857% { transform: translateX(-92em) }
  32.7143% { transform: translateX(-92em) }
  33.8571% { transform: translateX(-219.335em) }
  35% { transform: translateX(-257.839em) }
  36.1429% { transform: translateX(-269.417em) }
  37.2857% { transform: translateX(-272em) }
  42.7143% { transform: translateX(-272em) }
  43.8571% { transform: translateX(-399.335em) }
  45% { transform: translateX(-437.839em) }
  46.1429% { transform: translateX(-449.417em) }
  47.2857% { transform: translateX(-452em) }
  52.7143% { transform: translateX(-452em) }
  53.8571% { transform: translateX(-579.335em) }
  55% { transform: translateX(-617.839em) }
  56.1429% { transform: translateX(-629.417em) }
  57.2857% { transform: translateX(-632em) }
  62.7143% { transform: translateX(-632em) }
  63.8571% { transform: translateX(-759.335em) }
  65% { transform: translateX(-797.839em) }
  66.1429% { transform: translateX(-809.417em) }
  67.2857% { transform: translateX(-812em) }
  72.7143% { transform: translateX(-812em) }
  73.8571% { transform: translateX(-939.335em) }
  75% { transform: translateX(-977.839em) }
  76.1429% { transform: translateX(-989.417em) }
  77.2857% { transform: translateX(-992em) }
  82.7143% { transform: translateX(-992em) }
  83.8571% { transform: translateX(-1119.335em) }
  85% { transform: translateX(-1157.839em) }
  86.1429% { transform: translateX(-1169.417em) }
  87.2857% { transform: translateX(-1172em) }
  92.7143% { transform: translateX(-1172em) }
  93.8571% { transform: translateX(-1299.335em) }
  95% { transform: translateX(-1337.839em) }
  96.1429% { transform: translateX(-1349.417em) }
  97.2857% { transform: translateX(-1352em) }
  100% { transform: translateX(-1352em) }
}
@keyframes rack-blur {
  0% { filter: blur(3.5em) }
  3.8571% { filter: blur(3.5em) }
  5% { filter: blur(3.027em) }
  6.1429% { filter: blur(2.84em) }
  7.2857% { filter: blur(2.8em) }
  12.7143% { filter: blur(2.8em) }
  13.8571% { filter: blur(1.285em) }
  15% { filter: blur(0.983em) }
  16.1429% { filter: blur(0.903em) }
  17.2857% { filter: blur(0.886em) }
  22.7143% { filter: blur(0.886em) }
  23.8571% { filter: blur(0.302em) }
  25% { filter: blur(0.204em) }
  26.1429% { filter: blur(0.18em) }
  27.2857% { filter: blur(0.175em) }
  32.7143% { filter: blur(0.175em) }
  33.8571% { filter: blur(0.031em) }
  35% { filter: blur(0.015em) }
  36.1429% { filter: blur(0.012em) }
  37.2857% { filter: blur(0.011em) }
  42.7143% { filter: blur(0.011em) }
  43.8571% { filter: blur(0em) }
  52.7143% { filter: blur(0em) }
  53.8571% { filter: blur(0.003em) }
  55% { filter: blur(0.008em) }
  56.1429% { filter: blur(0.01em) }
  57.2857% { filter: blur(0.011em) }
  62.7143% { filter: blur(0.011em) }
  63.8571% { filter: blur(0.093em) }
  65% { filter: blur(0.149em) }
  66.1429% { filter: blur(0.17em) }
  67.2857% { filter: blur(0.175em) }
  72.7143% { filter: blur(0.175em) }
  73.8571% { filter: blur(0.588em) }
  75% { filter: blur(0.797em) }
  76.1429% { filter: blur(0.869em) }
  77.2857% { filter: blur(0.886em) }
  82.7143% { filter: blur(0.886em) }
  83.8571% { filter: blur(2.066em) }
  85% { filter: blur(2.586em) }
  86.1429% { filter: blur(2.76em) }
  87.2857% { filter: blur(2.8em) }
  92.7143% { filter: blur(2.8em) }
  93.8571% { filter: blur(3.5em) }
  100% { filter: blur(3.5em) }
}
@keyframes rack-dot {
  0% { transform: translateY(78em) }
  3.8571% { transform: translateY(78em) }
  5% { transform: translateY(67.456em) }
  6.1429% { transform: translateY(63.3em) }
  7.2857% { transform: translateY(62.4em) }
  12.7143% { transform: translateY(62.4em) }
  13.8571% { transform: translateY(28.648em) }
  15% { transform: translateY(21.898em) }
  16.1429% { transform: translateY(20.124em) }
  17.2857% { transform: translateY(19.744em) }
  22.7143% { transform: translateY(19.744em) }
  23.8571% { transform: translateY(6.734em) }
  25% { transform: translateY(4.551em) }
  26.1429% { transform: translateY(4.013em) }
  27.2857% { transform: translateY(3.9em) }
  32.7143% { transform: translateY(3.9em) }
  33.8571% { transform: translateY(0.68em) }
  35% { transform: translateY(0.33em) }
  36.1429% { transform: translateY(0.258em) }
  37.2857% { transform: translateY(0.244em) }
  42.7143% { transform: translateY(0.244em) }
  43.8571% { transform: translateY(0.002em) }
  45% { transform: translateY(0em) }
  52.7143% { transform: translateY(0em) }
  53.8571% { transform: translateY(0.061em) }
  55% { transform: translateY(0.176em) }
  56.1429% { transform: translateY(0.23em) }
  57.2857% { transform: translateY(0.244em) }
  62.7143% { transform: translateY(0.244em) }
  63.8571% { transform: translateY(2.072em) }
  65% { transform: translateY(3.322em) }
  66.1429% { transform: translateY(3.789em) }
  67.2857% { transform: translateY(3.9em) }
  72.7143% { transform: translateY(3.9em) }
  73.8571% { transform: translateY(13.097em) }
  75% { transform: translateY(17.753em) }
  76.1429% { transform: translateY(19.369em) }
  77.2857% { transform: translateY(19.744em) }
  82.7143% { transform: translateY(19.744em) }
  83.8571% { transform: translateY(46.05em) }
  85% { transform: translateY(57.634em) }
  86.1429% { transform: translateY(61.509em) }
  87.2857% { transform: translateY(62.4em) }
  92.7143% { transform: translateY(62.4em) }
  93.8571% { transform: translateY(78em) }
  100% { transform: translateY(78em) }
}
@keyframes rack-ring {
  0% { opacity: 0 }
  42.7143% { opacity: 0 }
  45% { opacity: 1 }
  52.7143% { opacity: 1 }
  55% { opacity: 0 }
  100% { opacity: 0 }
}
/* @end generated */

/* ---- narrow ------------------------------------------------------------- */
@media (max-width: 620px) {
  :root { --fs-body: 1rem; }
  h1 { letter-spacing: -.035em; }
  .eyebrow { letter-spacing: .3em; text-indent: .3em; }
  .themectl button { padding: 6px 8px; }
  .keys { gap: 8px 12px; }
  .kill { padding: 18px; }
  .src { padding-left: 12px; }
  /* The stage is a cropped window here and the keyboard hints next to this are
     hints about a keyboard the reader does not have. The one control that gives
     them the interface at a size they can read gets to look like a control. */
  .keys .fs {
    flex: none; color: var(--text-1); text-decoration: none;
    background: var(--plate); border: 1px solid var(--edge-ctl);
    border-radius: 999px; padding: 10px 16px;
  }
}
/* The lockup is 166 px of nowrap and the theme control is 160 px that must not
   shrink; a 320 px viewport has 280 px between the gutters. The mono prefix is
   the part that repeats what the masthead already said. */
@media (max-width: 440px) {
  .topbar .wm .o { display: none; }
}

/* ---- prefers-reduced-motion -------------------------------------------- */
/* The whole choreography, off. Not slowed, not shortened: the entrance
 * animations never run and their end state is the initial state, the scroll
 * rack never arms (the observer bails before it marks anything), and the live
 * dot stops breathing. What is left is the composition with nothing in
 * flight, which is what the page has to look like anyway.
 *
 * The three .rig figures are deliberately NOT covered here — they handle
 * reduce themselves, above, by pausing and inverting their own button so a
 * visitor who wants the mechanism can still opt into it. */
@media (prefers-reduced-motion: reduce) {
  /* .anim.rack and .anim.rise re-declare animation-name at two classes of
     specificity, which beats a bare .anim here whatever the source order. All
     three selectors, or the page's two loudest entry animations keep running. */
  .anim, .anim.rack, .anim.rise { animation: none; }
  .reveal, .reveal.in { opacity: 1; transform: none; filter: none; transition: none; }
  .stagebox.is-live .stage-cue .dot { animation: none; }
  .topbar::before, .topbar .mark,
  .stage-frame, .stage-spill, .stage-cue, .stage-cue .dot,
  .drive button, .themectl button, a { transition: none; }
}
