/* ============================================================
   KDB design tokens
   ------------------------------------------------------------
   Three tiers. Tier 1 primitives, Tier 2 semantics, Tier 3
   components. PRODUCTS CONSUME TIER 3, or Tier 2 where no
   component token fits. A page reaching into Tier 1 is the
   defect js/audit.js exists to catch — primitives are raw
   material, and a page that names one has decided something
   the system was supposed to have decided already.

   Complete as of Phase 8. Type D1, colour D2–D4, space D5,
   line and shape D6/D7, atmosphere D8, drawing law D9.

   Naming grammar: --kdb-<category>-<role>-<variant>
   ============================================================ */

/* ---------- Tier 1 · primitives ---------------------------- */

:root {
  /* Family. One family, split by width. */
  --kdb-family-archivo: "Archivo", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --kdb-family-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace; /* tooling only, not brand */

  /* Width axis. Expanded is a display property — see width law. */
  --kdb-wdth-figure: 62;   /* condensed — numerals at slide scale only. See below. */
  --kdb-wdth-text: 100;
  --kdb-wdth-head: 108;
  --kdb-wdth-display: 112;
  --kdb-wdth-lockup: 115;

  /* Weight axis. Three ship. No others. */
  --kdb-wght-regular: 400;
  --kdb-wght-medium: 600;
  --kdb-wght-heavy: 700;

  /* Size ramp. Hand-tuned, not ratio-derived. Nine steps, and
     nine is the ceiling — every extra step is a future argument. */
  --kdb-size-lockup: clamp(3rem, 11vw, 9.5rem);
  --kdb-size-display: clamp(2.25rem, 5.5vw, 4.5rem);
  --kdb-size-head: clamp(1.5rem, 3vw, 2.5rem);
  --kdb-size-figure: clamp(2rem, 4vw, 3.1rem);
  --kdb-size-deck: clamp(1.05rem, 1.6vw, 1.25rem);
  --kdb-size-body: 1rem;
  --kdb-size-small: 0.875rem;
  --kdb-size-caption: 0.8125rem;
  --kdb-size-label: 0.6875rem;
  --kdb-size-micro: 0.625rem;

  /* Line height. Tighter as size grows. */
  --kdb-leading-lockup: 0.9;
  --kdb-leading-display: 0.95;
  --kdb-leading-head: 1.06;
  --kdb-leading-figure: 1;
  --kdb-leading-deck: 1.42;
  --kdb-leading-body: 1.62;
  --kdb-leading-tight: 1.5;
  --kdb-leading-flat: 1.2;

  /* Tracking. Negative as size grows; positive for uppercase. */
  --kdb-track-lockup: -0.028em;
  --kdb-track-display: -0.024em;
  --kdb-track-head: -0.018em;
  --kdb-track-deck: -0.006em;
  --kdb-track-body: -0.004em;
  --kdb-track-caps: 0.12em;
  --kdb-track-caps-wide: 0.14em;

  /* Measure ceilings, in ch. */
  --kdb-measure-deck: 34em;
  --kdb-measure-body: 36em;
  --kdb-measure-caption: 42em;
}

/* ---------- Tier 1 · colour --------------------------------- */

