/* ==========================================================================
   KodeYak — design tokens
   v4.0 “Weave” · 2026-08-01 · see docs/brand-identity.md for rationale
   Supersedes v3.0 “Ember” (amber on warm graphite), v2.0 “Forge”, v1.0 (magenta
   on navy). Ember was a dark-first single-ground system; v4.0 is a two-ground
   system — paper and glass — and that change goes deeper than colour.

   Single source of truth for colour, type, spacing and motion.
   Component CSS must reference these variables, never raw hex values.

   ---- PALETTE STATUS -------------------------------------------------------
   Three palettes ship here. **Folded Light is the current front runner and is
   therefore the default** — it is what `:root` resolves to with no attribute
   set. The other two are one attribute away:

       <html data-palette="weave">    cool aqua fibre, sage-oat paper
       <html data-palette="vellum">   gold fibre, warm parchment
       <html>                         Folded Light — DEFAULT
       <html data-palette="folded">   Folded Light, stated explicitly

   None of the three is locked. Swapping the default is a one-line change: move
   the token block, not the component CSS. Every colour below is measured; the
   ratios in the comments are computed, not estimated.

   Live reference: docs/weave-system.html (the system, with a palette switch)
                   docs/font-study.html   (the typeface comparison)
   Those boards use unprefixed token names (--paper-200); this file is the
   canonical --ky- prefixed set. Same values, same structure.
   ========================================================================== */


/* ==========================================================================
   1. PALETTE-INDEPENDENT
   Geometry, type, spacing, motion. None of this changes between palettes.
   ========================================================================== */

