/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause. */
/* SPDX-License-Identifier: BSD-3-Clause */
/* pro.vivianvoss.net design system, in the owner's signature: lean, minimal,
   object-oriented, nearly grid-only. The stylesheet is a portfolio piece, so it
   is on show ({K0082}). It is organised in cascade layers whose order is fixed
   once ({K0092}): defaults, then breakpoints, then overrides. The breakpoints
   layer holds one sub-layer per breakpoint in order, so at a wide viewport, where
   every min-width query matches at once, the highest matching sub-layer wins and
   a class carries only the one declaration that changes at a step; the rest is
   inherited mobile-first. Every media query is min-width, never max. The lineage
   is minline: oklch, native nesting, intrinsic auto-fit reflow, no build step. */

@font-face {
    font-family: 'Oxanium';
    src: url('/fonts/oxanium.woff2') format('woff2');
    font-weight: 300 700;
    font-display: swap;
}
@font-face {
    font-family: 'Press Start 2P';
    src: url('/fonts/press-start-2p.woff2') format('woff2');
    font-weight: 400;
    font-display: swap;
}

@layer defaults, breakpoints, overrides;
@layer breakpoints { @layer base, phone, tablet, screen, widescreen; }
@layer overrides   { @layer lib.tailwind, page; }

/* =========================================================================
   DEFAULTS, tokens, reset, element base, and the reusable layout primitives.
   ========================================================================= */
@layer defaults {
    /* One Verbund, one colour world: pro carries advisor's warm palette ({K0081}).
       Green means one thing, this is really there, a measured figure or a built
       capability; amber is its counterpart for what is on the way; the pair is
       told apart by lightness, not hue, so it survives red-blindness. */
    :root {
        color-scheme: light;
        --unit: 0.5rem;
        --accent: oklch(68% 0.18 35);
        --accent-strong: oklch(50% 0.17 35);
        --accent-wash: oklch(96% 0.02 35);
        /* The accent lifted, for the one ground it has to carry itself on: ink. */
        --accent-lift: oklch(74% 0.16 35);
        --bg: oklch(98.5% 0.004 80);
        --surface: oklch(100% 0 0);
        --text: oklch(20% 0 0);
        --muted: oklch(52% 0 0);
        --border: oklch(90% 0 0);
        /* A firmer hairline, for a rule that has to hold its own against a
           heading rather than merely separate two rows. */
        --border-firm: oklch(80% 0.004 80);
        --ok: oklch(60% 0.16 145);
        --ok-strong: oklch(50% 0.16 145);
        /* Green lifted, for the same ground the accent needed lifting for. */
        --ok-lift: oklch(70% 0.15 145);
        --ok-wash: oklch(96% 0.03 145);
        /* The ink family. It was minted for the dark code island and now also
           carries the hero band; it stays in the Verbund's warm hue rather than
           taking the cooler one the design template used, because one colour
           world across the Verbund is the rule ({K0081}). */
        --panel: oklch(22% 0.008 80);
        --panel-2: oklch(27% 0.008 80);
        --panel-line: oklch(34% 0.008 80);
        --panel-muted: oklch(70% 0.005 80);
        --panel-bright: oklch(96% 0.004 80);
        --font-body: 'Oxanium', system-ui, sans-serif;
        --font-mark: 'Press Start 2P', monospace;
        --font-data: ui-monospace, 'SF Mono', monospace;
        /* The owner's five breakpoints, recorded as tokens for reference:
           base 0, phone 260, tablet 560, screen 960, widescreen 1260. */
        --bp-phone: 260px;
        --bp-tablet: 560px;
        --bp-screen: 960px;
        --bp-widescreen: 1260px;
        --measure: min(72rem, calc(100% - var(--unit) * 6));
        /* The masthead's height, so an anchored heading and the sticky chapter
           mark are both measured from the one thing they have to clear. Two
           rows mobile-first, one row from the tablet step. */
        --nav-h: 7rem;
        --prose: 68ch;
    }

    @media (prefers-contrast: more) {
        :root {
            --muted: oklch(35% 0 0);
            --border: oklch(55% 0 0);
            --accent: var(--accent-strong);
            --ok: var(--ok-strong);
        }
    }

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

    /* The page shell is a named-area grid: one element per region, so no layout
       wrapper is added, and a later page grows an aside by editing one template
       string, not the DOM. The middle row takes the slack, so a short page keeps
       its footer at the bottom. The skip link is out of flow and takes no cell. */
    body {
        margin: 0;
        min-height: 100dvh;
        display: grid;
        grid-template-columns: 1fr;
        grid-template-rows: auto 1fr auto;
        grid-template-areas: "nav" "main" "footer";
        background: var(--bg);
        color: var(--text);
        font-family: var(--font-body);
        font-size: 1.0625rem;
        line-height: 1.6;
        -webkit-text-size-adjust: 100%;
        text-size-adjust: 100%;
        & > .nav { grid-area: nav; }
        & > main { grid-area: main; }
        & > .footer { grid-area: footer; }
    }

    h1, h2, h3 { line-height: 1.2; font-weight: 700; }
    h1 { font-size: clamp(2rem, 5vw, 3rem); letter-spacing: -0.01em; }
    h2 { font-size: clamp(1.5rem, 3vw, 2rem); margin-top: calc(var(--unit) * 8); }

    a {
        color: var(--accent-strong);
        text-underline-offset: 0.15em;
        &:hover { text-decoration-thickness: 2px; }
    }

    .lede { font-size: 1.25rem; color: var(--text); }
    .muted { color: var(--muted); }

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

    .skip {
        position: absolute;
        left: -999px;
        &:focus {
            left: var(--unit);
            top: var(--unit);
            z-index: 10;
            padding: var(--unit) calc(var(--unit) * 2);
            background: var(--surface);
            border: 1px solid var(--accent-strong);
        }
    }

    /* --- Layout primitives: the reusable grid the pages are built from ------
       .wrap    the page measure, one column centred.
       .chapter a numbered beat: its mark in a gutter, its body beside. The
                mark sits above the body mobile-first and moves into its own
                sticky track at the screen step, where the rule down the page
                arrives with it. Asymmetric, so it is a named-step decision.
       .auto    a symmetric card set that flows, intrinsically, into as many equal
                columns as its own width allows, reflow without a media query.
       Each is a grid; flex stays for genuine one-dimensional inline clusters. */
    .wrap {
        width: var(--measure);
        margin-inline: auto;
        padding-block: calc(var(--unit) * 6);
    }

    .auto {
        display: grid;
        gap: calc(var(--unit) * 2);
        grid-template-columns: repeat(auto-fit, minmax(min(var(--auto-min, 13rem), 100%), 1fr));
    }
}