:root {
  /* Ground. Cool hair — a degree of blue in the paper, so it reads as
     daylight rather than lamp. Locked D2. */
  --kdb-ground: #fcfdfe;

  /* Neutral ramp, cool, hue ≈ 215. Eight steps, ground to ink. Locked D2.
     Step numbers are perceptual darkness, not lightness — 95 is the ink. */
  --kdb-neutral-00: #ffffff;
  --kdb-neutral-05: #f7f8fa;
  --kdb-neutral-10: #edeff3;
  --kdb-neutral-20: #d9dde3;
  --kdb-neutral-40: #a8afb9;
  --kdb-neutral-60: #6b7480;
  --kdb-neutral-80: #3a424d;
  --kdb-neutral-95: #14181d;

  /* Accent. Ice, hue ≈ 202. Locked D3.
     Contrast against ground is noted because the two ratio-law modes take
     two different steps — see the accent semantics below. */
  --kdb-ice-10: #eaf3f8; /* 1.10:1 — wash only */
  --kdb-ice-20: #cfe5f0; /* 1.28:1 */
  --kdb-ice-30: #a9d2e7; /* 1.58:1 */
  --kdb-ice-45: #74b4d8; /* 2.23:1 — the signature value. Atmosphere only. */
  --kdb-ice-60: #3a8dbe; /* 3.60:1 — floor for a meaningful mark */
  --kdb-ice-70: #2c7099; /* 5.31:1 */
  --kdb-ice-80: #215772; /* 7.72:1 — the only step safe for body text */
  --kdb-ice-90: #173c50; /* 11.46:1 */

  /* Depth steps. Added for D8 because the atmosphere needs values below
     ice-90 and was otherwise reaching for floating hexes that drifted 13°
     toward violet and lost saturation.

     Not eyeballed. The eight steps above measure as a near-perfect HSL
     line — hue 201.16 (sd 0.93), saturation 54.24 (sd 2.06) — so these
     three are generated on that same line and only lightness moves. */
  --kdb-ice-85: #1d4860; /*  9.60:1 — deep mass. Replaces a 78%-saturation navy */
  --kdb-ice-95: #0d212b; /* 16.24:1 — the floor of a field */
  --kdb-ice-98: #061016; /* 18.86:1 — near-black, still Ice and not a blue-grey */

  /* Club colours. Revised D9b: a club colour is allowed where the drawing
     represents ONE club, because there the colour is a fact about the club
     rather than an encoding.

     It is NOT allowed as a way of telling clubs apart in a single view, and
     that is a measurement rather than a preference. Four of the seven are
     the same hue — Genk 206, Napoli 197, City 207, Chelsea 212 — so side by
     side they do not separate: City against Napoli is 1.21:1 and Genk
     against Chelsea is 1.54:1. Ice sits at 202, in the middle of all four.

     One canonical hex each. The old builds carried two Genks (#0066b2 in
     the atlas, #0057b8 in the games grid) because every object picked its
     own, and that is the thing these tokens exist to stop. */
  --kdb-club-genk: #0066b2;      /* 5.82:1 */
  --kdb-club-bremen: #1d9053;    /* 3.99:1 */
  --kdb-club-wolfsburg: #65b32e; /* 2.56:1 — display only */
  --kdb-club-chelsea: #034694;   /* 8.94:1 */
  --kdb-club-city: #6cabdd;      /* 2.42:1 — display only */
  --kdb-club-napoli: #12a0d7;    /* 2.93:1 — display only */
  --kdb-club-belgium: #c8102e;   /* 5.78:1 */

  /* Three of them fail the 3:1 non-text floor against the ground, so they
     split the same way Ice did in D3: the true colour for display fills,
     and a mark tier for anything load-bearing. Hue and saturation are held
     exactly; only lightness moves, and only as far as 3:1. */
  --kdb-club-wolfsburg-mark: #5da42a; /* 3.03:1 — hue 95 held */
  --kdb-club-city-mark: #4d99d6;      /* 3.01:1 — hue 207 held */
  --kdb-club-napoli-mark: #129ed4;    /* 3.00:1 — hue 197 held */
}

/* ---------- Tier 1 · space & grid --------------------------- */