:root {
  /* ---- Substrate geometry -------------------------------------------------
     One lattice runs under the whole page. Every ground paints it at the same
     spacing, offset by its own DOCUMENT position modulo the major step, so a
     strand entering a tech panel exits it in register. See brand-identity.md §5. */
  --ky-grid-minor: 24px;    /* fine tooth — 5% ink on paper, 3% bone on glass */
  --ky-grid-major: 120px;   /* the fibre. Always a multiple of the minor step. */
  --ky-lattice-x: 0px;      /* written per ground by JS. WHOLE PIXELS ONLY —   */
  --ky-lattice-y: 0px;      /* a fractional offset splits a 1px rule in two.   */
  /* Lit disc radius. THESE MUST BE A WHOLE NUMBER OF PIXELS — see the media
     query below, which is why this is a breakpoint and not the `min(230px,38vw)`
     it briefly was. The bloom layer is a box positioned and offset by this
     radius, so a fractional radius makes its background-position fractional,
     which antialiases the 1px lattice rules across two rows and reads as a
     doubled line. Continuous scaling is not worth resurrecting that. */
  --ky-bloom: 260px;        /* on glass */
  --ky-bloom-paper: 230px;  /* smaller on paper — a sheet absorbs */

  /* ---- Typography ---------------------------------------------------------
     Two worlds, two voices, plus a mark that belongs to neither.
     Ground decides face: serif on paper and wood, sans on glass. Never mixed
     across the boundary inside one block — the switch IS the signal.

     Montserrat is LOGO ONLY. Because the lockups ship as outlined vector with
     no font references, restricting it to the wordmark removes it from the
     webfont payload entirely: it is the one face here that loads no bytes.

     Tilt Neon is DISPLAY ONLY, structurally — Google serves it at a single 400
     weight with no bold and no italic, only XROT/YROT rotation axes. Anything
     on glass that needs a weight is Ubuntu.

     Skia is NOT here by design: Apple-licensed, unusable as a webfont. It
     exists only as outlined vector inside the logo. Never add it.            */
  --ky-font-logo:      'Montserrat', system-ui, -apple-system, sans-serif;
  --ky-font-heading:   'EB Garamond', Garamond, 'Iowan Old Style', serif;
  --ky-font-body:      'Newsreader', Georgia, 'Times New Roman', serif;
  --ky-font-tech-display: 'Tilt Neon', system-ui, -apple-system, sans-serif;
  --ky-font-tech:      'Ubuntu', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --ky-font-mono:      'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Fluid scale, 1.250 major third, min→max across 360–1200px viewport */
  --ky-text-display: clamp(2.70rem, 1.40rem + 5.40vw, 5.20rem);  /* 43 → 83 */
  --ky-text-h1:      clamp(1.85rem, 1.35rem + 2.00vw, 2.70rem);  /* 30 → 43 */
  --ky-text-h2:      clamp(1.56rem, 1.30rem + 1.15vw, 2.06rem);  /* 25 → 33 */
  --ky-text-h3:      1.35rem;                                     /* 21.6    */
  --ky-text-tech-display: clamp(1.75rem, 1.25rem + 1.90vw, 2.60rem);
  --ky-text-body-lg: 1.1875rem;                                   /* 19 */
  --ky-text-body:    1.0625rem;                                   /* 17 */
  --ky-text-small:   0.875rem;                                    /* 14 */
  --ky-text-eyebrow: 0.70rem;                                     /* 11.2 */

  --ky-weight-regular: 400;
  --ky-weight-medium:  500;   /* Ubuntu's UI weight; Tilt Neon has no others */
  --ky-weight-bold:    600;   /* EB Garamond display */

  --ky-leading-tight: 1.06;   /* display */
  --ky-leading-head:  1.14;   /* section headings */
  --ky-leading-body:  1.62;
  --ky-tracking-display:  -0.012em;
  --ky-tracking-tech:      0.004em;  /* Tilt Neon wants a hair of positive */
  --ky-tracking-eyebrow:   0.22em;
  --ky-tracking-wordmark:  0.02em;
  --ky-measure: 64ch;

  /* ---- Spacing — 4px base ------------------------------------------------ */
  --ky-space-1:  0.25rem;  --ky-space-2:  0.5rem;   --ky-space-3:  0.75rem;
  --ky-space-4:  1rem;     --ky-space-6:  1.5rem;   --ky-space-8:  2rem;
  --ky-space-12: 3rem;     --ky-space-16: 4rem;     --ky-space-24: 6rem;
  --ky-space-32: 8rem;

  /* ---- Layout ------------------------------------------------------------ */
  --ky-content-max: 1240px;
  --ky-gutter: var(--ky-space-6);
  /* CORNERS, and the split is by what a thing IS, not by which ground it sits
     on. SCREENS are chamfered — the masthead, the hero display panel, the
     closing rail, the glass panels: surfaces you read off, cut like machined
     plate. They carry their own cut sizes locally (--ky-cut, --sc-cut) because
     the shapes are not symmetrical and there is no one number. CONTROLS are
     rounded: --ky-radius-pill on buttons. PAPER is rounded too, gently —
     --ky-radius-leaf, because a soft corner is what says "sheet".

     NEVER RESHAPE A CONTROL WITH clip-path. It clips the element's whole
     rendering and the FOCUS RING is part of that: a clipped control renders
     with no visible outline at all, at any outline-offset. Verified, not
     assumed. It looks perfect in a screenshot, which is exactly how it would
     ship. Screens can be clipped freely — none of them are focusable. */
  --ky-radius: 6px;
  --ky-radius-panel: 16px;
  --ky-radius-leaf: 4px;     /* paper cards — a sheet, not a card */
  --ky-radius-pill: 999px;   /* controls */

  /* ---- Motion ------------------------------------------------------------ */
  --ky-duration-fast: 150ms;
  --ky-duration-base: 250ms;
  --ky-duration-settle: 700ms;
  --ky-ease: cubic-bezier(0.16, 1, 0.3, 1);
  --ky-focus-width: 2px;
  --ky-focus-offset: 2px;
}