/* =========================================================================
   BREAKPOINTS.BASE, every component, mobile-first (0 and up).
   ========================================================================= */
@layer breakpoints.base {
    /* The masthead stays with the reader: a bar the full width of the viewport
       with a rule under it, the measure held by the wrap inside. It is blurred
       rather than opaque, so the hero's ruled ground shows through as it passes
       beneath. Mobile-first the routes are its second row. */
    .nav {
        position: sticky;
        top: 0;
        z-index: 8;
        background: color-mix(in oklab, var(--bg) 94%, transparent);
        backdrop-filter: blur(8px);
        border-bottom: 1px solid var(--border);
        & > .wrap {
            display: grid;
            grid-template-columns: minmax(0, 1fr);
            align-items: center;
            gap: calc(var(--unit) * 1.5);
            padding-block: calc(var(--unit) * 2);
        }
    }
    /* The mark is an icon beside text: a genuine inline cluster, so flex. */
    .mark {
        white-space: nowrap;
        display: inline-flex;
        align-items: center;
        gap: calc(var(--unit) * 1.5);
        font-family: var(--font-mark);
        font-size: 0.9rem;
        color: var(--text);
        text-decoration: none;
        letter-spacing: 0.02em;
        & em { color: var(--accent-strong); font-style: normal; }
    }
    .mark-logo {
        height: 1.6em;
        width: auto;
        /* The cursor blinks in one step, never a fade: a terminal caret does not
           dim. Stilled, not hidden, when the reader asks for less motion. */
        & .cursor { animation: cursor-blink 1.2s steps(1) infinite; }
    }
    @keyframes cursor-blink { 50% { opacity: 0; } }
    @media (prefers-reduced-motion: reduce) { .mark-logo .cursor { animation: none; } }
    /* The routes: a row of tracks, not a run of inline text, so the gap is one
       declaration and the row survives a link being added or removed. It
       scrolls rather than wraps, because a masthead that grows a second line
       under the reader's thumb is worse than one that slides. */
    .nav-links {
        display: grid;
        grid-auto-flow: column;
        grid-auto-columns: max-content;
        gap: calc(var(--unit) * 2.5);
        font-size: 0.9rem;
        overflow-x: auto;
        & a {
            color: var(--muted);
            text-decoration: none;
            padding-block: calc(var(--unit) * 0.5);
            border-bottom: 2px solid transparent;
            &:hover { color: var(--text); border-bottom-color: var(--accent); }
        }
    }

    /* ---- The hero: an ink band the full width of the viewport, ruled like
       drawing paper by two gradients. No image, no element, nothing to load.
       The page's own components sit on it and are recoloured by it rather than
       replaced by ink-prefixed twins. ---- */
    .hero {
        background: var(--panel);
        color: var(--panel-bright);
        border-bottom: 1px solid var(--panel-line);
        background-image:
            linear-gradient(to right, oklch(100% 0 0 / 0.035) 1px, transparent 1px),
            linear-gradient(to bottom, oklch(100% 0 0 / 0.035) 1px, transparent 1px);
        background-size: 2.5rem 2.5rem;
        & > .wrap {
            display: grid;
            gap: calc(var(--unit) * 5);
            padding-block: calc(var(--unit) * 7) calc(var(--unit) * 6);
        }
        /* The eyebrow is a path, not a sentence: the product, its kind, its
           standing, in the data face with the separators dimmed to the rule. */
        & .eyebrow {
            font-family: var(--font-data);
            font-size: 0.72rem;
            letter-spacing: 0.09em;
            text-transform: uppercase;
            color: var(--panel-muted);
            display: grid;
            grid-auto-flow: column;
            grid-auto-columns: max-content;
            justify-content: start;
            gap: calc(var(--unit) * 1.5);
            & b { color: var(--accent-lift); font-weight: 400; }
            & i { font-style: normal; color: var(--panel-line); }
        }
        & .tool-head { margin: 0; gap: calc(var(--unit) * 3); }
        /* The name is set in the mark face. It is a wordmark before it is a
           heading, and the pixel face is what the rest of the Verbund knows it
           by, so it is wider and lighter than a headline would be. */
        & .tool-name {
            font-family: var(--font-mark);
            font-size: clamp(1.75rem, 8vw, 3.4rem);
            letter-spacing: -0.01em;
            color: var(--panel-bright);
        }
        /* The claim carries its weight in size, not in stroke. */
        & .tool-claim {
            color: var(--panel-bright);
            font-weight: 400;
            font-size: clamp(1.5rem, 4.4vw, 2.6rem);
            line-height: 1.22;
            max-width: 30ch;
        }
        & .lede { color: var(--panel-muted); max-width: 62ch; font-size: 1.15rem; }
        & .tech-pills {
            margin: 0;
            & li {
                font-size: 0.68rem;
                letter-spacing: 0.08em;
                text-transform: uppercase;
                padding: calc(var(--unit) * 0.5) var(--unit);
                border-radius: 0;
                color: var(--panel-muted);
                border-color: var(--panel-line);
            }
        }
    }

    /* The specification as a strip of cells across the hero's foot, so the
       facts arrive before the argument instead of after it. The hairline is
       the gap, the same trick the built-on cells use. */
    .strip {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
        gap: 1px;
        margin: 0;
        background: var(--panel-line);
        border: 1px solid var(--panel-line);
        & > div {
            background: var(--panel);
            display: grid;
            gap: calc(var(--unit) * 0.35);
            align-content: start;
            padding: calc(var(--unit) * 2);
        }
        & dt {
            font-family: var(--font-data);
            font-size: 0.64rem;
            letter-spacing: 0.11em;
            text-transform: uppercase;
            color: var(--panel-muted);
        }
        & dd { margin: 0; font-size: 0.98rem; line-height: 1.35; color: var(--panel-bright); }
    }

    /* ---- A chapter: its mark in a gutter, its body beside. Mobile-first the
       mark sits above its body; at the screen step it takes its own track and
       the rule that runs down the page appears with it, and it stays put while
       the body scrolls past, so a reader always knows which step they are in. */
    .chapter {
        border-bottom: 1px solid var(--border);
        & > .wrap {
            display: grid;
            grid-template-columns: minmax(0, 1fr);
            gap: calc(var(--unit) * 3);
            padding-block: calc(var(--unit) * 7);
        }
    }
    .chapter-mark {
        display: grid;
        gap: calc(var(--unit) * 0.5);
        align-content: start;
        margin: 0;
        & .n { font-family: var(--font-mark); font-size: 1.1rem; line-height: 1; color: var(--accent-strong); }
        /* A continuation keeps its parent's weight but not its emphasis. */
        &.cont .n { color: var(--muted); }
        /* A reference chapter is not a step in the argument, so its mark is a
           word in the data face rather than the next number in the sequence. */
        &.ref .n {
            font-family: var(--font-data);
            font-size: 0.68rem;
            letter-spacing: 0.11em;
            text-transform: uppercase;
            color: var(--muted);
        }
        & .t {
            border-top: 1px solid var(--border-firm);
            padding-top: var(--unit);
            font-family: var(--font-data);
            font-size: 0.72rem;
            letter-spacing: 0.08em;
            text-transform: uppercase;
            color: var(--muted);
        }
    }
    /* A chapter on ink. It sets no component rules at all: it redefines the
       semantic tokens, and every box, table, chart and badge inside it follows,
       because they were all written against those names rather than against
       colours. That is the whole argument for a token layer, made visible. */
    .chapter.on-ink {
        --bg: var(--panel);
        --surface: var(--panel-2);
        --text: var(--panel-bright);
        --muted: var(--panel-muted);
        --border: var(--panel-line);
        --border-firm: var(--panel-muted);
        --accent: var(--accent-lift);
        --accent-strong: var(--accent-lift);
        --accent-wash: oklch(26% 0.035 35);
        --ok: var(--ok-lift);
        --ok-strong: var(--ok-lift);
        background: var(--panel);
        color: var(--panel-bright);
        border-bottom-color: var(--panel-line);
        & .panel { border-color: var(--panel-muted); }
    }

    .chapter-body {
        display: grid;
        gap: calc(var(--unit) * 4);
        align-content: start;
        /* The rhythm is the grid gap, so nothing inside carries a margin of its
           own; without this a chapter reads at four times the leading of its
           neighbour depending on whether its prose happens to sit in a wrapper. */
        & > p, & > ul, & > ol, & > pre, & > div > p { margin-block: 0; }
        & > div { display: grid; gap: calc(var(--unit) * 2.5); }
        & > p, & > div > p, & > ul, & > ol { max-width: var(--prose); }
        & > :first-child { margin-top: 0; }
        & > h2 { margin-top: 0; }
        & > h3 { font-size: 1.25rem; margin: calc(var(--unit) * 2) 0 0; }
    }

    /* Footer: pro runs on ZERVO+ too; the maker line is the point, not a note. */
    .footer {
        width: var(--measure);
        margin-inline: auto;
        padding-block: calc(var(--unit) * 6);
        border-top: 1px solid var(--border);
        color: var(--muted);
        font-size: 0.9rem;
        & a { color: var(--text); }
        & .pill {
            display: inline-block;
            padding: 0 0.5em;
            border: 1px solid var(--border);
            border-radius: 4px;
            font-family: var(--font-data);
            font-size: 0.85em;
        }
        & .made { margin-top: calc(var(--unit) * 1.5); }
    }
    .powered { font-size: 0.85rem; }
    .footer-line { margin: 0 0 calc(var(--unit) * 1.5); }

    .eyebrow {
        font-family: var(--font-mark);
        font-size: 0.72rem;
        letter-spacing: 0.03em;
        color: var(--muted);
        & b { color: var(--accent-strong); font-weight: 400; }
    }

    /* --- The roadmap: one card per tool. All the tools are built, so the card
       with a page is detected by the link it contains, :has(a), not a class; the
       rest are desaturated and dimmed so the live one stands out. ({K0089}) */
    .roadmap {
        list-style: none;
        padding: 0;
        margin-top: calc(var(--unit) * 4);
        display: grid;
        gap: calc(var(--unit) * 2);
    }
    .tool {
        border: 1px solid var(--border);
        background: var(--surface);
        &:has(a) { border-color: var(--accent); }
        &:not(:has(a)) { filter: grayscale(1); opacity: 0.7; }
    }
    @media (prefers-contrast: more) { .tool:not(:has(a)) { filter: none; opacity: 1; } }
    /* The card is a vertical stack whose foot is pushed to the bottom: a grid of
       three rows, the last one taking the slack and holding its child at its end. */
    .tool-inner {
        display: grid;
        grid-template-rows: auto auto 1fr;
        gap: calc(var(--unit) * 1.5);
        padding: calc(var(--unit) * 3);
        height: 100%;
        color: inherit;
        text-decoration: none;
        &:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: -2px; }
    }
    /* Name and badge, a genuine inline cluster that may wrap: flex. */
    .tool-head-row {
        display: flex;
        align-items: baseline;
        flex-wrap: wrap;
        gap: calc(var(--unit) * 1.5);
    }
    /* Uppercase for display only: the name stays cased in the markup. */
    .tool-title {
        font-family: var(--font-body);
        font-size: 1.25rem;
        font-weight: 700;
        color: var(--accent-strong);
        text-transform: uppercase;
        letter-spacing: 0.02em;
    }
    .tool-what { margin: 0; color: var(--muted); font-size: 0.95rem; & code { font-family: var(--font-data); font-size: 0.9em; } }
    .tool-go {
        align-self: end;
        font-family: var(--font-data);
        font-size: 0.8rem;
        letter-spacing: 0.06em;
        text-transform: uppercase;
        color: var(--accent-strong);
        &::after { content: " \2192"; }
    }
    a.tool-inner:hover .tool-go { text-decoration: underline; text-underline-offset: 3px; }
    /* The tool is built; this line is about the page, not the product. */
    .tool-pending {
        align-self: end;
        font-family: var(--font-data);
        font-size: 0.8rem;
        letter-spacing: 0.06em;
        text-transform: uppercase;
        color: var(--muted);
        border-top: 1px dashed var(--border);
        padding-top: calc(var(--unit) * 1.5);
    }

    /* =====================================================================
       TOOL PAGE, the instruments a page is built from.
       ===================================================================== */

    /* The head: the product is named before it is praised, both in one h1. */
    .tool-head { display: grid; gap: calc(var(--unit) * 1.5); margin: calc(var(--unit) * 2) 0 0; }
    .tool-name { font-size: clamp(2.75rem, 7vw, 4.25rem); line-height: 1; letter-spacing: -0.02em; color: var(--accent-strong); }
    .tool-claim { font-size: clamp(1.375rem, 3.2vw, 2.125rem); line-height: 1.15; letter-spacing: -0.01em; color: var(--text); }

    /* Technology pills: a repo's badge row, quiet. A genuine inline cluster. */
    .tech-pills {
        list-style: none;
        padding: 0;
        margin: calc(var(--unit) * 3) 0 0;
        display: flex;
        flex-wrap: wrap;
        gap: var(--unit);
        & li {
            font-family: var(--font-data);
            font-size: 0.78rem;
            color: var(--muted);
            border: 1px solid var(--border);
            border-radius: 3px;
            padding: calc(var(--unit) * 0.25) calc(var(--unit) * 1);
            white-space: nowrap;
        }
    }

    /* The schematic, built from boxes, not coordinates: every part a real element
       with padding and a gap, so nothing overlaps and it reflows on a phone. */
    .schema {
        display: grid;
        gap: calc(var(--unit) * 6);
        margin: 0;
        & > figcaption { grid-column: 1 / -1; max-width: var(--prose); color: var(--muted); font-size: 0.9rem; }
    }
    .schema-col { display: grid; gap: calc(var(--unit) * 3); align-content: start; }
    /* Head and foot bracket each column: which side this is, and what that side
       leaves you carrying. The plate between them says only how much. */
    .schema-head, .schema-foot {
        font-family: var(--font-data);
        font-size: 0.75rem;
        text-transform: uppercase;
        letter-spacing: 0.11em;
        color: var(--muted);
        margin: 0;
        &.on { color: var(--ok-strong); }
    }

    .box {
        border: 1px solid var(--border);
        background: var(--surface);
        padding: calc(var(--unit) * 2.5);
        display: grid;
        gap: calc(var(--unit));
        &.dashed { border-style: dashed; }
        &.inner { background: var(--bg); }
        /* A box holding a set of equal inner blocks is a symmetric card set, so
           it reflows on its own width with no media query; the label spans. */
        &.set {
            grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
            & > .box-label { grid-column: 1 / -1; }
        }
    }
    .box-label { font-family: var(--font-data); font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); }
    .box-name { font-family: var(--font-data); font-size: 0.95rem; font-weight: 600; }
    .box .line { font-family: var(--font-data); font-size: 0.85rem; color: var(--muted); margin: 0; }
    /* A box head: caption left, annotation right, a genuine inline row. */
    .box-head { display: flex; flex-wrap: wrap; gap: var(--unit) calc(var(--unit) * 3); justify-content: space-between; }

    /* Boxes that open. Native <details>, so they work before any script and print
       open. The summary is an inline row; the counter sits at its right. */
    details.box {
        padding: 0;
        gap: 0;
        & > summary {
            list-style: none;
            cursor: pointer;
            padding: calc(var(--unit) * 2) calc(var(--unit) * 2.5);
            display: grid;
            grid-template-columns: minmax(0, 1fr) max-content max-content;
            align-items: center;
            gap: calc(var(--unit) * 1.5);
            &::-webkit-details-marker { display: none; }
            /* The marker is drawn, not typed: two borders on a square, turned. */
            &::after {
                content: "";
                width: 0.5rem;
                height: 0.5rem;
                border-right: 1.5px solid var(--muted);
                border-bottom: 1.5px solid var(--muted);
                rotate: 45deg;
                transition: rotate 140ms ease;
            }
            &:hover { background: var(--accent-wash); }
            &:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: -2px; }
        }
        &[open] > summary::after { rotate: 225deg; }
        &[open] > .box-body {
            border-top: 1px solid var(--border);
            padding: calc(var(--unit) * 2.5);
            display: grid;
            gap: calc(var(--unit) * 2);
            margin: 0;
        }
        /* A list that rules itself needs no padding around it: its rows reach
           the box's own edges and the hairlines run the full width. */
        &[open] > ul.box-body { padding: 0; gap: 1px; }
    }
    /* The artefact: marked by colour alone, its row exactly as tall as the rest,
       so the weight comes from the counter, not from a bigger box. */
    .box.artefact {
        border-color: var(--accent);
        background: var(--accent-wash);
    }
    .box.subject {
        border-color: var(--ok);
        background: var(--ok-wash);
    }

    /* A note beside an instrument: what the instrument is not. */
    .box.note {
        border-color: var(--border-firm);
        & p { margin: 0; color: var(--muted); font-size: 0.92rem; line-height: 1.5; }
    }
    details.box.subject {
        padding: 0;
        gap: 0;
        & > summary { background: var(--ok-wash); }
        & > summary > .box-name { color: var(--ok-strong); }
        & > summary::after { border-color: var(--ok-strong); }
        & > .box-body { border-top-color: var(--ok); }
    }

    .count { font-family: var(--font-data); font-size: 0.8rem; color: var(--muted); }

    /* The kind of figure, said in words. It carries the caveat the colours can
       only imply, which is what lets the subject column be marked green without
       green meaning measured here. */
    .kind {
        grid-column: 1 / -1;
        justify-self: start;
        display: inline-grid;
        grid-auto-flow: column;
        align-items: center;
        gap: var(--unit);
        margin: 0;
        border: 1px solid var(--accent);
        color: var(--accent-strong);
        font-family: var(--font-data);
        font-size: 0.72rem;
        letter-spacing: 0.08em;
        text-transform: uppercase;
        padding: calc(var(--unit) * 0.6) calc(var(--unit) * 1.25);
        &::before { content: ""; width: 0.5rem; height: 0.5rem; background: var(--accent); }
        /* Measured is the one kind that earns the green. */
        &.on {
            border-color: var(--ok);
            color: var(--ok-strong);
            &::before { background: var(--ok); }
        }
    }

    /* The column plate: a figure, the same figure drawn as cells, and what it
       counts, in one card that heads its column. It is a plate rather than three
       loose things because it is the column's own verdict, and a verdict that
       floats reads as a caption. */
    .tally {
        display: grid;
        grid-template-columns: max-content max-content minmax(0, 1fr);
        align-items: center;
        gap: calc(var(--unit) * 2.5);
        padding: calc(var(--unit) * 2.25);
        border: 1px solid var(--border-firm);
        background: var(--surface);
        /* The subject's plate is marked green, and the tag above says the
           figure is counted rather than measured, so the word carries the kind
           and the colour carries only which side this is. */
        &.on {
            border-color: var(--ok);
            background: var(--ok-wash);
            & .tally-n { color: var(--ok-strong); }
            & .tally-units i { background: var(--ok); opacity: 1; }
        }
        & .of { display: grid; gap: calc(var(--unit) * 0.75); }
    }
    .tally-n { font-family: var(--font-mark); font-size: 2.1rem; line-height: 1; color: var(--muted); }
    /* Always two rows, filled column by column: one top, two bottom, three top.
       Every count is then the same height, so the plates that head the two
       columns line up and the comparison is between widths, not between boxes. */
    .tally-units {
        display: grid;
        grid-auto-flow: column;
        grid-template-rows: repeat(2, 0.55rem);
        /* The columns are fixed, not auto: an auto track stretches into free
           space, which would spread sixteen cells across the whole cell rather
           than packing them into the block the eye is meant to measure. */
        grid-auto-columns: 0.55rem;
        justify-content: start;
        gap: 3px;
        & i { display: block; background: var(--muted); opacity: 0.45; }
    }
    .tally-label { font-size: 0.95rem; line-height: 1.35; color: var(--text); }

    /* A list of parts is prose that numbers itself, so the last numeral equals
       the counter above it. The number wears the counter's own frame. */
    ul.box-body {
        list-style: none;
        padding-left: 0;
        display: grid;
        gap: 1px;
        background: var(--border);
        & li {
            background: var(--surface);
            padding: calc(var(--unit) * 1.75) calc(var(--unit) * 2.5);
            display: grid;
            gap: calc(var(--unit) * 0.4);
            color: var(--muted);
            font-size: 0.95rem;
            & b { color: var(--text); font-weight: 600; font-size: 0.98rem; }
            & span { color: var(--muted); font-size: 0.9rem; line-height: 1.45; text-wrap: pretty; }
        }
    }

    /* The architecture, where its own prose explains it. */
    .arch {
        display: grid;
        gap: calc(var(--unit) * 2);
        & .box { gap: calc(var(--unit) * 1.5); }
        & p { margin: 0; font-size: 0.95rem; color: var(--muted); max-width: none; }
        & .box.inner { margin-top: calc(var(--unit)); }
    }

    .stack { display: grid; gap: calc(var(--unit) * 4); grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr)); align-items: start; }
    .stack-group {
        & h3 { font-family: var(--font-data); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); font-weight: 400; margin: 0 0 calc(var(--unit) * 3); }
        & dl {
            margin: 0;
            display: grid;
            gap: 1px;
            background: var(--border);
            border: 1px solid var(--border);
            & > div { background: var(--surface); padding: calc(var(--unit) * 1.75) calc(var(--unit) * 2.5); display: grid; gap: calc(var(--unit) * 0.3); }
        }
        & dt { font-family: var(--font-data); font-size: 0.85rem; font-weight: 600; color: var(--accent-strong); }
        & dd { margin: 0; color: var(--muted); font-size: 0.82rem; line-height: 1.45; }
    }

    /* What joins two cards is a strip that says what the join is, not an arrow
       that leaves the reader to guess. It is the same hairline the cards use. */
    .link {
        border: 1px solid var(--border);
        border-top: 0;
        border-bottom: 0;
        padding: calc(var(--unit) * 1.25) calc(var(--unit) * 2.5);
        & .what {
            margin: 0;
            font-family: var(--font-data);
            font-size: 0.7rem;
            text-transform: uppercase;
            letter-spacing: 0.1em;
            color: var(--muted);
        }
        & .stem, & .head { display: none; }
    }
    /* The cards and their joins form one column with no gaps, so the rules meet. */
    .arch { gap: 0; & > .box + .link, & > .link + .box { margin-top: 0; } }

    /* Metric grid: the honesty rule made visual, colour and form saying the same. */
    /* The measured figures as one strip of cells, hairline-ruled, the way the
       specification strip in the hero is built. The kind is said once, by the
       tag above, so no cell has to carry a badge of its own. */
    .metric-grid {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(min(9rem, 100%), 1fr));
        gap: 1px;
        background: var(--border);
        border: 1px solid var(--border);
    }
    .metric {
        background: var(--surface);
        padding: calc(var(--unit) * 2.5);
        display: grid;
        gap: calc(var(--unit) * 0.5);
        align-content: start;
        & .value { font-family: var(--font-mark); font-size: 1.35rem; line-height: 1.2; color: var(--ok-strong); }
        & .label { font-size: 0.8rem; color: var(--muted); }
        & .tag { display: none; }
    }
    .metric-source { font-family: var(--font-data); font-size: 0.82rem; color: var(--muted); margin-top: calc(var(--unit) * 2); }

    .pill {
        font-size: 0.85rem;
        color: var(--muted);
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: calc(var(--unit) * 2);
        padding: calc(var(--unit) * 0.25) calc(var(--unit) * 1.75);
        line-height: 1.5;
        &.on { color: var(--accent-strong); border-color: var(--accent); }
    }

    /* Reject list. */
    .rejects {
        list-style: none;
        padding: 0;
        margin: 0;
        display: grid;
        gap: calc(var(--unit) * 1.5);
        & li { padding-left: calc(var(--unit) * 4); position: relative; color: var(--muted); max-width: 46rem; &::before { content: "\2022"; position: absolute; left: 0; color: var(--accent); font-family: var(--font-data); } }
        & b { color: var(--text); font-weight: 700; }
    }

    /* The dark island. */
    /* Real preformatted text, so what the binary prints is in the markup as the
       binary prints it, with no <br> and no non-breaking spaces standing in for
       indentation. Every panel then behaves alike: it scrolls, never wraps. */
    .panel {
        background: var(--panel);
        border: 1px solid var(--panel-line);
        color: var(--panel-bright);
        font-family: var(--font-data);
        font-size: 0.85rem;
        line-height: 1.6;
        padding: calc(var(--unit) * 3);
        margin: 0;
        white-space: pre;
        overflow-x: auto;
        & .dim { color: var(--panel-muted); }
    }

    .chart {
        margin: calc(var(--unit) * 4) 0 0;
        max-width: 44rem;
        & svg { display: block; width: 100%; height: auto; overflow: visible; }
        & figcaption { margin-top: calc(var(--unit) * 2); font-size: 0.85rem; color: var(--muted); }
    }
    /* A pair of related charts: stacked mobile-first, side by side at the screen
       step, where a monitor has the width for two plots at their own scale. */
    .chart-pair {
        display: grid;
        gap: calc(var(--unit) * 5);
        margin-top: calc(var(--unit) * 4);
        align-items: start;
        & .chart { margin: 0; max-width: none; }
        /* The table beside a chart: a visible column, not a disclosure. */
        & .table-view { margin: 0; & .chart-title { margin-bottom: calc(var(--unit) * 3); } }
    }
    .chart-title { font-family: var(--font-body); font-size: 1.05rem; font-weight: 700; margin: 0 0 calc(var(--unit) * 2); }
    /* The legend: a genuine inline cluster of keys. */
    .chart-legend { display: flex; flex-wrap: wrap; gap: var(--unit) calc(var(--unit) * 4); margin-bottom: calc(var(--unit) * 3); font-family: var(--font-data); font-size: 0.8rem; color: var(--muted); }
    .key { display: inline-flex; align-items: center; gap: var(--unit); &::before { content: ""; width: 18px; height: 2px; background: var(--muted); border-radius: 1px; } &.on::before { background: var(--accent); } }

    .grid { stroke: var(--border); stroke-width: 1; }
    .axis { stroke: var(--muted); stroke-width: 1; }
    .ln { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; &.base { stroke: var(--muted); } &.on { stroke: var(--accent); } }
    .dot { stroke: var(--surface); stroke-width: 2; &.base { fill: var(--muted); } &.on { fill: var(--accent); } }
    .tick, .axis-name { font-family: var(--font-data); font-size: 13px; fill: var(--muted); font-variant-numeric: tabular-nums; }
    .axis-name { letter-spacing: 0.08em; text-transform: uppercase; font-size: 12px; }
    .end { font-family: var(--font-body); font-size: 20px; font-weight: 700; fill: var(--text); }
    .end-sub { font-family: var(--font-data); font-size: 12px; fill: var(--muted); }
    .table-view { margin-top: calc(var(--unit) * 4); }

    /* Bars: a measured figure filled in green, an estimate an unfilled dashed
       outline, the caveat a property of the mark. */
    .bar.meas { fill: var(--ok); }
    .bar.est { fill: none; stroke: var(--muted); stroke-width: 1.5; stroke-dasharray: 5 4; }
    .bar.subj { fill: var(--accent); }
    .bar-name { font-family: var(--font-body); font-size: 15px; font-weight: 600; fill: var(--text); }
    .bar-tag { font-family: var(--font-data); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; fill: var(--muted); }
    .bar-val { font-family: var(--font-body); font-size: 17px; font-weight: 700; fill: var(--text); }

    /* Feature table. */
    .table-wrap { overflow-x: auto; margin-top: calc(var(--unit) * 3); }
    .features {
        border-collapse: collapse;
        width: 100%;
        font-size: 0.95rem;
        & th, & td { text-align: left; vertical-align: top; padding: calc(var(--unit) * 1.5) calc(var(--unit) * 2); border-bottom: 1px solid var(--border); }
        & thead th { font-family: var(--font-data); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); font-weight: 400; border-bottom-color: var(--text); }
        & tbody th { font-weight: 700; }
        /* Only the status badge resists wrapping, and it is found by what it
           holds rather than by its position: the register's tables have two
           columns, so a last-child rule would set their prose in one line. */
        & td:has(> .st) { white-space: nowrap; }
    }

    /* A region that scrolls has to be reachable without a pointer, so both of
       the page's scroll containers take a tab stop and say what they are. */
    .table-wrap, .panel {
        &:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: 2px; }
    }
    .st {
        font-family: var(--font-data);
        font-size: 0.72rem;
        text-transform: uppercase;
        letter-spacing: 0.06em;
        border: 1px solid var(--border);
        border-radius: 3px;
        padding: 0 calc(var(--unit) * 0.75);
        color: var(--muted);
        white-space: nowrap;
        /* Built and measured, this is really there: the green claim as an outline,
           border and text, never a solid block, lighter on the page. */
        &.on { border-color: var(--ok-strong); color: var(--ok-strong); }
        /* Not built: grey, and dashed, because a dashed outline means the same
           thing here as it does on the charts, that there is nothing solid
           behind it. The word in the badge says which kind of absence. */
        &.no { border-style: dashed; }
    }

    /* A feature in depth, both sides of its trade at equal weight, only the top
       rule differing: a cost drawn smaller than its gain is a sales sheet. */
    .deep { margin-top: calc(var(--unit) * 8); & h3 { font-size: 1.25rem; margin: 0 0 calc(var(--unit) * 2); } & > p { max-width: 46rem; } }
    .trade { display: grid; gap: calc(var(--unit) * 4); margin-top: calc(var(--unit) * 4); }
    .trade-side {
        border-top: 3px solid var(--border);
        padding-top: calc(var(--unit) * 2);
        &.wins { border-top-color: var(--accent); }
        &.costs { color: var(--muted); }
        & h4 { font-family: var(--font-data); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); margin: 0 0 calc(var(--unit) * 2); font-weight: 400; }
        & ul { margin: 0; padding-left: calc(var(--unit) * 2.5); display: grid; gap: calc(var(--unit) * 1.5); }
    }

    .jump-head {
        font-family: var(--font-data);
        font-size: 0.72rem;
        letter-spacing: 0.08em;
        text-transform: uppercase;
        font-weight: 400;
        color: var(--muted);
        margin: calc(var(--unit) * 5) 0 calc(var(--unit) * 1.5);
    }
    .jump-head + .jump { margin-top: 0; }

    /* The section index: the page is long, so it says what it holds before it
       says it. Built like .stack-group's cells, a hairline grid whose gap is the
       border, so it needs no new colour and no new idea. Harvested 2026-07-25. */
    .jump {
        list-style: none;
        padding: 0;
        margin: calc(var(--unit) * 5) 0 0;
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(min(13.5rem, 100%), 1fr));
        gap: 1px;
        background: var(--border);
        border: 1px solid var(--border);
        & a {
            background: var(--surface);
            padding: calc(var(--unit) * 2) calc(var(--unit) * 2.5);
            display: grid;
            gap: calc(var(--unit) * 0.5);
            align-content: start;
            height: 100%;
            color: var(--text);
            text-decoration: none;
            font-size: 0.95rem;
            font-weight: 600;
            &:hover { background: var(--accent-wash); color: var(--accent-strong); }
            &:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: -2px; }
        }
        & .n { font-family: var(--font-data); font-size: 0.68rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); font-weight: 400; }
        & .g { font-size: 0.82rem; color: var(--muted); font-weight: 400; line-height: 1.45; }
    }

    @media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }
}

