/* ==========================================================================
   ag0osW3b design system — token contract and theme presets

   TWO INDEPENDENT AXES, both set as attributes on <html>:

     data-theme   workshop | console | spec | terminal   (the design identity)
     data-mode    light | dark                           (absent = follow OS)

   Eight surfaces total. Every one of them is a shipped surface.

   ── HOW TO USE THIS FILE ──────────────────────────────────────────────────

   Components consume ONLY the semantic role names (--bg, --text, --accent,
   --radius, --font-body, ...). A component must never reference a preset by
   name and must never hardcode a color. That rule is the whole architecture:
   it is why adding a fifth preset is a data change, not a code change.

   ── HOW A PRESET IS DEFINED ───────────────────────────────────────────────

   Each preset block declares paired values:

     --l-*   the light-mode value
     --d-*   the dark-mode value

   plus its mode-independent type and shape tokens. The MODE RESOLUTION block
   at the bottom maps the --l- and --d- pairs onto the semantic names. That is
   written once and works for every preset, present and future.

   ── ADDING A PRESET ───────────────────────────────────────────────────────

   1. Copy a preset block, rename the [data-theme="..."] selector.
   2. Fill in all 14 --l-/--d- pairs plus type and shape tokens.
   3. Add the name to PRESETS in app/models/site_setting.rb.
   4. Run bin/rails test. test/design/contrast_test.rb will fail the build if
      any pair misses WCAG AA. It is not optional and it is not advisory.

   Color is authored in OKLCH. Chroma drops as lightness approaches either
   extreme. No #000, no #fff: every neutral is tinted toward its preset hue.
   ========================================================================== */

@layer tokens, base, layout, components, utilities;