/* ==========================================================================
   2. PALETTE A — “Folded Light”  ·  DEFAULT / FRONT RUNNER
   Extracted from a folded-parchment reference. Core spec colours:
     Ink #030405 · Abyss #0A2334 · Steel Teal #2B637A · Frost #B7C8C6 ·
     Sand #D6B997 · Amber #D99A3F, with Copper #8A4A18 as the warm terminal.

   Two spec notes were interpreted rather than copied — see brand-identity.md §4:
   the lattice is INERT at rest and only turns amber where the cursor lights it
   (so amber stays "one accent per view"), and Ink is the deepest glass step
   rather than the page (the 60/40 human-first ratio outranks a palette note).

   Grounds and accents are spec values as given. Text steps are darkened
   derivations, because ramp 07 is a mid-warm and several core colours land
   between 4.5 and 7:1 on it — Copper is 4.76, Steel Teal 4.62.
   ========================================================================== */

:root,
:root[data-palette="folded"] {
  --ky-paper-100:#EFE7DA; --ky-paper-200:#E2D6C2;   /* ramp 07 — the ground */
  --ky-paper-300:#D6B997; /* Sand */ --ky-paper-400:#C0A17B;
  --ky-glass-950:#030405; /* Ink */  --ky-glass-900:#0A2334; /* Abyss */
  --ky-glass-800:#154458; --ky-glass-700:#2B637A;   /* Steel Teal */

  --ky-ink-900:#0A2334;   /* 11.23:1 on paper — AAA */
  --ky-ink-600:#24404C;   /*  7.65:1 — AAA */
  --ky-ink-400:#5E7480;   /*  3.41:1 — NON-TEXT only */
  --ky-bone-100:#EFE7DA;  /* 13.13:1 on glass — AAA */
  --ky-bone-400:#B7C8C6;  /*  9.28:1 — Frost, exactly its stated role */
  --ky-bone-600:#6D8E94;  /*  4.56:1 — NON-TEXT only */

  /* The strand. In the source image the light IS amber. */
  --ky-fiber-200:#F7E9CE; --ky-fiber-300:#EBC98F;
  --ky-fiber-400:#E3AE5C; /*  8.04:1 on glass — AAA */
  --ky-fiber-500:#D99A3F; /* Amber — core */
  --ky-fiber-600:#A96A22;
  --ky-fiber-800:#63340E; /*  7.21:1 on paper — AAA */

  /* Counter-accent — occupies every slot amber held in Ember. */
  --ky-cool-300:#CFE0E4;
  --ky-cool-400:#93BDC9;  /*  7.95:1 on glass — AAA */
  --ky-cool-500:#2B637A;
  --ky-cool-800:#193F50;  /*  7.83:1 on paper — AAA */

  /* Copper is the warm ramp terminal — depth, which is what wood is for. */
  --ky-wood-400:#B0723A; --ky-wood-600:#8A4A18; /* Copper */
  --ky-wood-700:#5C3410; /* 7.49:1 on paper */ --ky-wood-900:#2E1908;

  --ky-label:#14100A;
  --ky-focus:#CDB6FF;     /*  9.00:1 on glass — outside amber AND steel */
  --ky-action-bg:#D99A3F; --ky-action-bg-hover:#E3AE5C; --ky-action-bg-active:#EBC98F;

  --ky-success-ink:#1B6340; --ky-warning-ink:#8B2E62; --ky-danger-ink:#7A1810;
  --ky-success-lit:#6ED7AB; --ky-warning-lit:#F0A3D0; --ky-danger-lit:#FF8A82;

  /* Substrate channels. Every lattice line and bloom layer references these. */
  --ky-rgb-ink:      10,35,52;    /* paper minor grid — the cool drafting line */
  --ky-rgb-thread:  138,74,24;    /* paper major — copper gilding, INERT */
  --ky-rgb-hue:     217,154,63;   /* amber, and only under the cursor */
  --ky-rgb-soft:    235,201,143;
  --ky-rgb-core:    247,233,206;
  --ky-rgb-gridlit:  77,126,147;  /* glass major at rest — steel, not amber */
  --ky-rgb-bone:    183,200,198;
  --ky-rgb-lit:     255,240,214;  /* glass bloom core — blows to warm white */
  --ky-rgb-void:      3,4,5;      /* = --ky-glass-950 (Ink), as channels.      */
                                  /* Needed wherever the deepest step is laid  */
                                  /* over artwork at partial alpha — a glass   */
                                  /* panel, a scrim. Every palette must define */
                                  /* it or those rgba() calls compute invalid  */
                                  /* and the background silently disappears.   */
  --ky-rgb-panel:    10,35,52;    /* = --ky-glass-900 (Abyss), as channels.    */
                                  /* The panel ground one step up from the     */
                                  /* void, for glass laid over artwork. Do NOT */
                                  /* substitute --ky-rgb-ink: it is identical  */
                                  /* here only because Folded Light's text ink */
                                  /* and its panel ground are the same colour. */
                                  /* In Weave they differ, and the swap would  */
                                  /* silently shift the panel in two palettes. */
  --ky-rgb-fiber:   217,154,63;   /* every glow, halo and drop-shadow */
  --ky-glass-grad-a:#123A4C; --ky-glass-grad-b:#0A2334; --ky-glass-grad-c:#050D14;
}