:root {
  /* Base 8. The 4px hair step is component-internal only — see D5a.
     Non-linear by design: 8s at the small end where differences are
     legible, ~x1.6 at the large end where they are not. */
  --kdb-space-hair: 4px;    /* inside a component, between elements that belong together */
  --kdb-space-tight: 8px;
  --kdb-space-snug: 16px;
  --kdb-space-base: 24px;
  --kdb-space-loose: 40px;
  /* --- boundary: below is component-internal, above is layout --- */
  --kdb-space-block: 64px;    /* related things inside one beat */
  --kdb-space-group: 104px;   /* a new subject, same chapter */
  --kdb-space-section: 168px; /* a new beat */
  --kdb-space-chapter: 272px; /* a new half of the site */

  /* Grid. Sixteen columns. The two-part default is 10/6 because that
     reduces to 5:3, the proportion of the logo plate.

     The count is a TOKEN because it changes with the viewport, and
     the breakpoints where it changes are derived rather than
     chosen — see the responsive block in system.css. Everything
     that places content reads this token, so a layout never
     hard-codes sixteen. */
  --kdb-grid-columns: 16;
  --kdb-grid-gutter: 20px;
  --kdb-grid-margin: clamp(20px, 4vw, 64px);
  --kdb-grid-container: 1440px;
}

/* ---------- Motion · D9g ----------------------------------- */

/* One duration set, one curve. These mirror KDB_MOTION in
   js/draw.js exactly — the drawn layer and the CSS layer must
   not each keep their own timings, which is how a reveal ends
   up 300ms in one object and 500ms in the next. */

:root {
  --kdb-motion-tick: 120ms;    /* a value changing */
  --kdb-motion-draw: 420ms;    /* one mark arriving */
  --kdb-motion-sweep: 900ms;   /* a whole series arriving */
  --kdb-motion-stagger: 18ms;  /* between marks in a series */
  --kdb-motion-curve: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* A reveal is a draw. Reduced motion is a fallback, not a
   removal: the drawing arrives complete on the first frame. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --kdb-motion-tick: 0ms;
    --kdb-motion-draw: 0ms;
    --kdb-motion-sweep: 0ms;
    --kdb-motion-stagger: 0ms;
  }
}

/* ---------- Tier 2 · semantics ----------------------------- */

:root {
  --kdb-text-primary: var(--kdb-neutral-95);
  --kdb-text-secondary: var(--kdb-neutral-80);
  --kdb-text-muted: var(--kdb-neutral-60);
  --kdb-text-inverse: var(--kdb-neutral-00);
  --kdb-surface-ground: var(--kdb-ground);
  --kdb-surface-sunken: var(--kdb-neutral-05);
  --kdb-border-hairline: var(--kdb-neutral-20);
  --kdb-border-faint: var(--kdb-neutral-10);
  --kdb-border-strong: var(--kdb-neutral-95);

  /* The ratio law is two modes, so the accent is two tokens. Using the wrong
     one is the failure this split exists to prevent: --kdb-accent-mark is the
     only accent that may carry meaning, --kdb-accent-atmos is the only accent
     that may cover a field. Neither substitutes for the other. */
  --kdb-accent-mark: var(--kdb-ice-60);
  --kdb-accent-atmos: var(--kdb-ice-45);
  --kdb-accent-text: var(--kdb-ice-80);
}


/* ---------- Tier 3 · components ----------------------------- */

/* The layer the pages actually name. Every value here resolves to
   Tier 2 or Tier 1 — nothing new is introduced, and that is the
   point: a component token is a DECISION ABOUT WHERE, not a new
   colour or a new size. If a page needs a value that is not here,
   the system has a hole and the fix is a token, never a local
   override.

   Named for the job, not the appearance. --kdb-plot-axis rather
   than --kdb-line-thin, so a change of mind about axes moves one
   token instead of being hunted through five files. */