@layer tokens {
  /* ---- Shared scales -----------------------------------------------------
     Mode- and preset-independent. Presets vary color, type and shape; the
     spatial skeleton stays fixed so a component written once works in all
     eight combinations. */

  :root {
    /* ---- Rhythm ----------------------------------------------------------
       The scale is authored once and multiplied at use time, so --density is
       the only thing a preset sets to become dense or airy. Spec compresses,
       workshop breathes, and no component has to know which it is in. */
    --density: 1;
    --space-1: calc(0.25rem * var(--density));
    --space-2: calc(0.5rem * var(--density));
    --space-3: calc(0.75rem * var(--density));
    --space-4: calc(1rem * var(--density));
    --space-5: calc(1.5rem * var(--density));
    --space-6: calc(2rem * var(--density));
    --space-7: calc(3rem * var(--density));
    --space-8: calc(4.5rem * var(--density));
    --space-9: calc(7rem * var(--density));

    /* type scale — heading steps stay at or above a 1.25 ratio */
    --text-xs: 0.75rem;
    --text-sm: 0.875rem;
    --text-base: 1.0625rem;
    --text-lg: 1.35rem;
    --text-xl: clamp(1.7rem, 1.45rem + 1.1vw, 2.1rem);
    --text-2xl: clamp(2.15rem, 1.7rem + 2.1vw, 2.9rem);
    --text-3xl: calc(clamp(2.7rem, 1.9rem + 3.6vw, 4rem) * var(--display-scale));
    --display-scale: 1;
    --weight-body-strong: 700;

    /* ---- Layout ----------------------------------------------------------
       The page is two columns: a metadata rail and a text column.

       Reading measure is capped in ch and cannot grow, so a container wider
       than the measure is only justified if something fills the difference.
       Narrowing the container to match the measure is the other way to resolve
       that, and it is the weaker one: it leaves a centred column of text with
       symmetric voids, which is the shape brand work is supposed to avoid.

       So the rail is what earns the width. Everything that annotates the text
       (section markers, dates, tags, the hero eyebrow) moves out of the text
       column and into the margin, and the text column holds nothing but text.

       The numbers are locked to spec's drawing grid and to each other:

         content column   960px = 12 x --grid-column
         rail + rail-gap  160px = 2 columns, so text starts on a grid line
         text column      800px = 10 columns
         --maxw           960px + two 2.5rem gutters = 65rem

       The text column is sized by the widest-set preset, not the narrowest.
       68ch of spec's grotesque is 732px where workshop's old-style serif needs
       616px for the same count, so a column that fits workshop clips spec below
       the 65ch floor. It is a legibility floor, so the widest one wins and the
       narrower presets simply leave slack at the right of the column.

       Change one number and recheck the rest. test/system/layout_test.rb fails
       if the measure leaves the 65-75ch band in any preset. */
    --maxw: 65rem;
    --gutter: clamp(1.25rem, 4vw, 2.5rem);
    --rail: 7.5rem;
    --rail-gap: 2.5rem;
    --rail-indent: calc(var(--rail) + var(--rail-gap));

    /* The hero cap is per-preset: 17ch of a humanist sans and 17ch of a wide
       mono are very different line counts, and text-wrap: balance gives up
       above six lines. */
    --hero-measure: 17ch;

    /* ---- Section grammar -------------------------------------------------
       How a preset punctuates a section. This is what stops all four reading
       as one page in four palettes: the rule vocabulary and the heading marker
       differ per preset, while the markup stays identical.

       --heading-marker is substituted into a content: property, so it may hold
       a string, a counter(), or both. */
    --rule-width: 1px;
    --rule-style: solid;
    --heading-marker: "";
    --heading-marker-display: none;
    --heading-marker-size: var(--text-xs);
    --cursor-content: "";

    /* Where rail content sits in its track. Left is the default because a
       flush left edge is what makes the rail read as a column rather than as
       text pushed away from the body. Terminal overrides it: a prompt belongs
       next to its command, not a screen-width away from it. */
    --rail-align: left;

    /* ---- The hand --------------------------------------------------------
       Two pieces of grammar that let a person appear on the page: a mark over
       a phrase and a note in the margin. Both are content, written in the
       admin, so this layer only decides what the marks are made of.

       Every preset answers both, because both carry meaning that must survive
       a switch: a marked phrase stays marked and a note stays a note. What
       changes is the instrument. Workshop is a marker and a hand; console
       labels its panel; spec annotates a drawing; terminal writes a comment.

       --mark is a real background colour rather than a translucent wash, and
       it is in the contrast test as a pair with --text. A highlighter does
       not obscure the ink under it, so a solid fill behind the text is the
       honest model as well as the testable one.

       The instrument is drawn with a background image rather than a clipped or
       rotated box, and that is not a stylistic choice. A marked phrase wraps,
       and on a wrapped inline both clip-path and transform resolve against the
       union of the line fragments, so a two-line mark gets one rectangle
       spanning both lines and the space between them. Backgrounds and radii
       are the only decoration that box-decoration-break can clone per line, so
       the swipe has to live in those. */
    --font-hand: var(--font-mono);
    --hand-variation: normal;
    --mark-image: linear-gradient(var(--mark) 0 0);
    --mark-radius: 0;
    --note-prefix: "";
    --note-rotate: 0deg;
    --note-rule: 0;
    --note-leader: none;

    /* ---- Ground ----------------------------------------------------------
       Two separate grounds, because they answer to different things.

       --texture is full-bleed on <body>: a material the whole page is printed
       on, which is what a paper grain is.

       --grid is drawn on a layer that tracks the content column, because a
       drawing grid that nothing is drawn against is just decoration. Painted
       on <body> it was anchored to the viewport while the content was anchored
       to a centred container, so the two could never line up: the text started
       61px into an 88px column, and that offset drifted with every resize.

       Both are CSS-generated. No images, no external assets. */
    --texture: none;
    --texture-size: auto;
    --grid: none;
    --grid-column: 5rem;

    /* ---- Motion personality ----------------------------------------------
       Each preset moves like the thing it is: hardware snaps, paper drifts.
       All of it is disabled under prefers-reduced-motion. */
    --ease: cubic-bezier(0.22, 1, 0.36, 1);
    --dur-fast: 120ms;
    --dur: 220ms;
    --dur-enter: 420ms;
    --stagger: 70ms;

    /* Only terminal has a cursor to blink. An empty --cursor-content still
       generates a pseudo-element, so this has to be `none` elsewhere or three
       presets run a forever-animation on a box that draws nothing. */
    --cursor-anim: none;

    /* ---- Delight ---------------------------------------------------------
       One per preset, and exactly one.

       Every slot is inert here and answered by a single preset, the same way
       --cursor-content is: the mechanism ships in all four, the performance
       belongs to one. That is what keeps a delight a signature instead of site
       furniture. Two presets answering the same slot would make it neither's,
       and a preset answering several is where delight turns into noise.

       Each one names the object its preset is built on, which is the Earned
       Ornament Rule applied to behaviour rather than to decoration:

         --ink-stroke    workshop  a pen stroke laid under a title, unhurried
         --lamp-*        console   a channel's tally lamp, lit while it is live
         --dimension-*   spec      a drawing dimensioning its own object
         --endmark-*     terminal  the prompt returning at the end of a session

       test/design/stylesheet_test.rb fails the build if a slot loses its inert
       default or is answered by more than one preset. */
    --ink-stroke: transparent;
    --lamp-display: none;
    --lamp-timeline: auto;
    --dimension-display: none;
    --dimension-label: "";
    --endmark-display: none;
    --endmark-content: "";

    /* derived roles: computed from the preset palette, never hand-authored */
    --surface-hover: color-mix(in oklab, var(--surface) 94%, var(--text));
    --accent-subtle: color-mix(in oklab, var(--accent) 14%, var(--bg));
    --accent-fill-hover: color-mix(in oklab, var(--accent-fill) 88%, var(--text));
    --danger-bg: color-mix(in oklab, var(--danger) 12%, var(--bg));
    --danger-border: color-mix(in oklab, var(--danger) 55%, var(--bg));
    --success-bg: color-mix(in oklab, var(--success) 12%, var(--bg));
    --success-border: color-mix(in oklab, var(--success) 55%, var(--bg));
    --focus: var(--accent);

    /* Preset preview swatches, for the switcher chips.

       These are deliberately mode-independent: a chip is a paint chip. It
       stands for the preset's identity, not for how that preset happens to
       render right now, so it does not change under you when you flip modes.
       Each pair is the preset's characteristic surface and accent. Terminal
       shows its dark home, because that is what terminal is. */
    --chip-workshop-1: oklch(0.967 0.015 88);
    --chip-workshop-2: oklch(0.485 0.16 33);
    --chip-console-1: oklch(0.895 0.006 95);
    --chip-console-2: oklch(0.66 0.19 48);
    --chip-spec-1: oklch(0.98 0.003 250);
    --chip-spec-2: oklch(0.5 0.21 27);
    --chip-terminal-1: oklch(0.155 0.012 65);
    --chip-terminal-2: oklch(0.8 0.145 78);
  }

  /* ======================================================================
     PRESET: workshop  (default)

     A marked-up proof. The page is a printed document and somebody has been
     working on it: the operative phrase is under a marker, and there is a note
     in the margin in their own hand.

     That is the argument the site makes, drawn rather than stated. Proof over
     claims means showing the work on the page, and the margin is where the
     thinking sits.

     Three instruments, three faces. The document is set in a grotesque with
     visibly irregular joints over an old-style book serif; the marks are a
     marker and a hand. The previous version of this preset was a humanist sans
     over that same serif on cream, which is the exact aesthetic every
     considered personal site converges on. Nothing here is doing that job by
     reflex any more.

     Still no rules and no chapter numbers: sections are separated by air, and
     the only thing in the margin is a person's note.

     Scene: the printout of a long post on a workbench, marker uncapped,
     mid-morning.
     ====================================================================== */

  [data-theme="workshop"] {
    --l-bg: oklch(0.967 0.015 88);
    --d-bg: oklch(0.215 0.016 62);
    --l-bg-subtle: oklch(0.936 0.02 88);
    --d-bg-subtle: oklch(0.178 0.014 62);
    --l-surface: oklch(0.988 0.008 88);
    --d-surface: oklch(0.258 0.016 62);
    --l-border: oklch(0.862 0.017 84);
    --d-border: oklch(0.365 0.018 62);
    --l-border-strong: oklch(0.608 0.025 84);
    --d-border-strong: oklch(0.56 0.022 62);
    --l-text: oklch(0.253 0.022 58);
    --d-text: oklch(0.93 0.014 86);
    --l-text-muted: oklch(0.482 0.024 58);
    --d-text-muted: oklch(0.73 0.018 76);
    --l-text-faint: oklch(0.6 0.02 58);
    --d-text-faint: oklch(0.61 0.017 76);
    --l-accent: oklch(0.485 0.16 33);
    --d-accent: oklch(0.75 0.13 45);
    --l-accent-hover: oklch(0.405 0.16 33);
    --d-accent-hover: oklch(0.84 0.11 48);
    --l-accent-fill: oklch(0.485 0.16 33);
    --d-accent-fill: oklch(0.75 0.13 45);
    --l-accent-contrast: oklch(0.985 0.008 88);
    --d-accent-contrast: oklch(0.19 0.016 62);
    --l-danger: oklch(0.47 0.17 22);
    --d-danger: oklch(0.72 0.15 22);
    --l-success: oklch(0.47 0.1 150);
    --d-success: oklch(0.74 0.1 150);
    /* the marker. Yellow on stock, and a burnt swipe on the dark one, since a
       highlighter on dark paper is a stain rather than a glow. */
    --l-mark: oklch(0.895 0.175 98);
    --d-mark: oklch(0.44 0.115 72);

    /* type: an irregular grotesque for the document, an old-style book serif
       for reading, a marker face for the hand. Bricolage is drawn with joints
       that do not quite line up, which is the point: it is a face made in a
       workshop rather than a foundry. */
    --font-display: "Bricolage Grotesque", ui-sans-serif, system-ui,
      "Segoe UI Variable Display", Ubuntu, "Helvetica Neue", sans-serif;
    --font-body: "Alegreya", "Iowan Old Style", Charter, "Palatino Linotype",
      Palatino, "Book Antiqua", Georgia, ui-serif, serif;
    --font-mono: "Sometype Mono", ui-monospace, "SF Mono", "Cascadia Mono", Menlo,
      Consolas, monospace;
    --font-hand: "Shantell Sans", "Bradley Hand", "Segoe Print", "Comic Sans MS",
      ui-rounded, cursive;
    /* Informality and bounce, so the margin reads as written rather than set.
       Ignored by every fallback face, which is why the fallbacks are hands. */
    --hand-variation: "INFM" 62, "BNCE" 22;
    --tracking-display: -0.022em;
    --tracking-label: 0.01em;
    --case-label: none;
    --weight-display: 750;
    --leading-body: 1.7;

    /* the marker: a swipe, not a rectangle. The band is laid at a degree and a
       half off level, it stops short of the line box top and bottom the way a
       nib narrower than the type does, and the four corner radii disagree with
       each other so no end lands square. */
    --mark-image: linear-gradient(1.4deg,
        transparent 0 14%,
        var(--mark) 14% 97%,
        transparent 97%);
    --mark-radius: 0.26em 0.08em 0.32em 0.1em / 0.45em 0.16em 0.28em 0.2em;
    --note-rotate: -1.2deg;
    --note-leader: block;

    /* rhythm: unhurried. The most generous of the four. */
    --density: 1.18;
    /* the document's title is set to be read across a room */
    --display-scale: 1.14;

    /* grammar: no rules at all. Sections are separated by air, which is the
       entire idea. A hairline here would make it the same page as spec. */
    --rule-width: 0;
    --rule-style: none;
    --heading-marker: "";
    --heading-marker-display: none;

    /* ground: a faint paper grain, barely above the threshold of noticing */
    --texture: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23g)' opacity='0.032'/%3E%3C/svg%3E");
    --texture-size: 140px 140px;

    /* motion: drifts in, the way a page settles */
    --ease: cubic-bezier(0.16, 1, 0.3, 1);
    --dur: 260ms;
    --dur-enter: 620ms;
    --stagger: 95ms;

    /* delight: the ink stroke. A post title's underline is laid down rather
       than switched on, at this preset's own unhurried drift, and it lifts off
       to the right the way a pen leaves the page. The quietest of the four
       delights, which is right for the quietest preset. */
    --ink-stroke: var(--accent);

    /* shape: soft, near-flat, paper-like */
    --radius: 6px;
    --radius-sm: 4px;
    --radius-lg: 10px;
    --border-width: 1px;
    --shadow: 0 1px 2px oklch(0.27 0.018 60 / 0.05), 0 8px 24px oklch(0.27 0.018 60 / 0.06);

    /* syntax: warm, book-ink hues */
    --hl-comment: oklch(var(--hl-l) 0.018 80);
    --hl-keyword: oklch(var(--hl-l) 0.14 30);
    --hl-string: oklch(var(--hl-l) 0.1 145);
    --hl-number: oklch(var(--hl-l) 0.12 60);
    --hl-name: oklch(var(--hl-l) 0.12 300);
    --hl-builtin: oklch(var(--hl-l) 0.13 50);
    --hl-punct: oklch(var(--hl-l) 0.03 80);
  }

  /* ======================================================================
     PRESET: console  (studio instrument)

     Hardware-panel logic drawn from thirteen years of audio work. Panel grey
     with a signal orange that carries real surface area. Neo-grotesque for
     labels, mono for values and metadata. Machined edges, crisp hard shadow,
     no blur.

     --case-label is uppercase HERE AND ONLY HERE. Small tracked uppercase
     labels are normally AI scaffolding; they are earned in this preset because
     equipment labeling is the named system it is built on. Do not copy the
     pattern into another preset.

     Scene: adjusting a channel on a desk under studio lighting, mid-session.
     ====================================================================== */

  [data-theme="console"] {
    --l-bg: oklch(0.895 0.006 95);
    --d-bg: oklch(0.255 0.006 95);
    --l-bg-subtle: oklch(0.855 0.006 95);
    --d-bg-subtle: oklch(0.205 0.005 95);
    --l-surface: oklch(0.945 0.005 95);
    --d-surface: oklch(0.305 0.006 95);
    --l-border: oklch(0.74 0.008 95);
    --d-border: oklch(0.42 0.008 95);
    --l-border-strong: oklch(0.55 0.01 95);
    --d-border-strong: oklch(0.6 0.01 95);
    --l-text: oklch(0.24 0.008 95);
    --d-text: oklch(0.94 0.005 95);
    --l-text-muted: oklch(0.44 0.01 95);
    --d-text-muted: oklch(0.72 0.008 95);
    --l-text-faint: oklch(0.55 0.01 95);
    --d-text-faint: oklch(0.61 0.008 95);
    --l-accent: oklch(0.46 0.16 42);
    --d-accent: oklch(0.76 0.16 55);
    --l-accent-hover: oklch(0.39 0.15 42);
    --d-accent-hover: oklch(0.84 0.13 58);
    --l-accent-fill: oklch(0.66 0.19 48);
    --d-accent-fill: oklch(0.7 0.185 50);
    --l-accent-contrast: oklch(0.18 0.01 95);
    --d-accent-contrast: oklch(0.18 0.006 95);
    --l-danger: oklch(0.46 0.18 25);
    --d-danger: oklch(0.72 0.16 25);
    --l-success: oklch(0.46 0.11 150);
    --d-success: oklch(0.75 0.11 150);
    /* the mark: a strip of tape across the panel. Level, hard-edged, cut to
       the height of the lettering rather than the line. */
    --l-mark: oklch(0.815 0.15 62);
    --d-mark: oklch(0.415 0.105 55);
    --mark-image: linear-gradient(transparent 0 9%, var(--mark) 9% 89%, transparent 89%);

    /* type: industrial grotesque, geometric mono for values */
    --font-display: "Archivo", "Helvetica Neue", Helvetica, "Nimbus Sans", Arial,
      "Liberation Sans", ui-sans-serif, sans-serif;
    --font-body: "Archivo", "Helvetica Neue", Helvetica, "Nimbus Sans", Arial,
      "Liberation Sans", ui-sans-serif, sans-serif;
    --font-mono: "Azeret Mono", ui-monospace, "SF Mono", "Cascadia Mono", Menlo,
      Consolas, "Liberation Mono", monospace;
    --tracking-display: -0.022em;
    --tracking-label: 0.09em;
    --case-label: uppercase;
    --weight-display: 700;
    --leading-body: 1.6;

    /* rhythm: tight, the way a rack unit is tight */
    --density: 0.88;

    /* grammar: channel numbering over a panel seam. `double` renders as two
       hairlines with a gap, which is a seam between panels, not a border. */
    --rule-width: 3px;
    --rule-style: double;
    --heading-marker: "CH." counter(section, decimal-leading-zero);
    --heading-marker-display: block;

    --texture: none;

    /* motion: hardware does not ease, it snaps. Stepped easing reads as
       segments clicking on rather than anything fading. */
    --ease: steps(2, end);
    --dur: 90ms;
    --dur-enter: 150ms;
    --stagger: 45ms;

    /* delight: the tally lamp. Every channel on a desk carries one and it
       states exactly one thing: this channel is live. Here it lights while its
       section is on screen, which is the same claim about the same object.

       It is not a meter. No levels, no bars, no invented signal: a portfolio
       with a fake VU on it is the failure mode this preset exists to avoid. */
    --lamp-display: inline-block;
    --lamp-timeline: --section-view;

    /* shape: machined, hard-edged, zero blur */
    --radius: 3px;
    --radius-sm: 2px;
    --radius-lg: 4px;
    --border-width: 1px;
    --shadow: 0 1px 0 0 oklch(0.24 0.008 95 / 0.22);

    /* syntax: orange family against neutral panel */
    --hl-comment: oklch(var(--hl-l) 0.012 95);
    --hl-keyword: oklch(var(--hl-l) 0.15 45);
    --hl-string: oklch(var(--hl-l) 0.09 160);
    --hl-number: oklch(var(--hl-l) 0.13 75);
    --hl-name: oklch(var(--hl-l) 0.1 265);
    --hl-builtin: oklch(var(--hl-l) 0.14 30);
    --hl-punct: oklch(var(--hl-l) 0.02 95);
  }

  /* ======================================================================
     PRESET: spec  (technical-spec grid)

     Swiss rigor. Near-monochrome with one signal red. The 1px rule is the
     primary visual device and it is load-bearing, not decoration. Zero radius,
     zero elevation, tight tracking, strong weight contrast.

     Density here comes from type and rules, not from spacing: the spatial
     skeleton is shared across all presets by design.

     Scene: checking a drawing against a spec sheet at a desk, task light on.
     ====================================================================== */

  [data-theme="spec"] {
    --l-bg: oklch(0.98 0.003 250);
    --d-bg: oklch(0.185 0.008 250);
    --l-bg-subtle: oklch(0.945 0.005 250);
    --d-bg-subtle: oklch(0.15 0.007 250);
    --l-surface: oklch(0.995 0.0015 250);
    --d-surface: oklch(0.225 0.009 250);
    --l-border: oklch(0.84 0.008 250);
    --d-border: oklch(0.34 0.012 250);
    --l-border-strong: oklch(0.3 0.012 250);
    --d-border-strong: oklch(0.72 0.01 250);
    --l-text: oklch(0.21 0.012 250);
    --d-text: oklch(0.95 0.004 250);
    --l-text-muted: oklch(0.48 0.012 250);
    --d-text-muted: oklch(0.72 0.008 250);
    --l-text-faint: oklch(0.6 0.01 250);
    --d-text-faint: oklch(0.59 0.008 250);
    --l-accent: oklch(0.52 0.2 27);
    --d-accent: oklch(0.68 0.19 27);
    --l-accent-hover: oklch(0.44 0.19 27);
    --d-accent-hover: oklch(0.77 0.16 27);
    --l-accent-fill: oklch(0.5 0.21 27);
    --d-accent-fill: oklch(0.52 0.21 27);
    --l-accent-contrast: oklch(0.99 0.002 250);
    --d-accent-contrast: oklch(0.98 0.003 250);
    --l-danger: oklch(0.52 0.2 27);
    --d-danger: oklch(0.68 0.19 27);
    --l-success: oklch(0.46 0.11 155);
    --d-success: oklch(0.74 0.11 155);
    /* the mark: the red wash a checker lays over the clause in question */
    --l-mark: oklch(0.855 0.085 25);
    --d-mark: oklch(0.375 0.105 25);

    /* type: contemporary grotesque, tight, high weight contrast */
    --font-display: "Schibsted Grotesk", ui-sans-serif, system-ui, -apple-system,
      "Segoe UI Variable Display", "Segoe UI", Roboto, "Noto Sans", sans-serif;
    --font-body: "Schibsted Grotesk", ui-sans-serif, system-ui, -apple-system,
      "Segoe UI Variable Text", "Segoe UI", Roboto, "Noto Sans", sans-serif;
    --font-mono: "Geist Mono", ui-monospace, "SF Mono", "Cascadia Mono", Menlo,
      Consolas, "Liberation Mono", monospace;
    --tracking-display: -0.032em;
    --tracking-label: 0.02em;
    --case-label: none;
    --weight-display: 700;
    --leading-body: 1.55;

    /* rhythm: the densest of the four. A spec sheet does not waste paper. */
    --density: 0.84;
    --display-scale: 1.05;

    /* a note on a drawing is a callout: it sits under its own hairline, in the
       same hand as every other figure on the sheet. */
    --note-rule: var(--border-width) solid var(--border);

    /* the mark: drawn along a straightedge under the words, not over them.
       A checker rules the clause in question; they do not colour it in. */
    --mark-image: linear-gradient(transparent 0 58%, var(--mark) 58% 100%);

    /* grammar: numbered sections over a hairline. The number is the label. */
    --rule-width: 1px;
    --rule-style: solid;
    --heading-marker: counter(section, decimal-leading-zero);
    --heading-marker-display: block;
    --heading-marker-size: var(--text-sm);

    /* ground: the drawing grid itself, faint but genuinely there. It is drawn
       on the content layer, not the page, so its lines land on the text edge
       and every 6.25rem across the column: exactly 8 columns at full width. */
    --grid: repeating-linear-gradient(to right,
        color-mix(in oklab, var(--border) 60%, transparent) 0 1px,
        transparent 1px var(--grid-column));

    /* motion: measured and quick, like a rule being struck */
    --ease: cubic-bezier(0.25, 1, 0.5, 1);
    --dur: 170ms;
    --dur-enter: 320ms;
    --stagger: 45ms;

    /* delight: the drawing dimensions itself. An overall width, ticked at both
       ends, sitting under the object it measures, exactly where a drawing puts
       one. The figure is checkable against the grid lines behind it: the text
       column is ten of the twelve columns.

       The label restates --rail and --grid-column, so those three move
       together or the annotation starts lying. Components hide it below the
       width where the container stops being twelve whole columns. */
    --dimension-display: block;
    --dimension-label: "10 COL";

    /* shape: sharp, flat, rule-driven */
    --radius: 0;
    --radius-sm: 0;
    --radius-lg: 0;
    --border-width: 1px;
    --shadow: none;

    /* syntax: precise, cool, one warm signal */
    --hl-comment: oklch(var(--hl-l) 0.012 250);
    --hl-keyword: oklch(var(--hl-l) 0.17 27);
    --hl-string: oklch(var(--hl-l) 0.1 160);
    --hl-number: oklch(var(--hl-l) 0.13 255);
    --hl-name: oklch(var(--hl-l) 0.12 290);
    --hl-builtin: oklch(var(--hl-l) 0.12 60);
    --hl-punct: oklch(var(--hl-l) 0.02 250);
  }

  /* ======================================================================
     PRESET: terminal

     Dark-first, monospace throughout. AMBER phosphor, not green: amber reads
     as a 1970s terminal manual and ties to the hardware lineage, where green
     on black lands straight in the AI-startup-neon cliché this site is
     explicitly not.

     HARD BANS inside this preset: no glow, no bloom, no text-shadow, no
     gradient, no scanline overlay, no particles. The restraint is what
     separates it from the thing it resembles.

     Light mode is a line printer on fanfold paper. It is a finished surface,
     not a fallback.

     Scene: reading logs in a dim room late, or the same output on paper.
     ====================================================================== */

  [data-theme="terminal"] {
    --l-bg: oklch(0.955 0.01 90);
    --d-bg: oklch(0.135 0.012 65);
    --l-bg-subtle: oklch(0.92 0.014 90);
    --d-bg-subtle: oklch(0.105 0.01 65);
    --l-surface: oklch(0.975 0.007 90);
    --d-surface: oklch(0.17 0.013 65);
    --l-border: oklch(0.83 0.016 88);
    --d-border: oklch(0.33 0.018 65);
    --l-border-strong: oklch(0.6 0.022 85);
    --d-border-strong: oklch(0.54 0.03 68);
    --l-text: oklch(0.28 0.02 70);
    --d-text: oklch(0.89 0.045 80);
    --l-text-muted: oklch(0.48 0.022 70);
    --d-text-muted: oklch(0.7 0.055 75);
    --l-text-faint: oklch(0.6 0.02 70);
    --d-text-faint: oklch(0.56 0.05 72);
    --l-accent: oklch(0.46 0.115 55);
    --d-accent: oklch(0.8 0.145 78);
    --l-accent-hover: oklch(0.38 0.11 55);
    --d-accent-hover: oklch(0.88 0.12 80);
    --l-accent-fill: oklch(0.5 0.13 55);
    --d-accent-fill: oklch(0.75 0.15 75);
    --l-accent-contrast: oklch(0.97 0.008 90);
    --d-accent-contrast: oklch(0.14 0.012 65);
    --l-danger: oklch(0.47 0.18 25);
    --d-danger: oklch(0.74 0.16 25);
    --l-success: oklch(0.46 0.1 145);
    --d-success: oklch(0.78 0.1 145);
    /* the mark: a filled character cell, the way a terminal selects a run of
       text. It is the one preset that wants the plain fill, so it inherits the
       default image rather than drawing its own. */
    --l-mark: oklch(0.862 0.115 88);
    --d-mark: oklch(0.418 0.105 78);

    /* type: mono, all of it. A wide boxy face for display, a readable one for
       everything that is actually read. */
    --font-display: "Martian Mono", ui-monospace, "SF Mono", "Cascadia Mono",
      Menlo, Consolas, "Liberation Mono", monospace;
    --font-body: "Sometype Mono", ui-monospace, "SF Mono", "Cascadia Mono",
      Menlo, Consolas, "Liberation Mono", monospace;
    --font-mono: "Sometype Mono", ui-monospace, "SF Mono", "Cascadia Mono",
      Menlo, Consolas, "Liberation Mono", monospace;
    --tracking-display: -0.04em;
    --tracking-label: 0.04em;
    --case-label: none;
    --weight-display: 700;
    --leading-body: 1.65;

    /* rhythm: a shade tighter than default, like line spacing in a session */
    --density: 0.94;
    --display-scale: 0.82;

    /* A wide mono fits far fewer words per ch than a sans does, so the shared
       17ch cap pushed this hero to seven lines: past the six-line ceiling where
       Chrome gives up on text-wrap: balance, which is why it was orphaning a
       word. Widening the measure brings it back under the limit. */
    --hero-measure: 21ch;

    /* a note in a session is a comment, and it is marked as one. */
    --note-prefix: "# ";

    /* grammar: a prompt before every heading, dashed rules between sections.
       The prompt is the punctuation; nothing else is needed. */
    --rule-width: 1px;
    --rule-style: dashed;
    --heading-marker: "$";
    --heading-marker-display: block;
    /* Bigger than the other markers because it is a prompt, not a label, and
       right-aligned so it sits beside the heading the way a prompt sits beside
       a command rather than stranded at the far edge of the rail. */
    --heading-marker-size: var(--text-lg);
    --rail-align: right;
    --cursor-content: " ▋";

    --texture: none;

    /* motion: chunky, like a character cell repainting. Stepped, never smooth,
       and never a glow. */
    --ease: steps(3, end);
    --dur: 140ms;
    --dur-enter: 280ms;
    --stagger: 60ms;
    --cursor-anim: blink 1.15s steps(1, end) infinite;

    /* delight: the prompt returns. Finishing a post is the end of a session,
       so it comes back and waits for the next command. It reuses the
       cursor this preset already blinks in the hero rather than inventing a
       second piece of vocabulary, and the two never share a page: a post has
       no hero. */
    --endmark-display: block;
    --endmark-content: "$";

    /* shape: nothing rounded, nothing raised */
    --radius: 0;
    --radius-sm: 0;
    --radius-lg: 0;
    --border-width: 1px;
    --shadow: none;

    /* syntax: near-monochrome amber, separated by chroma rather than hue */
    --hl-comment: oklch(var(--hl-l) 0.02 70);
    --hl-keyword: oklch(var(--hl-l) 0.15 68);
    --hl-string: oklch(var(--hl-l) 0.08 105);
    --hl-number: oklch(var(--hl-l) 0.13 88);
    --hl-name: oklch(var(--hl-l) 0.11 45);
    --hl-builtin: oklch(var(--hl-l) 0.14 55);
    --hl-punct: oklch(var(--hl-l) 0.035 70);
  }

  /* ======================================================================
     MODE RESOLUTION

     Written once; applies to every preset. Three cases, in cascade order:

       1. no data-mode attribute, OS prefers light  → light  (the base rule)
       2. no data-mode attribute, OS prefers dark   → dark   (media query)
       3. data-mode set explicitly                  → wins over the OS

     Case 2 is what keeps the site correct with JavaScript unavailable: the
     server never renders data-mode, so the OS preference decides and no
     visitor gets a surface they did not ask for.
     ====================================================================== */

  :root {
    color-scheme: light;
    --hl-l: 0.43;
    --bg: var(--l-bg);
    --bg-subtle: var(--l-bg-subtle);
    --surface: var(--l-surface);
    --border: var(--l-border);
    --border-strong: var(--l-border-strong);
    --text: var(--l-text);
    --text-muted: var(--l-text-muted);
    --text-faint: var(--l-text-faint);
    --accent: var(--l-accent);
    --accent-hover: var(--l-accent-hover);
    --accent-fill: var(--l-accent-fill);
    --accent-contrast: var(--l-accent-contrast);
    --mark: var(--l-mark);
    --danger: var(--l-danger);
    --success: var(--l-success);
  }

  @media (prefers-color-scheme: dark) {
    :root:not([data-mode="light"]) {
      color-scheme: dark;
      --hl-l: 0.75;
      --bg: var(--d-bg);
      --bg-subtle: var(--d-bg-subtle);
      --surface: var(--d-surface);
      --border: var(--d-border);
      --border-strong: var(--d-border-strong);
      --text: var(--d-text);
      --text-muted: var(--d-text-muted);
      --text-faint: var(--d-text-faint);
      --accent: var(--d-accent);
      --accent-hover: var(--d-accent-hover);
      --accent-fill: var(--d-accent-fill);
      --accent-contrast: var(--d-accent-contrast);
      --mark: var(--d-mark);
      --danger: var(--d-danger);
      --success: var(--d-success);
    }
  }

  :root[data-mode="dark"] {
    color-scheme: dark;
    --hl-l: 0.75;
    --bg: var(--d-bg);
    --bg-subtle: var(--d-bg-subtle);
    --surface: var(--d-surface);
    --border: var(--d-border);
    --border-strong: var(--d-border-strong);
    --text: var(--d-text);
    --text-muted: var(--d-text-muted);
    --text-faint: var(--d-text-faint);
    --accent: var(--d-accent);
    --accent-hover: var(--d-accent-hover);
    --accent-fill: var(--d-accent-fill);
    --accent-contrast: var(--d-accent-contrast);
    --mark: var(--d-mark);
    --danger: var(--d-danger);
    --success: var(--d-success);
  }
}