/* ==========================================================================
   3. PALETTE B — “Weave”
   The original v4.0 pass. Cool aqua fibre on sage-oat paper, warm graphite
   glass. Amber survives from Ember as the counter-accent.
   ========================================================================== */

:root[data-palette="weave"] {
  --ky-paper-100:#F2F3EA; --ky-paper-200:#E6E8DA;
  --ky-paper-300:#D7DAC7; --ky-paper-400:#C4C8B2;
  --ky-glass-950:#0F1012; --ky-glass-900:#17181A;
  --ky-glass-800:#212327; --ky-glass-700:#2E3136;

  --ky-ink-900:#191A16;   /* 14.09:1 */  --ky-ink-600:#4A4B43;  /* 7.11:1 */
  --ky-ink-400:#77786E;   /* 3.60:1 — non-text */
  --ky-bone-100:#F4F3EC;  /* 15.97:1 */  --ky-bone-400:#A9AAA3; /* 7.59:1 */
  --ky-bone-600:#6E706B;  /* 3.55:1 — non-text */

  --ky-fiber-200:#D6FBEE; --ky-fiber-300:#B7F4E0;
  --ky-fiber-400:#6FE3C1; /* 11.37:1 on glass */
  --ky-fiber-500:#46D3A8; --ky-fiber-600:#2BA888;
  --ky-fiber-800:#0D5545; /*  7.04:1 on paper */

  --ky-cool-300:#F0C79B; --ky-cool-400:#E9AB69; /* 8.88:1 on glass */
  --ky-cool-500:#E08A2E; --ky-cool-800:#6B4116; /* 7.07:1 on paper */

  --ky-wood-400:#B08C63; --ky-wood-600:#7A5433;
  --ky-wood-700:#5E3E22; /* 7.73:1 */ --ky-wood-900:#33210F;

  --ky-label:#140F09;
  --ky-focus:#A9C8FF;     /* 10.48:1 on glass */
  --ky-action-bg:#E08A2E; --ky-action-bg-hover:#E9AB69; --ky-action-bg-active:#F0C79B;

  --ky-success-ink:#1B6340; --ky-warning-ink:#8B2E62; --ky-danger-ink:#A3231F;
  --ky-success-lit:#6ED7AB; --ky-warning-lit:#F0A3D0; --ky-danger-lit:#FF8A82;

  --ky-rgb-ink:      25,26,22;
  --ky-rgb-thread:   13,85,69;
  --ky-rgb-hue:      18,214,162;
  --ky-rgb-soft:    143,233,200;
  --ky-rgb-core:    160,240,210;
  --ky-rgb-gridlit: 111,227,193;
  --ky-rgb-bone:    244,243,236;
  --ky-rgb-lit:     206,255,240;
  --ky-rgb-void:     15,16,18;    /* = --ky-glass-950 */
  --ky-rgb-panel:    23,24,26;    /* = --ky-glass-900 */
  --ky-rgb-fiber:    70,211,168;
  --ky-glass-grad-a:#24262B; --ky-glass-grad-b:#17181A; --ky-glass-grad-c:#121316;
}


