/* =========================================================================
   mana box — base layer.  ARCHITECT-OWNED.  Section builders: do not edit.

   This file exists so that five people building five sections in parallel
   produce one page instead of five. Everything measurable in the rubric —
   the type scale, the spacing unit, the nine colours, the single easing
   curve — is decided here, once.

   RULES FOR SECTION FILES
     1. No raw hex, rgb() or hsl() anywhere. Use var(--c-*) only.
        Need transparency? color-mix(in srgb, var(--c-duck) 88%, transparent)
        or rgb(var(--rgb-duck) / .88). Never #000, #fff, or rgba(0,0,0,·).
     2. No raw px for vertical rhythm. Use var(--s-*) / var(--section-pad).
     3. No new font-size values. Use var(--t-*). Six sizes exist; that is all
        the page gets.
     4. No new easing curves and no new durations. var(--ease), var(--dur).
     5. Every transition must be inside a
        @media (prefers-reduced-motion: no-preference) guard, or use the
        .motion-safe helper. The page has to be still for people who ask.
   ========================================================================= */

/* ---------------------------------------------------------------- fonts -- */

@font-face {
  font-family: "Space Grotesk";
  src: url("/fonts/space-grotesk-700.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* YOUNG SERIF — the display face, chosen by the owner 2026-08-14 from a
   nine-way comparison set in this page's own headlines on this page's own
   grounds.

   IT SHIPS EXACTLY ONE WEIGHT: 400. Google serves no other, verified by
   requesting 300/500/600/700/800/900 and getting HTTP 400 for every one. That
   is the whole reason this block carries `font-weight: 400` and not a range:
   declaring a range invites the browser to SYNTHESISE a bold, which is what an
   earlier comparison sheet accidentally showed the owner — a smeared, filled-
   counter fake that looked like a different and heavier typeface than the one
   that actually exists. Every `font-weight: 700` that currently pairs with
   --f-display has to become 400 for the same reason.

   `font-synthesis: none` is set on :root below so that a miss can never be
   silent: an un-authored weight renders LIGHT, which is visible, rather than
   smeared, which reads as a design choice. */
@font-face {
  font-family: "Young Serif";
  src: url("/fonts/young-serif-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* Archivo is a variable face, 100-900. Introspected on disk, not assumed.
   (brand/fonts/manrope.woff2, which _brief.md B13 recommends, is a *static
   ExtraBold* — usWeightClass 800, no fvar — so it cannot carry body copy.
   Archivo is the other web-ready neutral grotesque already in brand/fonts/.) */
@font-face {
  font-family: "Archivo";
  src: url("/fonts/archivo.woff2") format("woff2-variations"),
       url("/fonts/archivo.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* Shipped, deliberately unused. See "two families" note below. Swap it into
   --f-spec if the owner prefers the three-face setup of _brief.md B14. */
@font-face {
  font-family: "IBM Plex Mono";
  src: url("/fonts/ibm-plex-mono-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

/* --------------------------------------------------------------- tokens -- */

:root {
  /* --- colour: Werner's, nine values, no tenth -------------------------- */
  --c-duck:      #33431E;  /* L  61  primary ground, DAY                    */
  --c-prussian:  #1C1949;  /* L  29  primary ground, NIGHT                  */
  --c-ink:       #252024;  /* L  33  deep ground; type on light             */
  --c-straw:     #F0D696;  /* L 215  primary light — body type on dark      */
  --c-snow:      #F1E9CD;  /* L 233  brightest — HEADLINES ONLY             */
  --c-tile:      #C76B4A;  /* L 124  ACCENT ONLY. Never a surface.          */
  --c-olive:     #67765B;  /* L 113  rules, borders, secondary surface      */
  --c-grey:      #BAB191;  /* L 177  secondary text on dark                 */
  --c-wood:      #C39E6D;  /* L 162  warm neutral, optional                 */

  /* same nine as channel triplets, for rgb(var(--rgb-x) / alpha) */
  --rgb-duck:     51  67  30;
  --rgb-prussian: 28  25  73;
  --rgb-ink:      37  32  36;
  --rgb-straw:   240 214 150;
  --rgb-snow:    241 233 205;
  --rgb-tile:    199 107  74;
  --rgb-olive:   103 118  91;
  --rgb-grey:    186 177 145;
  --rgb-wood:    195 158 109;

  /* Product photography sits at ~210 luminance. A packshot may only sit on a
     ground >= 60 points away: prussian (181), ink (177), duck (149), olive
     (97). Straw, snow, grey and wood are ILLEGAL grounds for a packshot. */

  /* --- type: three families, six sizes, three weights ------------------- */
  /* YOUNG SERIF, owner-chosen 2026-08-14. ONE WEIGHT: 400. Every rule that
     pairs --f-display with font-weight:700 was swept to 400 in the same pass;
     `font-synthesis: none` below makes a missed one render LIGHT rather than
     smeared, so the failure mode is visible instead of plausible. */
  --f-display:  "Young Serif", Georgia, "Times New Roman", serif;
  --f-body:     "Archivo", ui-sans-serif, system-ui, sans-serif;
  --f-spec:     "Archivo", ui-sans-serif, system-ui, sans-serif;

  /* THE THIRD FACE, AND WHY IT IS WORTH THE RUBRIC POINT. 2026-08-14.
     The note below argued for two families on the grounds that the rubric
     scores <=2 at 5 and 3 at 4. That argument was correct for a page with two
     things to say. This page now has three: the world voice (display serif),
     the label voice (.spec, tracked caps), and THE TRUE LINE — one factual
     sentence per section, about the real SuperCollider patch, which must never
     be mistaken for either of the other two.

     It cannot be .spec. Tracked caps is already the label register, and
     REFERENCE-LAW L3 permits ONE medium tracked-caps run per section — putting
     a true line in every section would have failed L3 across the entire page
     and `tools/check-law.mjs` would have caught it as six separate violations.
     It cannot be --f-body either: set in Archivo at 12px it reads as a caption
     of the paragraph above it, which is exactly the confusion the register
     exists to prevent.

     So: lowercase mono, its own class, one point of rubric criterion 6 spent
     deliberately. The font was already downloaded and shipping unused — the
     housekeeping pass on 2026-08-14 kept `ibm-plex-mono-500.woff2` as a
     provision rather than deleting it as an orphan. This is the provision being
     called in; no new bytes over the wire. */
  --f-mono:     "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* THE WORDMARK GETS ITS OWN TOKEN, AND THIS IS THE POINT OF IT.
     `.wordmark` used to read `font-family: var(--f-display)`, so the display
     face and the logotype were the same variable. brand.md locks the wordmark
     to Space Grotesk at 0.22em tracking — "never tightened, never uppercased" —
     so pointing --f-display at Young Serif would have silently rewritten the
     brand's own logotype everywhere it is set in live text (the sticky tab, the
     ticker, §12's byline). Splitting the token is what makes the display swap
     possible without touching the mark. The §12 slab and the SVG lockups are
     outlined artwork and were never affected either way. */
  --f-wordmark: "Space Grotesk", ui-sans-serif, system-ui, sans-serif;

  /* SUPERSEDED 2026-08-14 by the --f-mono note above; kept because it records
     why the page ran on two faces for as long as it did. Criterion 6 accepts
     "mono OR tabular-lining figures" for the spec register, and Archivo has
     tnum, so .spec never needed a third face — and still doesn't. The mono is
     bought for `.true`, not for `.spec`. brand.md lists IBM Plex Mono as a
     *candidate*, so nothing locked is being broken by promoting it.
     The one glyph Archivo lacks is the infinity sign — wrap it in
     <span class="glyph">, which borrows the display face. */

  --t-display: clamp(2.125rem, 1.35rem + 2.4vw, 2.9375rem); /* 34 -> 47px  */
  --t-h2:      clamp(1.625rem, 1.32rem + 0.95vw, 1.9375rem);/* 26 -> 31px  */
  --t-h3:      1.3125rem;                                   /* 21px        */
  --t-body:    1.0625rem;                                   /* 17px        */
  --t-small:   0.875rem;                                    /* 14px        */
  --t-micro:   0.75rem;                                     /* 12px        */
  /* display : body = 47 : 17 = 2.76 : 1, and 47px is 3.3% of a 1440 viewport */

  /* --- v2: the scale extremes ------------------------------------------
     _brief2.md item 17 and SITE-V2-PLAN rule 5: "enormous display type next to
     10px monospace, with nothing in between." The six sizes above top out at
     2.76x body, which is the whole reason v1 read as one template repeated —
     every section had the same 47/21/17/14/12 ladder and therefore the same
     silhouette. Three more tokens exist so that THREE sections, and only three,
     can leave that ladder. They are ARCHITECT-OWNED and each has exactly one
     legal consumer:

       --t-mega   §03 the claim, and nothing else.       112/17 = 6.6x body
       --t-slab   §12 the footer wordmark, and nothing else. Bleeds off both
                  edges by design, so it is allowed to overflow its box.
       --t-nano   the small pole of the extreme — ticker, callout keys, score
                  boxes, colophon. 10px. Never body copy, never a sentence.

     A section file still declares NO font-size literal. If a section needs a
     size that is not one of these nine tokens, that is a conversation with the
     architect, not a local override. */
  --t-mega:  clamp(3.25rem, 0.6rem + 8.8vw, 7rem);        /*  52 -> 112px */
  --t-slab:  clamp(5rem, -2.4rem + 24vw, 19rem);          /*  80 -> 304px */
  --t-nano:  0.625rem;                                    /*        10px  */

  /* --t-hero  §01 the hero headline, and nothing else.   62/17 = 3.7x body
     PROMOTED BY THE INTEGRATOR, not invented here. `.hero-line` shipped with
     this expression inline and a note in 2-openers.html addressed to the
     architect: "this is a tenth size on a page whose base layer says there are
     nine … if it is going to stay it belongs in base.css." It stays, so it is
     here. An audit of every rendered font-size on the page now returns nine
     tokens and no strays, which is the only version of that claim worth making.

     It is a derivation, not a taste value. The headline sits in the reserve to
     the LEFT of the product in the footage, so the type and the crop are one
     decision: the column is 0.4D - 5vw - 16 below --page and -0.1D + 560 above
     it, and the line has to fill that column without wrapping. The two regimes
     cross exactly at 1280, so min() of the pair is the whole curve.
       1024 -> 50.2   1280 -> 63.6   1440 -> 62.1   1920 -> 54.9
     No floor, deliberately: past ~2600px the reserve is genuinely narrower
     than the line needs, and shrinking beats wrapping. */
  --t-hero: min(calc(5.2239vw - 3.284px), calc(83.582px - 1.4925vw));

  /* line-height is monotone — never tighter as the type gets smaller */
  --lh-display: 1.05;
  --lh-h2:      1.12;
  --lh-h3:      1.25;
  --lh-body:    1.5;

  /* tracking is monotone the other way — tighter as the type gets larger */
  --tr-display: -0.03em;
  --tr-h2:      -0.02em;
  --tr-h3:      -0.01em;
  --tr-body:     0em;
  --tr-small:    0.005em;
  --tr-label:    0.14em;
  --tr-wordmark: 0.22em;  /* load-bearing. brand.md. Do not tighten. */

  /* --- space: one base unit of 8px, seven steps ------------------------- */
  --s-1:  0.5rem;   /*   8 */
  --s-2:  1rem;     /*  16 */
  --s-3:  1.5rem;   /*  24 */
  --s-4:  2.5rem;   /*  40 */
  --s-5:  4rem;     /*  64 */
  --s-6:  6.5rem;   /* 104 */
  --s-7: 10.5rem;   /* 168 */

  /* section separation = ~12x body line-height at desktop, proportional down */
  --section-pad: clamp(5.5rem, 11vw, 10rem);  /* 88 -> 160px */

  --page:   1280px;   /* the scrub is 1280 source px. Nothing exceeds it. */
  --narrow: 720px;
  --gutter: clamp(1.25rem, 5vw, 4rem);
  --measure: 62ch;

  /* THE PAGE DATUM. One number for every contained section's left and right
     edge. `.wrap` reaches the same place by max-width + auto margins + gutter,
     but anything that is NOT a .wrap child (a bled rail, an aside, a footer
     column) used to pick its own number, which is how the page ended up with
     seven left datums and eleven right ones. Sections that deliberately bleed
     still bleed; everything that does not, starts here.
     Was defined locally inside 2-openers.html; promoted so it is shared. */
  /* 100vw, NOT 100%. A custom property inherits its *expression*, not its
     computed value, so a percentage re-resolves against whatever element
     finally uses it. On a full-width .wrap that is the viewport and the
     maths is right; on a 584 px grid column (100% - 1280px) goes negative,
     max() clamps it to 0, and --edge silently collapses to a bare gutter.
     That is how .turn-aside and .foot-byline kept landing on 64. */
  --edge: calc(max(0px, (100vw - var(--page)) / 2) + var(--gutter));

  /* --- radius: two values, and only two ---------------------------------
     REFERENCE-LAW L10: the set is bimodal. Rounded brands sit at 10-24px
     (AIRSIGN 14, Distillo 24, the RTD carousel 14); hard-cornered brands sit at
     ~0 (ode, reMarkable) and are unmistakably premium. There is no middle, and
     what is fatal is being between the two — which is what v2 was, at 3px.

     The owner's call settles which pole: "almost all the references that I
     actually showed you have like rounded corners. It's photo driven."

     --r-card  every photograph, card, plate and panel. One value.
     --r-pill  controls only. Both poles of the set run pill CTAs, so this is
               not a second radius so much as the shape of a button. */
  --r-card: 14px;
  --r-pill: 999px;

  /* --- motion: one curve, three durations ------------------------------- */
  --ease:      cubic-bezier(0.2, 0.6, 0.2, 1);
  --dur:       240ms;
  --dur-fast:  140ms;
  --dur-slow:  420ms;

  --rule: 1px solid rgb(var(--rgb-olive) / 0.5);
}

/* The hero's second regime. Below 5:4 the scrub reframes to portrait and the
   headline leaves the footage for the duck-green seat beneath it, so the column
   stops being "the reserve beside the product" and becomes the whole frame less
   two gutters: font = 13.43vw - 2.3px, capped so a portrait tablet does not get
   a 110px headline out of the same rule. Kept in the base layer beside the
   landscape regime rather than as a font-size override inside the hero's own
   media query — one token, one place, both regimes. */
@media (max-aspect-ratio: 5 / 4) {
  :root { --t-hero: min(3.25rem, calc(13.43vw - 2.3px)); }
}

/* ----------------------------------------------------------------- reset - */

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

/* NO FAKED WEIGHTS OR SLANTS ANYWHERE. The display face ships a single 400,
   so any surviving `font-weight: 700` against it would otherwise be smeared
   into a fake bold by the browser — which is precisely the artefact that made
   an earlier comparison sheet show the owner a heavier typeface than exists.
   With synthesis off, a miss is under-weight and obvious. */
html { font-synthesis: none; -webkit-font-synthesis: none; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: auto;
  /* v2: `overflow-x: clip` on BODY alone is not enough. Several v2 sections
     deliberately run wider than the viewport — §07's owner row, §08's press
     rail, §12's wordmark — and body-level clipping does not stop the root
     element from scrolling: measured at 1024 the document scrolled 172 px
     sideways with body already clipped. Clipping the root as well kills the
     sideways scroll while leaving `position: fixed` (the sticky bar) working,
     which `overflow: hidden` here would not reliably do. */
  overflow-x: clip;
}

body {
  margin: 0;
  background: var(--c-duck);
  color: var(--c-straw);
  font-family: var(--f-body);
  font-weight: 400;
  font-size: var(--t-body);
  line-height: var(--lh-body);
  letter-spacing: var(--tr-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;   /* a horizontal scrollbar at any width is a 1 on the rubric */
}

img, video, canvas, svg { display: block; max-width: 100%; }
img, video { height: auto; }

h1, h2, h3, p, figure, blockquote, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }

a { color: inherit; }

:focus-visible {
  outline: 2px solid var(--c-tile);
  outline-offset: 3px;
}

::selection {
  background: rgb(var(--rgb-tile) / 0.85);
  color: var(--c-snow);
}

/* ------------------------------------------------------------- typography */

.display, h1 {
  font-family: var(--f-display);
  font-weight: 400;
  font-size: var(--t-display);
  line-height: var(--lh-display);
  letter-spacing: var(--tr-display);
  color: var(--c-snow);
  text-wrap: balance;
}

.h2, h2 {
  font-family: var(--f-display);
  font-weight: 400;
  font-size: var(--t-h2);
  line-height: var(--lh-h2);
  letter-spacing: var(--tr-h2);
  color: var(--c-snow);
  text-wrap: balance;
}

.h3, h3 {
  font-family: var(--f-display);
  font-weight: 400;
  font-size: var(--t-h3);
  line-height: var(--lh-h3);
  letter-spacing: var(--tr-h3);
  color: var(--c-snow);
}

p { max-width: var(--measure); }

.lede { font-size: var(--t-h3); line-height: var(--lh-h3); color: var(--c-straw); }
.small { font-size: var(--t-small); letter-spacing: var(--tr-small); color: var(--c-grey); }

/* the small pole of the scale extreme. Same register as .spec, one step down.
   Labels, keys, ticker words, score attributions. Never a sentence of body. */
.nano {
  font-family: var(--f-spec);
  font-weight: 500;
  font-size: var(--t-nano);
  line-height: var(--lh-body);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
  color: var(--c-grey);
}

/* the technical register: uppercase, wide-tracked, tabular figures.
   Reserved for specs, labels, eyebrows and callouts. Nothing else. */
.spec, .label {
  font-family: var(--f-spec);
  font-weight: 500;
  font-size: var(--t-micro);
  line-height: var(--lh-body);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
  color: var(--c-grey);
}

/* THE TRUE LINE. 2026-08-14. One per section, and the rules are absolute:
   it is always factual, always about the real SuperCollider patch, and it
   never acknowledges the fiction it is sitting next to. It is a museum wall
   card beside a prop — "Reconstruction. Oak and brass. The mechanism is
   original." The card does not apologise for the prop; it tells you which part
   to be impressed by, and the object survives the honesty intact.

   LOWERCASE, and that is load-bearing twice over. Once for L3: caps here would
   be a second tracked-caps run in every section it appears in, which the law
   caps at one. Once for tone: caps would make it an announcement, and the whole
   effect depends on this register sounding like it is not trying to convince
   you of anything.

   THE MONO IS GONE, 2026-08-16, OWNER'S CALL: "turn this back into the old
   original font for the text that it replaced. this font doesn't match this."

   He is right, and the reason is worth writing down because the mistake was
   mine and it was a category error. IBM Plex Mono earns its place on IDENTIFIERS
   — `volume`, `arpRate`, `/ambientbox/set mode 1` — where the monospacing is
   telling you "this is a literal string a computer reads". These lines are not
   identifiers. They are sentences. Setting three paragraphs of English prose in
   a 12px monospace face made them read as terminal output pasted into a
   magazine, and on a page whose type is otherwise Young Serif and Archivo it
   read as a foreign object rather than as a second voice.

   The distinction survives without a third face. What separated the true line
   from the fiction was never the FONT — it was the size, the colour and the
   position. Those all still hold. The register still reads; it just reads as
   part of the same document now.

   Each section restates the exact treatment of the copy it replaced (see
   `.claim-true`, `.tear-true`, `.foot-true`), so what ships is the body face at
   the size that block always used. --f-mono survives for the manual's terms in
   §turn, which is the identifier case above.

   NOT to be used in §press, §owners or §creators. Those sections are the world,
   and captioning an invented review with a fact is the cheap version of this
   idea told three times. They are covered once, in the colophon. */
.true {
  font-family: var(--f-body);
  font-weight: 400;
  font-size: var(--t-body);
  line-height: var(--lh-body);
  letter-spacing: var(--tr-body);
  text-transform: none;
  color: var(--c-straw);
  max-width: 46ch;
  text-wrap: pretty;
}

/* Archivo has no U+221E. Borrow one glyph from the display face. */
/* 400, not 700 — same single-weight reason as everything else on the display
   face. NOTE: this class exists to borrow a glyph Archivo lacks; if Young
   Serif lacks it too the stack falls through to Georgia, which carries it.
   That is a graceful degradation, not a break, but it is a THIRD face on
   screen for one character — checked below. */
.glyph { font-family: var(--f-display); font-weight: 400; }

.wordmark {
  /* --f-wordmark, NOT --f-display. The logotype is Space Grotesk and stays
     Space Grotesk whatever the display face becomes. brand.md, locked. */
  font-family: var(--f-wordmark);
  font-weight: 700;
  text-transform: lowercase;
  letter-spacing: var(--tr-wordmark);
}

/* ---------------------------------------------------------------- layout - */

.section { padding-block: var(--section-pad); }

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

.wrap-narrow {
  width: 100%;
  max-width: var(--narrow);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* full-bleed escape hatch for images that should be cropped by the viewport */
.bleed { width: 100%; margin-inline: 0; padding-inline: 0; max-width: none; }

/* --- grounds. Mode is carried by ground, never by swapping the accent. --- */
.ground-day    { background: var(--c-duck); }
.ground-night  { background: var(--c-prussian); }
.ground-deep   { background: var(--c-ink); }
.ground-day, .ground-night, .ground-deep { color: var(--c-straw); }

/* the one accent, as a mark only — never a surface */
.accent { color: var(--c-tile); }
.rule   { border: 0; border-top: var(--rule); }

/* 44px MINIMUM, and this one belongs to the architect.
   The critique measured it at 164 x 34 — 9.5px under the WCAG 2.5.8 floor —
   and the builder who owned the other undersized controls correctly refused to
   fix it, because it is declared in shell.html and styled here, both outside
   that builder's ownership. It is the first control a keyboard user meets, so
   it is a poor one to leave short. `min-height` plus a flex centre rather than
   more padding: padding would have moved the text off the optical centre of the
   plate, and this element's whole job is to be legible for the half-second it
   is on screen. */
.skip-link {
  position: absolute;
  left: var(--gutter);
  top: var(--s-2);
  z-index: 10;
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  padding: var(--s-1) var(--s-2);
  background: var(--c-ink);
  color: var(--c-straw);
  text-decoration: none;
  transform: translateY(-200%);
}
.skip-link:focus-visible { transform: none; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* ---------------------------------------------------------------- motion - */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* Sections that want a transition should apply it inside this helper so the
   reduced-motion contract is honoured by construction, not by memory. */
@media (prefers-reduced-motion: no-preference) {
  .motion-safe { transition: all var(--dur) var(--ease); }
}