:root {
  /* Page shell. The container, its margins, and the rhythm
     between beats — D5, D5e. */
  --kdb-page-max: var(--kdb-grid-container);
  --kdb-page-margin: var(--kdb-grid-margin);
  --kdb-page-gutter: var(--kdb-grid-gutter);
  --kdb-page-ground: var(--kdb-surface-ground);

  /* Section rhythm. Four gaps, D5e — nothing between them. */
  --kdb-section-gap: var(--kdb-space-section);
  --kdb-group-gap: var(--kdb-space-group);
  --kdb-block-gap: var(--kdb-space-block);
  --kdb-chapter-gap: var(--kdb-space-chapter);

  /* The annotation margin, D5d. Column 1 of sixteen, and the only
     approved home for a year, a unit, a source or a scoreline —
     because the position above a headline is reserved for nothing
     (No eyebrows, 2026-09-01). */
  --kdb-annotation-col: 1;
  --kdb-annotation-ink: var(--kdb-text-muted);
  --kdb-annotation-size: var(--kdb-size-micro);
  --kdb-annotation-track: var(--kdb-track-caps);

  /* Drawn surfaces. A plot is a drawing, so it takes drawing ink
     and never a chart library's greys — D9. */
  --kdb-plot-axis: var(--kdb-neutral-95);
  --kdb-plot-tick: var(--kdb-neutral-40);
  --kdb-plot-label: var(--kdb-text-secondary);
  --kdb-plot-value: var(--kdb-text-primary);
  --kdb-plot-field: var(--kdb-neutral-40);   /* D9f field tier */
  --kdb-plot-subject: var(--kdb-accent-mark);
  --kdb-plot-gap: var(--kdb-space-snug);

  /* Media. Captions sit BENEATH — one of the three things the
     eyebrow ban explicitly does not touch. */
  --kdb-media-caption-ink: var(--kdb-text-muted);
  --kdb-media-caption-size: var(--kdb-size-caption);
  --kdb-media-caption-gap: var(--kdb-space-tight);
  --kdb-media-frame: var(--kdb-border-faint);

  /* Rule ladder. D6 weights, as the page names them. Non-scaling
     is enforced in SVG; these are the CSS-side equivalents. */
  --kdb-rule-hair: 0.75px;
  --kdb-rule-divide: 1px;
  --kdb-rule-strong: 1px;
  --kdb-rule-hair-ink: var(--kdb-neutral-40);
  --kdb-rule-divide-ink: var(--kdb-border-hairline);
  --kdb-rule-strong-ink: var(--kdb-border-strong);

  /* Tag — the one container permitted to hold type alone
     (KDB_CONTAINERS.tag). Used for a club identifier, never for a
     legend swatch: legends are banned at D9d. */
  --kdb-tag-pad-x: var(--kdb-space-tight);
  --kdb-tag-pad-y: var(--kdb-space-hair);
  --kdb-tag-size: var(--kdb-size-label);
  --kdb-tag-track: var(--kdb-track-caps);

  /* The mark is shown at the size a mark is used at, not at
     whatever width its column happens to be. Rendered full-column
     it came out 735px tall, which is a poster rather than a
     specimen — and at that size the plate reads as a black slab. */
  --kdb-mark-max: 420px;

  /* Link. Found by review: nothing styled a link, so every one on
     every page was the user agent's royal blue — a colour that is
     not in the palette and could not be, since it fails the ratio
     law and the hue family at once. --kdb-ice-80 is the only Ice
     step measured safe for body text (7.72:1). */
  --kdb-link-ink: var(--kdb-accent-text);
  --kdb-link-underline: color-mix(in srgb, var(--kdb-accent-text) 40%, transparent);
  --kdb-link-hover: var(--kdb-neutral-95);

  /* Ambient motion — D21. A separate set from the arrival
     durations, because ambient motion never resolves and the two
     have opposite requirements: an arrival should be noticed, and
     a float should not. */
  --kdb-drift-period: 14s;    /* one full cycle, slow enough to doubt */
  --kdb-drift-amp: 3%;        /* of the object's own size */
  --kdb-drift-tilt: 1.5deg;   /* past ~2 a jersey reads as tumbling */
  --kdb-drift-settle: 320ms;  /* how fast a hovered object stops */

  /* The distance from the page's own content edge to the viewport
     edge. Written once because two components now need it and
     both were about to compute it themselves: D5d sanctions media
     bleeding right off the container, and a component taking that
     break has to know how far "off" is. It is not simply the page
     margin — beyond the 1440 cap the margin stops growing and the
     real gap keeps going, which is how a bleeding rail ends up
     stopping 240px short of the edge on a 1920 screen and reading
     as a clipped box.

     Written the first way round as
     `(100vw - min(100vw - 2*margin, page-max)) / 2`, which is
     wrong in precisely the case it was added for: `--kdb-page-max`
     is a BORDER-box width and `.k-page` carries its padding
     inside it (box-sizing is border-box globally), so past the cap
     that expression lands one page margin short of the content
     edge. Measured at 1920 before the fix: 240px against the 304px
     it needed, leaving the rail stopping 64px shy of the viewport.
     The gap is just the margin, plus whatever the cap is holding
     back — which is nothing until the cap binds. */
  --kdb-page-bleed: calc(
    var(--kdb-page-margin) + max(0px, (100vw - var(--kdb-page-max)) / 2));

  /* Slide — beat 04, the record read sideways. D29: this is a
     full-viewport slide, not a block in a scrolling page, so its
     measurements are viewport-relative and hand-tuned (D34) rather
     than rungs on the rem ladder. They are tokens anyway, because
     the composition should be adjustable in one place.

     Values recovered from the reference build,
     references/_archive/section-sheet/sections/02-hero-stats.html,
     which is where this composition was worked out. */
  --kdb-slide-pad-block: 1.4rem 1.6rem;
  --kdb-slide-pad-inline: 2.5vw;
  --kdb-slide-group-gap: 4.5vw;      /* between subjects — assists, awards, Belgium, cabinet */
  /* Shares of the RAIL's content height, not the viewport's — cqh
     against the slide's container query, so nothing hangs out of
     the bottom on a short window.

     The numbers are the reference build's own proportions
     converted, NOT rounded up: 58vh of a 900px viewport is 522px,
     which is 66% of the rail's 796px content box, not 72%. The
     first conversion inflated every frame by about a tenth and
     spread the run from 8.1 screens to 9.1 — the composition got
     roomier when what made it good was that it was tight. */
  --kdb-slide-h-full: 100%;
  --kdb-slide-h-lg: 66cqh;
  --kdb-slide-h-md: 47cqh;
  --kdb-slide-h-sm: 34cqh;
  --kdb-slide-h-tuck: 70cqh;
  --kdb-slide-h-tuck-low: 52cqh;
  --kdb-slide-overlap: -9vw;
  --kdb-slide-overlap-sm: -6.5vw;
  --kdb-slide-tuck-pull: -40vw;    /* how far a tucked plate rides back over its numeral */
  --kdb-slide-bar: var(--kdb-space-hair);

  /* How much vertical scroll one screen of sideways travel costs.
     1 means the track moves exactly with the finger. Below 1 it
     moves faster, which is how a long run stays walkable on a
     phone without cutting content — 15 screens of track at 1:1 is
     eight viewport-heights of scrolling before the section ends. */
  --kdb-slide-pace: 1;

  /* The billboard numeral. Condensed, because the composition was
     drawn in Anton and the proportion is load-bearing — an expanded
     numeral at this size is a different layout, not the same one
     restyled. Archivo at wdth 62 is the nearest match in the one
     family D1 allows.

     Weight is --kdb-wght-heavy (700), the system's heaviest. Anton
     is heavier than that and the reference reads with more mass;
     matching it exactly would mean a 900 and a fourth weight in
     D1, which is a decision this file has not made. */
  --kdb-slide-figure-wdth: var(--kdb-wdth-figure);

  /* On pitch / off pitch — beat 05. A full-viewport stage with two
     lines of lockup type and a photograph slotted INTO one of them
     at cap height, so the frame reads as a glyph in the word rather
     than an illustration beside it. Recovered from v2's
     .kdb-otot-stage, which is the best thing in that build. */
  /* Type steps back so the photograph can be the event. The words
     are what is there at rest; the frame is what arrives. A lockup
     big enough to fill the width leaves no width for an image. */
  --kdb-otot-type: clamp(2.4rem, 9.2vw, 8rem);

  /* The frame is no longer sized off the type's em. Tied to it, a
     smaller lockup meant a smaller picture — exactly backwards from
     what shrinking the type was for. It takes its own height from
     the viewport and its own width outright. */
  --kdb-otot-slot-h: clamp(8rem, 25vh, 21rem);
  --kdb-otot-slot-w: clamp(13rem, 38vw, 38rem);
  --kdb-otot-slot-gap: clamp(0.75rem, 1.8vw, 1.6rem);
  --kdb-otot-row-gap: clamp(1rem, 7vh, 5.5rem);
  --kdb-otot-shear: -9deg;                /* the one earned crop in this beat, D14 */
  /* Two durations, not one. The colour has to answer the cursor
     immediately — it is a value changing, so it takes the tick
     step. The frame is a drawing arriving, so it takes the draw
     step. Both on one 620ms value felt broken rather than slow:
     the section appeared not to respond, then caught up. */
  --kdb-otot-shift: var(--kdb-motion-tick);
  --kdb-otot-wipe: var(--kdb-motion-draw);

  /* The finale — beat 04's last cell, which stops being a cell.
     The cabinet plate opens out to the full slide with a hairline
     of ground still showing, so the horizontal run ends on an
     image taking the whole screen rather than just running out.
     That full-bleed frame is what beat 05 then scrolls up into. */
  --kdb-slide-finale-pad: clamp(12px, 1.6vw, 28px);

  /* The solo hero — one frame, the whole screen, a narrow margin.
     Same value as the finale's, so the page opens and the record
     ends on the identical inset. */
  --kdb-hero-solo-pad: clamp(12px, 1.6vw, 28px);

  /* The feed — beat 10. Frames at a size that reads as a glance
     rather than a gallery: this beat is the corner of the eye. */
  --kdb-feed-gap: var(--kdb-space-snug);

  /* Footer. The wordmark is cropped by the bottom edge — the
     fraction below is how much of it the page keeps. */
  /* At 0.34 the word showed a 52px band of a 152px letter — a
     sliver, not a wordmark. Two thirds keeps the letterforms
     legible while the baseline still runs off the page. */
  --kdb-footer-word-crop: 0.62;

  /* The trophy shelf, along the bottom. */
  --kdb-shelf-thumb: clamp(54px, 6vw, 92px);
  --kdb-shelf-gap: clamp(8px, 1.2vw, 20px);
  --kdb-shelf-lift: 760ms;      /* the raise. Slower than a draw: it is a lift */
  /* The panel is hinged at its bottom edge and tipped away before
     it arrives, so it stands up rather than sliding. Past about
     20deg the foreshortening reads as a card trick; under about 8
     it reads as a rendering error rather than depth. */
  --kdb-shelf-tip: 26deg;
  --kdb-shelf-depth: 900px;    /* viewing distance. Nearer is more violent */

  /* The jersey wall — beat 07. Six kits as physical things, each
     in front of the ground it was worn at. */
  /* He stands in the shirt you are looking at. Tall enough to be a
     figure rather than a sticker, short enough to stay behind the
     rail. */
  --kdb-wall-player-h: clamp(190px, 50cqh, 480px);
  --kdb-wall-player-rise: 5%;
  /* THE FLOOR HE STANDS ON is the hem line of the shirts, not the
     bottom of the section. Anchored to the section he landed on
     top of the caption — the club name and its figures live down
     there — and the vertical follow then walked his feet out
     through the bottom edge. This is the room the caption needs,
     so the layer stops where the rail does. */
  --kdb-wall-player-floor: clamp(11rem, 28cqh, 17rem);
  /* How far he tracks the pointer. Wider than it is tall, because
     the wall is a horizontal rail — lateral movement reads as
     parallax, vertical reads as wobble.

     These were 34px and 16px, and at that size the effect was
     invisible: 34px against a 1700px wall is two per cent of the
     width, so sweeping the pointer the whole way across moved him
     about a third of an inch. The mechanism was working and could
     be measured; it simply could not be seen. Sized in vw now, so
     the throw stays the same fraction of the wall on any screen
     instead of shrinking into nothing on a wide one. */
  --kdb-wall-follow-x: 9vw;
  /* Kept under the clearance between his feet and the caption, so
     the downward half of the throw cannot walk him back onto the
     type the floor was measured to avoid. */
  --kdb-wall-follow-y: 2.4vh;
  /* Shorter, too. At 420ms he trailed the cursor far enough to
     read as lag rather than as weight. */
  --kdb-wall-follow-ease: 260ms;
  --kdb-wall-jersey-h: clamp(140px, 38cqh, 360px);
  --kdb-wall-jersey-gap: clamp(0.5rem, 2vw, 2.6rem);
  --kdb-wall-dim: 0.34;          /* an unselected kit, against the drawing */
  /* How far the stadium drawing is let up. At 0.62 it competed
     with the kits it is supposed to sit behind — the ground is
     the room, not the subject. */
  --kdb-wall-ground-ink: 0.3;
  /* Depth for the kits' own tilt. Same device as the trophy lift,
     and the same reason: without a viewing distance a rotate is a
     squash. */
  --kdb-wall-depth: 1400px;
  --kdb-wall-turn: 7deg;
  --kdb-wall-swap: var(--kdb-motion-sweep);

  /* Partnerships — beat 06. A wall of marks; taking one opens a
     player over the page showing that partnership's own media. */
  /* A mark is not a heading. At 2.2vw these measured within 2px of
     the section's own h2 and the wall read as eight more headlines
     — the beat has one heading and a wall of names under it. */
  /* On the ramp, not near it. A hand-picked clamp resolved to
     21.6px — between the deck step's 20 and the head step's 24,
     which is off D1's nine and exactly what the auditor is for.
     Deck is the right step anyway: a mark on this wall is a name,
     one notch above body, not a heading. */
  /* A wall of partners set at deck size in a section reserving
     62vh is a caption in a room. The marks carry the beat, so
     they are set as headings. */
  /* THE NAMES ARE THE ARTWORK. This beat was a small list in a
     62vh band with the section's own h2 set larger than any of
     its content — the label out-scaling the thing it labels,
     which is what made it read as timid. Closed, the marks are
     display type and the wall is the whole screen. */
  --kdb-partner-mark-size: clamp(2rem, 5vw, 4.75rem);
  --kdb-ticker-tilt: -3.5deg;
  --kdb-ticker-gap: clamp(2rem, 4vw, 4.5rem);   /* between the marks */
  --kdb-ticker-row-gap: clamp(0.5rem, 1.5vh, 1.5rem);
  --kdb-ticker-pace: 34s;
  --kdb-partner-mark-size-open: var(--kdb-size-deck);
  --kdb-partner-row-pad: clamp(0.5rem, 1.1vh, 0.95rem);
  /* The frame composition. Two equal rectangles side by side is
     the most inert arrangement available; these are the numbers
     that make it a spread instead. */
  --kdb-frame-lead: 7fr;      /* the larger, lower frame */
  --kdb-frame-follow: 5fr;    /* the smaller, raised one  */
  --kdb-frame-stagger: 8%;
  --kdb-frame-h-lead: 88%;
  --kdb-frame-h-follow: 72%;
  --kdb-frame-lap: -2.5vw;    /* they overlap; D-reversal allows it */
  --kdb-partner-gap: clamp(1.5rem, 4vw, 4rem);
  --kdb-partner-dim: var(--kdb-text-muted);
  --kdb-player-ground: var(--kdb-neutral-95);
  --kdb-player-pad: clamp(16px, 3vw, 48px);
  --kdb-player-open: var(--kdb-motion-draw);

  /* The mouse trail — frames from the selected competition,
     dropped where the pointer has been and taken away again. */
  --kdb-trail-w: clamp(120px, 15vw, 260px);
  --kdb-trail-step: 92px;       /* pointer travel between frames */
  --kdb-trail-life: 900ms;      /* how long one frame stays */
  --kdb-trail-in: 180ms;

  /* Card. Deliberately thin, because in this system a card is
     mostly an absence — see the component. */
  --kdb-card-gap: var(--kdb-space-snug);
  --kdb-card-label-gap: var(--kdb-space-tight);
  --kdb-card-rule: var(--kdb-border-hairline);
  --kdb-card-meta-ink: var(--kdb-text-muted);

  /* Navigation. A header is furniture: it must be findable and
     must never compete with the page. Height is on the space
     scale, not a round number. */
  --kdb-nav-height: var(--kdb-space-block);
  --kdb-nav-ink: var(--kdb-text-primary);
  --kdb-nav-ink-muted: var(--kdb-text-muted);
  --kdb-nav-rule: var(--kdb-border-hairline);
  --kdb-nav-gap: var(--kdb-space-base);

  /* Footer. The one place the site is allowed to go dark, because
     it is the end of the page rather than a surface behind
     content — D4's ban is on blue as a background, and ink is not
     blue. */
  --kdb-footer-ground: var(--kdb-neutral-95);
  --kdb-footer-ink: var(--kdb-neutral-20);
  --kdb-footer-ink-strong: var(--kdb-neutral-00);
  --kdb-footer-rule: var(--kdb-neutral-80);

  /* Table. Numerals are lining and tabular everywhere, so nothing
     here re-states that — it is a base rule, not a component one. */
  --kdb-table-row-pad: var(--kdb-space-tight);
  --kdb-table-head-ink: var(--kdb-text-muted);
  --kdb-table-rule: var(--kdb-border-hairline);
  --kdb-table-rule-strong: var(--kdb-border-strong);
}