/* ==========================================================================
   4. PALETTE C — “Vellum”
   Warm parchment, gilded thread, deep warm dark, steel blue out of the shadow
   side. First pass at inverting the poles: here the strand IS the warmth.
   ========================================================================== */

:root[data-palette="vellum"] {
  --ky-paper-100:#F5EFE0; --ky-paper-200:#EBE3D1;
  --ky-paper-300:#DCD2BB; --ky-paper-400:#C7BB9E;
  --ky-glass-950:#0B0A09; --ky-glass-900:#141210;
  --ky-glass-800:#1F1C18; --ky-glass-700:#2C2823;

  --ky-ink-900:#1B1710;   /* 13.97:1 */  --ky-ink-600:#4E4738;  /* 7.20:1 */
  --ky-ink-400:#7C7362;
  --ky-bone-100:#F4EFE3;  /* 16.29:1 */  --ky-bone-400:#A8A092; /* 7.22:1 */
  --ky-bone-600:#6E6759;

  --ky-fiber-200:#FCE9C4; --ky-fiber-300:#F8DCA6;
  --ky-fiber-400:#EFBF6E; /* 10.99:1 on glass */
  --ky-fiber-500:#E3A44A; --ky-fiber-600:#B87F2E;
  --ky-fiber-800:#653C0C; /*  7.24:1 on paper */

  --ky-cool-300:#BBD3DE; --ky-cool-400:#8FB6C9; /* 8.63:1 on glass */
  --ky-cool-500:#5E8DA5; --ky-cool-800:#2A4C5E; /* 7.17:1 on paper */

  --ky-wood-400:#A8865C; --ky-wood-600:#7A5A34;
  --ky-wood-700:#5C4122; --ky-wood-900:#2E1E0C;

  --ky-label:#17110A;
  --ky-focus:#CDB6FF;     /* 11.03:1 — violet, because the cool accent is blue */
  --ky-action-bg:#E3A44A; --ky-action-bg-hover:#EFBF6E; --ky-action-bg-active:#F8DCA6;

  --ky-success-ink:#1B6340; --ky-warning-ink:#8B2E62; --ky-danger-ink:#9E2420;
  --ky-success-lit:#6ED7AB; --ky-warning-lit:#F0A3D0; --ky-danger-lit:#FF8A82;

  --ky-rgb-ink:      27,23,16;
  --ky-rgb-thread:  110,67,16;
  --ky-rgb-hue:     240,168,60;
  --ky-rgb-soft:    246,214,158;
  --ky-rgb-core:    252,232,196;
  --ky-rgb-gridlit: 227,164,74;
  --ky-rgb-bone:    244,239,227;
  --ky-rgb-lit:     255,238,205;
  --ky-rgb-void:     11,10,9;     /* = --ky-glass-950 */
  --ky-rgb-panel:    20,18,16;    /* = --ky-glass-900 */
  --ky-rgb-fiber:   227,164,74;
  --ky-glass-grad-a:#221E19; --ky-glass-grad-b:#141210; --ky-glass-grad-c:#100E0C;
}


/* ==========================================================================
   5. SEMANTIC ROLES
   Component CSS uses THESE, never the raw palette steps above. Because custom
   properties resolve lazily, this block is written once and picks up whichever
   palette is active — it does not need duplicating per palette.

   The page ground is PAPER. That is the biggest break from Ember, which was
   dark-first. Tech surfaces are glass panels set into the paper, at roughly
   40% of ground area — see brand-identity.md §2.
   ========================================================================== */