/* =========================================================================
   BREAKPOINTS, the per-step deltas. Each class carries only what changes at
   its step; the sub-layer order makes the highest matching breakpoint win.
   ========================================================================= */

/* tablet, 560 and up: the tool cards pair. */
@layer breakpoints.tablet {
    @media (min-width: 560px) {
        .roadmap { grid-template-columns: 1fr 1fr; }
        /* The routes rejoin the crest on one line. */
        :root { --nav-h: 4.25rem; }
        .nav > .wrap { grid-template-columns: minmax(0, 1fr) minmax(0, max-content); }
        .hero > .wrap { padding-block: calc(var(--unit) * 10) calc(var(--unit) * 8); }
        .chapter > .wrap { padding-block: calc(var(--unit) * 9); }
    }
}

/* screen, 960 and up: the paired charts sit side by side, each at its own scale. */
@layer breakpoints.screen {
    @media (min-width: 960px) {
        /* The chapter mark moves into its own track and stays there while the
           body scrolls past it; the rule down the page arrives with it. */
        .chapter > .wrap {
            grid-template-columns: 8.5rem minmax(0, 1fr);
            gap: calc(var(--unit) * 6);
            padding-block: calc(var(--unit) * 11);
        }
        .chapter-mark {
            border-right: 1px solid var(--border);
            padding-right: calc(var(--unit) * 3);
            position: sticky;
            top: calc(var(--nav-h) + var(--unit) * 3);
            align-self: start;
            & .n { font-size: 1.3rem; }
        }
        .hero > .wrap { padding-block: calc(var(--unit) * 13) calc(var(--unit) * 9); }
        .chart-pair { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: calc(var(--unit) * 6); }
    }
}