/* ---------- Base -------------------------------------------- */

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

/* Block margins are zeroed at the base. Found by the Phase 8
   auditor: the browser's own 1em margin on <p>, <h*> and friends
   is 10px at body size and 16.8px on a deck — neither is on the
   space scale, and both were quietly setting the vertical rhythm
   underneath D5. Spacing is placed by the .k-* rhythm classes,
   never inherited from the user agent. */
:where(h1, h2, h3, h4, p, figure, figcaption, ul, ol, dl, dd,
       blockquote, pre, table, hr) {
  margin: 0;
}

/* hr also carries a UA `margin-inline: auto`, which collapses it
   to zero width inside a grid cell — found in the footer, where
   the divider rendered as nothing at all. */
:where(hr) { width: 100%; border: 0; }

html {
  font-variant-numeric: lining-nums tabular-nums;
  -webkit-text-size-adjust: 100%;
}

body {
  /* The user agent's own 8px margin. Every page in this system had
     it, which put an 8px gap above a sticky header that is
     supposed to sit against the top of the window, and shifted the
     whole grid off the viewport edge. Caught the first time the
     rendered page could actually be looked at. */
  margin: 0;
  font-family: var(--kdb-family-archivo);
  font-variation-settings: "wdth" var(--kdb-wdth-text);
  font-weight: var(--kdb-wght-regular);
  font-size: var(--kdb-size-body);
  line-height: var(--kdb-leading-body);
  letter-spacing: var(--kdb-track-body);
  color: var(--kdb-text-primary);
  background: var(--kdb-surface-ground);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Tabular figures are the default, so opt OUT explicitly where
   proportional reads better — running prose with a stray year. */
.kdb-nums-proportional { font-variant-numeric: lining-nums proportional-nums; }