:root {
  --ky-bg:             var(--ky-paper-200);
  --ky-surface:        var(--ky-paper-100);   /* a leaf laid on the ground */
  --ky-surface-raised: var(--ky-paper-300);
  --ky-border:         var(--ky-paper-400);

  --ky-text:           var(--ky-ink-900);
  --ky-text-muted:     var(--ky-ink-600);
  --ky-text-dim:       var(--ky-ink-400);     /* non-text / decorative only */
  --ky-text-accent:    var(--ky-fiber-800);   /* eyebrows on paper */

  --ky-panel:          var(--ky-glass-900);
  --ky-panel-text:     var(--ky-bone-100);
  --ky-panel-muted:    var(--ky-bone-400);
  --ky-panel-dim:      var(--ky-bone-600);    /* non-text only */
  --ky-panel-accent:   var(--ky-cool-400);    /* eyebrows on glass */

  /* Grounds swap accents: paper wears the fibre, glass wears the counter.
     That crossover is what stops the two halves reading as two brands. */

  --ky-link:           var(--ky-fiber-800);
  --ky-link-hover:     var(--ky-fiber-600);
  --ky-link-panel:     var(--ky-fiber-400);

  /* The primary fill is declared PER PALETTE, because "which colour is the
     CTA" is a palette decision. The invariant lives here: the label is dark,
     so every palette must supply a LIGHT fill and the fill LIGHTENS on
     interaction. Folded Light and Vellum both have a dark counter-accent —
     pointing the CTA at it puts a near-black label on a dark ground and
     measures 2.86:1. This indirection exists because that actually happened. */
  --ky-action-text:    var(--ky-label);

  /* Status. The accent never signals state. On a warm-accented light system a
     yellow warning is indistinguishable from the brand, so warning moves to
     plum — and status is never signalled by colour alone regardless. */
  --ky-success:        var(--ky-success-ink);
  --ky-warning:        var(--ky-warning-ink);
  --ky-danger:         var(--ky-danger-ink);
  --ky-info:           var(--ky-cool-800);
}

/* Inside a glass panel the same semantic names flip to their lit values, so
   component CSS never branches on ground. */
.ky-glass {
  /* The flip has to land on `color` too, not just on the custom properties.
     body sets `color: var(--ky-text)` once, and its COMPUTED value inherits
     down — redeclaring --ky-text here does not reach back up to change it. So
     anything that never sets its own colour (h1, h2, h3, li, plain text) stays
     paper ink and goes near-invisible on glass. Caught on the planet hero,
     where the headline rendered #0A2334 on a near-black ground; the parts that
     looked right only looked right because they name a token explicitly. */
  color:               var(--ky-text);

  --ky-bg:             var(--ky-glass-900);
  --ky-surface:        var(--ky-glass-800);
  --ky-surface-raised: var(--ky-glass-700);
  --ky-border:         rgba(var(--ky-rgb-bone), .16);
  --ky-text:           var(--ky-bone-100);
  --ky-text-muted:     var(--ky-bone-400);
  --ky-text-dim:       var(--ky-bone-600);
  --ky-text-accent:    var(--ky-cool-400);
  --ky-link:           var(--ky-fiber-400);
  --ky-link-hover:     var(--ky-fiber-300);
  --ky-success:        var(--ky-success-lit);
  --ky-warning:        var(--ky-warning-lit);
  --ky-danger:         var(--ky-danger-lit);
}

/* The bloom has to read as a SPOT — a place on the page that is lit, not a wash
   over the whole screen. At the desktop radius the disc is a 460px circle, which
   is wider than a phone. One breakpoint rather than a vw expression, because the
   radius must stay a whole number of pixels (see the tokens). 700px is well clear
   of the width where a full-size disc would start to flood. */
@media (max-width: 700px) {
  :root {
    --ky-bloom: 170px;
    --ky-bloom-paper: 150px;
  }
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --ky-duration-fast: 0ms;
    --ky-duration-base: 0ms;
    --ky-duration-settle: 0ms;
    --ky-bloom: 0px;
    --ky-bloom-paper: 0px;
  }
}