/* widescreen, 1260 and up: the two-track sections split, claim beside instrument,
   and the schematic pairs its columns. The asymmetric split (a fixed claim, an
   elastic instrument) is a viewport decision at the owner's named step. */
@layer breakpoints.widescreen {
    @media (min-width: 1260px) {
        .chapter > .wrap { grid-template-columns: 11rem minmax(0, 1fr); }
        .trade { grid-template-columns: 1fr 1fr; column-gap: calc(var(--unit) * 8); }
        .schema { grid-template-columns: 1fr 1fr; column-gap: calc(var(--unit) * 8); align-items: start; }
    }
}

/* =========================================================================
   OVERRIDES, the highest layer. A page-scoped tweak lives in overrides.page;
   a migration lib in overrides.lib.tailwind, switchable off as a whole once the
   move is complete. Empty until earned.
   ========================================================================= */
@layer overrides.lib.tailwind {}

@layer overrides.page {
    /* Long lines set better ragged than gappy, and a heading should not leave a
       single word on its last line. Two declarations, no markup. Harvested from
       the parallel design pass, 2026-07-25. */
    p, li, dd, figcaption { text-wrap: pretty; }
    h1, h2, h3, .tool-claim { text-wrap: balance; }

    /* A jump to a section should not tuck its heading under the top of the view. */
    section[id], h2[id] { scroll-margin-top: calc(var(--nav-h) + var(--unit) * 2); }

}