/* ==========================================================================
   WHEN THE CURSOR BLOOM IS OFF
   Two tokens, both read by src/css/weave.css. --ky-bloom-display hides the
   masked bloom layers outright. --ky-bloom-wash zeroes the panel's cursor wash,
   which cannot use the first one because it is a layer of the panel's OWN
   background rather than a separate element — it has to be a colour that goes to
   nothing, not a box that stops being displayed. Both must be set together or
   turning the bloom off leaves a glow following the cursor with no lattice in
   it. The conditions live here so that "does the lit
   lattice run" has a single answer in a single place, rather than a media query
   copied next to each of the four layers that would then drift.

   THIS IS A COMFORT CONTROL, NOT A CONFORMANCE CRUTCH — and the distinction
   matters if it is ever revisited. It was originally scoped to license the
   bloom's contrast failure over glass panels (Frost body copy measured 1.00:1
   under the lit strand). That failure was fixed properly instead: the panel's
   lit STRANDS are ramped out before they reach a word, and only the broad cursor
   wash is allowed over the copy, because a soft even lift costs about a point of
   contrast where a 1px strand costs everything. Every measured pair clears AA
   with the cursor parked directly on the text. What remains is a real but
   different need: a lit ground moving under body copy is tiring for some readers
   at any ratio, and readers who ask their OS for more contrast expect flat
   grounds.

   Three ways in, and the explicit opt-OUT is what makes the manual control real
   rather than decorative. Without [data-fx-ok], a reader whose OS already asks
   for more contrast would press the button and watch nothing happen.
   ========================================================================== */

/* Motion preference is absolute and deliberately NOT overridable below — a
   reader who asked for less movement did not ask for a moving grid. */
@media (prefers-reduced-motion: reduce) { :root { --ky-bloom-display: none; --ky-bloom-wash: 0; } }

/* The OS signal. Honoured automatically, which is the part a toggle cannot do:
   it spares the reader having to meet the full effect before finding the
   control that turns it off. */
@media (prefers-contrast: more) { :root:not([data-fx-ok]) { --ky-bloom-display: none; --ky-bloom-wash: 0; } }

/* The manual control, last so an explicit request always wins. Written by the
   inline script in _includes/page.njk and persisted in localStorage. */
:root[data-reduce-fx] { --ky-bloom-display: none; --ky-bloom-wash: 0; }


/* ==========================================================================
   6. BASELINE APPLICATION
   ========================================================================== */

/* html carries the background so body's does NOT propagate to the canvas.
   Body must keep painting it inside its own stacking context, or the bloom
   (a z-index:-1 pseudo-element) has nothing to blend against. */
html { background: var(--ky-bg); }

body {
  /* The reset belongs here, not in a chunk file. It was supplied only by
     `html.scene-lab body` in scene.css until 2026-08-10, so the moment a page
     rendered without the scene class it picked the UA's 8px back up — which a
     full-bleed fixed masthead and a full-bleed footer both make immediately
     obvious. Found while moving the inner pages onto the weave layout. */
  margin: 0;
  background: var(--ky-bg);
  color: var(--ky-text);
  font-family: var(--ky-font-body);
  font-variation-settings: 'opsz' 16;
  font-size: var(--ky-text-body);
  line-height: var(--ky-leading-body);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  font-family: var(--ky-font-heading);
  font-weight: var(--ky-weight-bold);
  line-height: var(--ky-leading-head);
}
h1 { font-size: var(--ky-text-h1); line-height: var(--ky-leading-tight);
     letter-spacing: var(--ky-tracking-display); }

/* On glass the heading face changes with the ground. Tilt Neon is display-only
   and takes the filament glow honestly — it is an actual neon face. Anything
   below display size is Ubuntu, which has the weights Tilt Neon lacks. */
.ky-glass h1, .ky-glass h2 {
  font-family: var(--ky-font-tech-display);
  font-weight: var(--ky-weight-regular);
  font-variation-settings: 'XROT' 0, 'YROT' 0;
  letter-spacing: var(--ky-tracking-tech);
  text-shadow: 0 0 20px rgba(var(--ky-rgb-fiber), .30);
}
.ky-glass h3, .ky-glass h4, .ky-glass p, .ky-glass li {
  font-family: var(--ky-font-tech);
}
.ky-glass h3, .ky-glass h4 { font-weight: var(--ky-weight-medium); }

.ky-wordmark {
  font-family: var(--ky-font-logo);
  font-weight: 700;
  font-variant-caps: all-small-caps;
  letter-spacing: var(--ky-tracking-wordmark);
}

/* Section labels: "01  SERVICES" */
.ky-eyebrow {
  font-family: var(--ky-font-mono);
  font-size: var(--ky-text-eyebrow);
  font-weight: var(--ky-weight-medium);
  letter-spacing: var(--ky-tracking-eyebrow);
  text-transform: uppercase;
  color: var(--ky-text-accent);
}

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

/* ---- Buttons -------------------------------------------------------------
   Controls are always tech-side — a button is a machine part wherever it sits,
   so it takes Ubuntu on both grounds. The fill LIGHTENS on interaction because
   the label is near-black; every state below is measured. If a white-label
   variant is ever added, that direction flips — check the label first.

   A PILL, and deliberately not a chamfer. The chamfer is the SCREENS' language
   — the masthead, the hero panel, the closing rail: surfaces you read off, cut
   like machined plate. A control is a different kind of object and gets the
   ordinary rounded pill, which is also what everyone already knows how to
   press. Buttons were briefly chamfered as part of a broader "tech is never
   rounded" reading; that was too wide. The rule is about screens.

   Keeping it a plain border-radius also keeps the FOCUS RING working for free.
   Any future attempt to reshape a control with `clip-path` will silently delete
   it — see the note on the corner tokens above.                                */
.ky-btn {
  font-family: var(--ky-font-tech);
  font-weight: var(--ky-weight-medium);
  font-size: var(--ky-text-small);
  padding: var(--ky-space-3) var(--ky-space-6);
  border-radius: var(--ky-radius-pill);
  border: 1px solid transparent;
  background: var(--ky-action-bg);
  color: var(--ky-action-text);
  display: inline-flex;
  align-items: center;
  gap: var(--ky-space-2);
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--ky-duration-fast) var(--ky-ease);
}
.ky-btn:hover  { background: var(--ky-action-bg-hover); }
.ky-btn:active { background: var(--ky-action-bg-active); }

/* Secondary: outlined in wood on paper, bone on glass. */
.ky-btn--secondary {
  background: transparent;
  border-color: var(--ky-wood-600);
  color: var(--ky-wood-700);
}
.ky-btn--secondary:hover { background: rgba(var(--ky-rgb-thread), .10); }
.ky-glass .ky-btn--secondary {
  border-color: rgba(var(--ky-rgb-bone), .24);
  color: var(--ky-bone-100);
}
.ky-glass .ky-btn--secondary:hover { background: rgba(var(--ky-rgb-bone), .08); }

.ky-btn[disabled] {
  background: var(--ky-surface-raised);
  color: var(--ky-text-dim);
  border-color: transparent;
  cursor: not-allowed;
}
.ky-btn[disabled]:hover { background: var(--ky-surface-raised); }

/* Focus sits OUTSIDE the control via outline-offset, never inset, in a hue
   outside both brand poles — so focus never reads as accent or link. Each
   palette moves it as needed: Folded Light and Vellum both use a cool accent,
   so the ring goes violet there. */
:where(a, button, [tabindex]):focus-visible {
  outline: var(--ky-focus-width) solid var(--ky-focus);
  outline-offset: var(--ky-focus-offset);
}

/* The old site broke words mid-syllable ("Assessme/nt") in the service cards.
   Never use word-break: break-all. */
* { overflow-wrap: break-word; word-break: normal; }
