/* ============================================================================
   PageSpeedAudit design system v2 — "precision instrument"
   Single file, no framework, no JS. Mobile-first. WCAG 2.2 AA contrast.
   Display face: Space Grotesk (variable, self-hosted, 22 KB). Body: system stack.
   ============================================================================ */

/* The @font-face for Space Grotesk deliberately does NOT live here — it is emitted inline in
   App.razor's <head>. A static .css file cannot resolve the build's content fingerprint, so the
   src would have to name the raw /fonts/space-grotesk-var.woff2 path while <link rel=preload>
   (which CAN resolve it, via @Assets) names the hashed one. That mismatch made the browser fetch
   the same 22 KB face twice: once at high priority from the preload, then discarded, then again
   late via CSS discovery — on the raw path's 4-hour revalidating cache instead of the hashed
   path's immutable year. Declaring it in Razor lets preload and @font-face share one
   fingerprint-resolved URL, so they can never drift apart again. */

:root {
    /* ink + surfaces */
    --ink: #10211a;            /* deep pine */
    --ink-soft: #33433a;
    --muted: #4b5b53;          /* 7.03:1 on paper (AAA) */
    --paper: #fbfdfb;
    --card: #ffffff;
    --wash: #eef4f0;
    --wash-deep: #e2ece5;
    --line: #d8e2db;
    --line-soft: #e6eee8;

    /* brand */
    --brand: #0d6b3c;          /* 6.1:1 on paper */
    --brand-strong: #0a4f2d;
    --brand-deep: #07391f;
    --brand-wash: #e3f2e8;

    /* dark sections */
    --dark-bg: #0b1812;
    --dark-bg-2: #102219;
    --dark-line: #24382d;
    --dark-ink: #eef6f0;
    --dark-soft: #b9cfc1;      /* 9.5:1 on dark-bg */
    --signal: #8df0a6;         /* the wow accent — dark surfaces only (11:1 on dark-bg) */

    /* semantic */
    --good: #0e7a3e; --warn: #8a5800; --bad: #b3261e;
    --good-dark: #8df0a6; --warn-dark: #ffd28a; --bad-dark: #ffb1aa;
    --focus: #1456d8;

    /* shape + depth */
    --radius: 14px;
    --radius-sm: 9px;
    --shadow-1: 0 1px 2px rgb(13 33 23 / .05), 0 2px 8px rgb(13 33 23 / .05);
    --shadow-2: 0 2px 4px rgb(13 33 23 / .06), 0 12px 32px -8px rgb(13 33 23 / .14);
    --ring: inset 0 1px 0 rgb(255 255 255 / .14);

    /* type */
    --font-display: "Space Grotesk", system-ui, sans-serif;
    --font-body: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-mono: ui-monospace, "Cascadia Code", SFMono-Regular, Consolas, monospace;
    --text-sm: .875rem;
    --text-base: clamp(1rem, .97rem + .15vw, 1.0625rem);
    --text-lg: clamp(1.125rem, 1.04rem + .4vw, 1.3125rem);
    --text-xl: clamp(1.3125rem, 1.16rem + .75vw, 1.75rem);
    --text-2xl: clamp(1.625rem, 1.32rem + 1.5vw, 2.375rem);
    --text-3xl: clamp(2rem, 1.5rem + 2.4vw, 3.125rem);
    --text-hero: clamp(2.375rem, 1.6rem + 3.9vw, 4.25rem);

    --space-1: .25rem; --space-2: .5rem; --space-3: .75rem; --space-4: 1rem;
    --space-6: 1.5rem; --space-8: 2rem; --space-10: 2.5rem; --space-12: 3rem;
    --space-16: 4rem; --space-20: 5rem;
}

/* ---------------------------------------------------------------- base */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after { animation: none !important; transition: none !important; }
}
body {
    margin: 0;
    font-family: var(--font-body);
    font-size: var(--text-base);
    line-height: 1.65;
    color: var(--ink);
    background: var(--paper);
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    text-rendering: optimizeLegibility;
}
main { flex: 1; }
img, svg, iframe { max-width: 100%; }
img, svg { height: auto; display: block; }

h1, h2, h3, .display {
    font-family: var(--font-display);
    font-weight: 600;
    line-height: 1.08;
    letter-spacing: -.022em;
    margin: 0 0 .5em;
    text-wrap: balance;
    color: var(--ink);
}
h1 { font-size: var(--text-3xl); }
h2 { font-size: var(--text-2xl); }
h3 { font-size: var(--text-lg); letter-spacing: -.01em; }
p { margin: 0 0 1em; }
.lede { font-size: var(--text-lg); color: var(--ink-soft); max-width: 56ch; text-wrap: pretty; }
a { color: var(--brand); text-underline-offset: 3px; text-decoration-thickness: 1px; }
a:hover { color: var(--brand-strong); }
small { font-size: var(--text-sm); }
code { font-family: var(--font-mono); font-size: .88em; background: var(--wash); border: 1px solid var(--line-soft); padding: .1em .4em; border-radius: 6px; overflow-wrap: anywhere; }
/* Inside a <pre> the code element is just the block's text — drop the inline-chip background/border so it
   doesn't paint a light box over a dark code block (that was hiding the API-keys quick-start curl). */
pre code { background: transparent; border: none; padding: 0; border-radius: 0; font-size: inherit; color: inherit; }
.num, .metric { font-family: var(--font-display); font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
:focus-visible { outline: 2px solid #fff; outline-offset: 0; box-shadow: 0 0 0 4px var(--focus); border-radius: 3px; }
/* Blazor's <FocusOnNavigate> moves focus to the page <h1> for screen readers; that programmatic
   focus must not paint a visible ring on a non-interactive heading (it isn't keyboard-tabbable). */
/* FocusOnNavigate parks focus on the page heading after in-app navigation (an a11y feature we keep) —
   but a heading isn't interactive, so it gets no visible ring: clear the box-shadow too, not just the
   outline, or the global focus style's blue glow paints a rectangle around every routed-to h1. */
h1:focus, h1:focus-visible { outline: none; box-shadow: none; }
::selection { background: #cdebd7; }

.container { width: min(100% - 2.5rem, 71rem); margin-inline: auto; }
.container-narrow { width: min(100% - 2.5rem, 46rem); margin-inline: auto; }
.center { text-align: center; }
.center .lede { margin-inline: auto; }

.skip-link {
    position: absolute; left: -200vw; top: 0; z-index: 200;
    background: var(--brand-deep); color: #fff; padding: .75rem 1.25rem;
    border-radius: 0 0 var(--radius-sm) 0; text-decoration: none; font-weight: 600;
}
.skip-link:focus { left: 0; color: #fff; }
/* main is focusable only so the skip link can move focus there; it should not draw a ring when
   it receives that programmatic focus — the ring belongs to whatever the user Tabs to next. */
#main:focus { outline: none; }

/* ---------------------------------------------------------------- header */
/* One source of truth for the sticky header's height. scroll-padding-top reads the same variable,
   so an anchor jump can never land behind the bar again (it did: 6rem was hard-coded for the
   desktop header and fell 119px short of the mobile one). */
:root { --header-h: 72px; }

.site-header {
    position: sticky; top: 0; z-index: 50;
    background: rgb(251 253 251 / .86);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--line-soft);
}
/* The toggle checkbox itself is never seen — only its label, and only on small screens. It has to
   LEAVE the tab order with its label: above the breakpoint .nav-toggle is display:none, so a
   focusable 1px checkbox there is a tab stop with nothing visible to put a ring on (WCAG 2.4.7).
   display:none removes it from the tab order without touching the :checked sibling selectors. */
.nav-toggle-cb { display: none; position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.nav-toggle { display: none; }
/* Two-class selectors on purpose: the phone rule `.nav-toggle { display: inline-flex }` must not
   show the popover button in a browser without popover support. */
.nav-toggle.nav-toggle--pop { display: none; font: inherit; }
.nav-links > .nav-scrim { display: none; }
/* The list carries the popover attribute for the phone menu. On wide screens it is an ordinary row,
   so the browser's popover defaults (fixed, centred, bordered, hidden while closed) are undone. */
.nav-links[popover] {
    position: static; inset: auto; width: auto; height: auto; overflow: visible;
    border: 0; color: inherit; background: transparent;
}
.nav { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-6); padding-block: .8rem; }
.brand {
    font-family: var(--font-display); font-weight: 700; font-size: 1.25rem;
    color: var(--ink); text-decoration: none; letter-spacing: -.03em;
    display: inline-flex; align-items: center; gap: .5rem;
}
.brand:hover { color: var(--brand-strong); }
.brand-mark { width: 1.6rem; height: 1.6rem; flex: none; }
.brand { min-height: 44px; }
.brand .tld { color: var(--brand); }
.nav-links { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-4); list-style: none; margin: 0 0 0 auto; padding: 0; }
.nav-links a:not(.btn) {
    display: inline-flex; align-items: center; padding: .55rem .35rem; min-height: 44px;
    color: var(--ink-soft); text-decoration: none; font-weight: 550; font-size: .96rem;
}
.nav-links a:not(.btn):hover, .nav-links a[aria-current="page"] { color: var(--brand-strong); box-shadow: inset 0 -2px 0 var(--brand); }
.nav-cta { margin-left: var(--space-2); }

/* JS-free nav dropdown (<details>): works on static-SSR pages, keyboard-operable out of the
   box (summary is focusable, Enter/Space toggles). Styled to read like a sibling nav link. */
.nav-drop { position: relative; }
.nav-drop > summary {
    list-style: none; cursor: pointer; display: inline-flex; align-items: center;
    padding: .55rem .35rem; min-height: 44px; color: var(--ink-soft); font-weight: 550; font-size: .96rem;
}
.nav-drop > summary::-webkit-details-marker { display: none; }
.nav-drop > summary::after { content: "▾"; font-size: .7em; margin-left: .35rem; }
.nav-drop > summary:hover, .nav-drop[open] > summary { color: var(--brand-strong); }
.nav-drop > ul {
    position: absolute; right: 0; top: calc(100% + .35rem); z-index: 50;
    min-width: 13.5rem; margin: 0; padding: .4rem; list-style: none;
    background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-2);
}
.nav-links .nav-drop > ul a:not(.btn) { display: block; padding: .5rem .7rem; border-radius: 6px; min-height: 0; white-space: nowrap; }
.nav-links .nav-drop > ul a:not(.btn):hover { background: var(--wash); box-shadow: none; }

/* Below 56rem the six nav items wrapped onto three lines inside the sticky bar: 214.6px, 26.4% of
   a 375x812 viewport, held at every scroll position, with page content ghosting through the
   translucent backdrop. The footer already had three breakpoints; the header had none but @media
   print. A checkbox and its label collapse it with no JavaScript, so it works on the static-SSR
   pages too — and "Get your audit" stays outside the toggle, because it is the page's conversion.
   (<details> is the tidier markup and cannot be used: a closed <details> hides its content in the
   UA shadow DOM, where no media query can bring it back for the desktop layout.) */
@media (max-width: 56rem) {
    :root { --header-h: 64px; }
    /* wrap, not nowrap: the opened list (flex-basis 100%) must drop onto its own row. With nowrap it
       was squeezed into the bar beside Menu and pushed the page 45px wider than a 430px phone. */
    .nav { flex-wrap: wrap; gap: var(--space-2); padding-block: .55rem; }
    .brand { font-size: 1.1rem; }

    .nav-toggle {
        display: inline-flex; align-items: center; gap: .45rem;
        order: 3; margin-left: auto;
        min-height: 44px; padding: .45rem .8rem;
        border: 1.5px solid var(--line); border-radius: 8px; background: var(--card);
        font-weight: 600; font-size: .92rem; color: var(--ink-soft); cursor: pointer;
    }
    .nav-toggle__bars, .nav-toggle__bars::before, .nav-toggle__bars::after {
        display: block; width: 1.05rem; height: 2px; background: currentColor; border-radius: 2px;
    }
    .nav-toggle__bars { position: relative; }
    .nav-toggle__bars::before, .nav-toggle__bars::after { content: ""; position: absolute; left: 0; }
    .nav-toggle__bars::before { top: -6px; }
    .nav-toggle__bars::after { top: 6px; }
    /* Here the label IS on screen, so the checkbox rejoins the tab order (Tab reaches it, Space
       toggles it); the ring is drawn on the label, which is the thing the eye is actually on. */
    .nav-toggle-cb { display: block; }
    .nav-toggle-cb:focus-visible ~ .nav-toggle { outline: 3px solid var(--focus); outline-offset: 1px; }

    /* Closed: the list is out of the flow entirely, so the bar is one row high. */
    .nav-links {
        display: none;
        order: 4; flex-basis: 100%; flex-direction: column; align-items: stretch;
        gap: 0; margin: .5rem 0 .35rem;
        border-top: 1px solid var(--line-soft); padding-top: .35rem;
    }
    .nav-toggle-cb:checked ~ .nav-links { display: flex; }
    .nav-links a:not(.btn) { padding: .7rem .35rem; font-size: 1rem; }

    /* Ordering: brand, CTA, Menu on the bar itself; the list drops beneath it when opened.
       The CTA sits OUTSIDE the collapsible list on purpose — it is the page's conversion and
       must not cost a tap to find. */
    .brand { order: 1; }
    .nav-cta { order: 2; margin-left: auto; }
    .nav-toggle { order: 3; margin-left: .5rem; }
    .nav-links { order: 4; }
    .nav-cta .btn { padding-inline: .75rem; font-size: .88rem; white-space: nowrap; }
}

/* Popover menu, where the browser supports it: Escape and a tap outside close it, focus returns to
   Menu, and no script is involved. The checkbox and its label stand down; older browsers keep them.
   The open list sits in the top layer, so it is placed just under the sticky header (--header-h). */
@supports selector(:popover-open) {
    @media (max-width: 56rem) {
        .nav-toggle-cb, .nav-toggle.nav-toggle--cb { display: none; }
        .nav-toggle.nav-toggle--pop { display: inline-flex; font-weight: 600; font-size: .92rem; }
        .nav-toggle--pop:focus-visible { outline: 3px solid var(--focus); outline-offset: 1px; }
        /* The popover fills everything under the header: the links on a card, then the dimmed scrim,
           which is part of the popover so a tap there closes the menu and reaches nothing else. */
        .nav-links:popover-open {
            display: flex; position: fixed; inset: var(--header-h) 0 0 0;
            width: auto; height: auto; overflow-y: auto; overscroll-behavior: contain;
            margin: 0; padding: 0; border: 0; background: rgb(13 33 23 / .18);
        }
        .nav-links:popover-open > li:not(.nav-scrim) { background: var(--card); padding-inline: max(1.25rem, (100% - 71rem) / 2); }
        .nav-links:popover-open > li:first-child { padding-top: .35rem; }
        .nav-links:popover-open > li:nth-last-child(2) { padding-bottom: .6rem; border-bottom: 1px solid var(--line); box-shadow: 0 12px 24px rgb(13 33 23 / .12); }
        .nav-links:popover-open > .nav-scrim { display: block; flex: 1 0 3rem; }
        .nav-scrim > button { display: block; width: 100%; height: 100%; padding: 0; border: 0; background: transparent; cursor: pointer; }
    }
}

/* ≤ 480px the single bar cannot hold brand + CTA + Menu: their minimum widths sum past
   the viewport and the nowrap flex row clips the CTA — the one element that must never
   be clipped. Two deliberate rows instead: brand + Menu on top, the CTA full-width
   beneath, the collapsible list still dropping under both. --header-h follows so
   anchor targets keep clearing the taller sticky bar. */
@media (max-width: 30rem) {
    /* Two 44px rows + the gap and padding: 112px. The popover menu opens exactly here. */
    :root { --header-h: 112px; }
    .nav {
        display: grid; grid-template-columns: minmax(0, 1fr) auto;
        align-items: center; gap: .4rem .6rem; padding-block: .5rem .55rem;
    }
    .brand { grid-column: 1; grid-row: 1; min-width: 0; }
    .nav-toggle { grid-column: 2; grid-row: 1; margin-left: 0; }
    .nav-cta { grid-column: 1 / -1; grid-row: 2; margin-left: 0; }
    .nav-cta .btn { width: 100%; }
    .nav-links { grid-column: 1 / -1; grid-row: 3; margin: .1rem 0 .3rem; }
}

/* ---------------------------------------------------------------- buttons */
.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: .55rem;
    min-height: 46px; padding: .65rem 1.45rem;
    font-family: var(--font-display); font-size: 1.02rem; font-weight: 600; letter-spacing: .002em;
    text-decoration: none; text-align: center; white-space: nowrap;
    border-radius: 999px; border: 1.5px solid transparent; cursor: pointer;
    transition: transform .15s ease, box-shadow .15s ease, background-color .15s ease;
}
/* nowrap keeps button labels on one line — but a label longer than the phone ("Secondary
   evidence — Heričko 2021 Table 2 (CSV)") then pushes the whole PAGE into sideways scroll,
   measured at up to 171px past a 360px viewport. On phones the label wraps instead; max-width
   stops a flex/grid parent from letting the pill itself poke out of the column. */
@media (max-width: 40rem) {
    .btn { white-space: normal; max-width: 100%; }
}
.btn-primary {
    background: linear-gradient(180deg, var(--brand), var(--brand-strong));
    color: #fff; box-shadow: var(--shadow-1), var(--ring);
}
.btn-primary:hover { color: #fff; transform: translateY(-1px); box-shadow: var(--shadow-2), var(--ring); }
.btn-primary:active { transform: translateY(0); }
.btn-secondary { background: var(--card); color: var(--brand-strong); border-color: var(--brand); }
.btn-secondary:hover { background: var(--brand-wash); }
.btn-ghost { background: transparent; color: var(--ink-soft); border-color: var(--line); }
.btn-ghost:hover { border-color: var(--muted); color: var(--ink); }
.btn-danger { background: var(--card); color: var(--bad); border-color: var(--bad); }
.btn-danger:hover { background: #fbeae9; }
.btn-lg { min-height: 56px; padding: .9rem 2.1rem; font-size: 1.13rem; }
.btn-sm { min-height: 36px; padding: .25rem 1rem; font-size: .92rem; }
.btn[disabled] { opacity: .55; cursor: not-allowed; transform: none; }
.btn .arrow { transition: transform .15s ease; }
.btn:hover .arrow { transform: translateX(3px); }

/* on-dark button variants */
.section-dark .btn-primary { background: var(--signal); color: var(--brand-deep); box-shadow: 0 8px 28px -8px rgb(141 240 166 / .45); }
.section-dark .btn-primary:hover { color: var(--brand-deep); background: #a4f5b8; }
.section-dark .btn-secondary { background: transparent; color: var(--dark-ink); border-color: var(--dark-line); }
.section-dark .btn-secondary:hover { background: var(--dark-bg-2); border-color: var(--dark-soft); }

/* ---------------------------------------------------------------- forms */
.field { margin-bottom: var(--space-4); }
.field label { display: block; font-weight: 600; margin-bottom: .35rem; font-size: .97rem; }
.hint { color: var(--muted); font-size: var(--text-sm); margin-top: .35rem; }
/* Exclusion-based, NOT a list of types. The old rule enumerated text/email/password/url and so
   silently excluded number and file — which is why the sample-size calculator's δ, α, power, n and
   both JSON uploads rendered as 21px browser defaults in 13px Arial: below the WCAG 2.2 SC 2.5.8
   24px target minimum, and a different design system from the field directly above them. Written
   this way, any type added later inherits the design system instead of falling out of it. */
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]):not([type="hidden"]),
textarea, select {
    width: 100%; min-height: 48px; padding: .6rem .9rem;
    font: inherit; color: var(--ink); background: var(--card);
    border: 1.5px solid #b9c6be; border-radius: var(--radius-sm);
    transition: border-color .12s ease;
}
/* Fields inherit their font, so one inside a small label came out at 13.4px — and iOS zooms the whole
   page when a field under 16px is tapped. On phone layouts and touch screens a field is never smaller
   than 16px (1em here is the size it inherits, so larger fields keep theirs). */
@media (max-width: 56rem), (pointer: coarse) {
    input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]):not([type="hidden"]),
    textarea, select { font-size: max(16px, 1em); }
}
/* A number field is semantically short — that is a WIDTH goal, never a height one. */
input[type="number"] { max-width: 9rem; }
.field-wide input[type="number"] { max-width: 14rem; }
/* A file input's box cannot be sized like a text field, so the button part carries the presence. */
input[type="file"] { padding: .45rem .5rem; cursor: pointer; color: var(--ink-soft); }
input[type="file"]::file-selector-button {
    min-height: 40px; margin: 0 .8rem 0 0; padding: .45rem 1rem;
    font: inherit; font-weight: 600; color: var(--brand-strong);
    background: var(--brand-wash); border: 1.5px solid var(--brand);
    border-radius: var(--radius-sm); cursor: pointer;
    transition: border-color .12s ease, background-color .12s ease;
}
input[type="file"]::file-selector-button:hover { border-color: var(--brand-strong); background: var(--card); }
input:hover, textarea:hover { border-color: var(--muted); }
textarea { min-height: 8.5rem; resize: vertical; }
input:focus-visible, textarea:focus-visible, select:focus-visible { outline: 3px solid var(--focus); outline-offset: 1px; border-color: var(--focus); }
input[disabled] { background: var(--wash); color: var(--muted); }
.error-text { color: var(--bad); font-weight: 600; margin: var(--space-2) 0; }
.success-text { color: var(--good); font-weight: 600; margin: var(--space-2) 0; }
.form-row { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.form-row input { flex: 1 1 14rem; }

/* ---------------------------------------------------------------- cards */
.card {
    background: var(--card); border: 1px solid var(--line);
    border-radius: var(--radius); padding: clamp(1.25rem, 3vw, 2rem);
    box-shadow: var(--shadow-1);
}
.card.lift { transition: transform .18s ease, box-shadow .18s ease; }
.card.lift:hover { transform: translateY(-3px); box-shadow: var(--shadow-2); }
.card + .card { margin-top: var(--space-4); }
/* …but inside a grid the `gap` already spaces cards. Without this reset the
   stacking margin above pushes every card after the first down within its
   cell, so a row's card tops (and their bottom-aligned buttons) don't line up. */
.grid-2 > .card + .card, .grid-3 > .card + .card { margin-top: 0; }
.card h2:first-child, .card h3:first-child { margin-top: 0; }
.panel { background: var(--wash); border: 1px solid var(--line-soft); border-radius: var(--radius); padding: clamp(1.25rem, 3vw, 1.75rem); }

.icon-tile {
    width: 2.75rem; height: 2.75rem; border-radius: 12px; margin-bottom: var(--space-3);
    display: grid; place-items: center;
    background: var(--brand-wash); color: var(--brand-strong);
}
.icon-tile svg { width: 1.4rem; height: 1.4rem; }

/* ---------------------------------------------------------------- badges & chips */
.badge {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .14rem .7rem; border-radius: 999px;
    font-family: var(--font-display); font-size: .82rem; font-weight: 600;
    border: 1.5px solid currentColor; white-space: nowrap;
}
.badge::before { content: ""; width: .45rem; height: .45rem; border-radius: 50%; background: currentColor; }
.badge-queued, .badge-starting { color: var(--warn); }
.badge-running { color: #155cb0; }
.badge-running::before { animation: pulse 1.2s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: .25; } }
.badge-succeeded, .badge-active { color: var(--good); }
.badge-failed, .badge-timedout, .badge-revoked { color: var(--bad); }
.badge-expired, .badge-canceled { color: var(--muted); }
/* Ticket taxonomy: status stays green/gray, the LLM CATEGORY is blue, and the arrival
   CHANNEL (opened by email instead of the form) is violet — three facts, three colors. */
.badge-category { color: #155cb0; }
.badge-channel { color: #8b5cf6; }
/* A partner asset written TO the partner (tracking explainer, economics sheet), never for
   their audience. Muted on purpose: a fact about the block, not a warning. */
.badge-private { color: var(--muted); }
.eyebrow {
    display: inline-flex; align-items: center; gap: .5rem;
    font-family: var(--font-display); color: var(--brand-strong); font-weight: 600;
    letter-spacing: .09em; text-transform: uppercase; font-size: .78rem;
    margin-bottom: var(--space-3);
}
.eyebrow::before { content: ""; width: 1.5rem; height: 2px; background: var(--brand); border-radius: 2px; }
.section-dark .eyebrow { color: var(--signal); }
.section-dark .eyebrow::before { background: var(--signal); }

/* ---------------------------------------------------------------- tables */
/* A table wider than the phone scrolls inside its own box rather than widening the page. WCAG 2.2
   SC 1.4.10 (Reflow) exempts data tables from the no-two-dimensional-scrolling rule, so this IS the
   conformant answer — but a scroll container then has to be reachable by keyboard (SC 2.1.1), which
   is why every .table-wrap is marked up as a focusable, named region. */
.table-wrap {
    overflow-x: auto;
    /* A grid/flex child defaults to min-width:auto and refuses to shrink below its own content, so
       the PAGE scrolls sideways instead of the table. Same trap already fixed for .hero-grid. */
    min-width: 0;
    /* A horizontal swipe across the table must not trigger the browser's back gesture. */
    overscroll-behavior-x: contain;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--card);
    /* Overlay scrollbars (macOS, and Windows since 11) are invisible at rest, so a table that
       scrolls looks identical to one that doesn't. Styling the scrollbar at all opts the container
       out of overlay behaviour, giving a permanent cue — and it costs no height when the table
       fits, because overflow-x:auto only creates the bar when there IS overflow.

       The usual alternative, background-gradient scroll shadows, is wrong for THIS table: the
       shadow layers paint behind the cells, and `table.data thead th` is opaque (--wash), so the
       shadow would be interrupted across the header row and read as a rendering fault. */
    scrollbar-width: thin;
    scrollbar-color: var(--line) transparent;
}
.table-wrap::-webkit-scrollbar { height: .5rem; }
.table-wrap::-webkit-scrollbar-track { background: transparent; }
.table-wrap::-webkit-scrollbar-thumb { background: var(--line); border-radius: 999px; }
.table-wrap:hover::-webkit-scrollbar-thumb { background: var(--ink-soft); }
/* The global focus ring squares off the corners; keep the wrapper's own radius. */
.table-wrap:focus-visible { border-radius: var(--radius); }

table.data { width: 100%; border-collapse: collapse; font-size: .94rem; }
table.data th, table.data td { text-align: left; padding: .7rem .9rem; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
/* Figures and timestamps break into nonsense when wrapped mid-value ("2026-07-25" over three
   lines). Scoped to tbody on purpose: .num is also used on header cells whose text is a prose
   label ("Desktop break-even (r ≈ 0.986)"), and pinning those would widen the table into a scroll
   it does not currently need. Values never wrap; the words naming them still do. */
table.data tbody .num { white-space: nowrap; }
table.data thead th { background: var(--wash); font-family: var(--font-display); font-weight: 600; font-size: .85rem; letter-spacing: .03em; text-transform: uppercase; color: var(--ink-soft); }
table.data tbody tr:last-child td { border-bottom: none; }
table.data tbody tr:hover { background: #f6faf7; }
/* That wash is a light-mode value. Inside a dark section it paints a near-white row under the
   section's light text — 1.05:1 for the emphasis column, unreadable. Lift the row within the
   dark palette instead: the hover affordance survives and the text stays at 9.8:1 / 14.7:1. */
.section-dark table.data tbody tr:hover { background: rgb(255 255 255 / .06); }

/* A row that IS the problem — an overdue worker, a dependency that has stopped answering. Scanning
   a status table is a search for the bad rows, and a badge in one column makes that search a
   column-by-column read; tinting the whole line makes it a glance.

   Three cues, not one: tint, a heavy left rule and bolder ink. Colour alone fails anyone with a
   red-green deficiency, and on the printed page an operator forwards to someone — and the State
   cell still says "Stale" in words for whoever the colour never reaches at all.

   The left rule is an inset shadow rather than a border because `border-collapse: collapse` would
   reflow the cell by its width; a shadow paints inside the box the cell already occupies.

   The tint is deliberately barely-there: --ink measures 15.3:1 on it and 14.3:1 on the hover
   shade, against 16.8:1 on white. The row is marked without becoming harder to read, which is the
   usual failure of helpful status colouring. */
table.data tbody tr.row-bad { background: #fdf2f1; }
table.data tbody tr.row-bad td:first-child { box-shadow: inset 3px 0 0 var(--bad); }
table.data tbody tr.row-bad td { font-weight: 500; }
table.data tbody tr.row-bad:hover { background: #fbe9e7; }

/* ---------------------------------------------------------------- stacked tables (phones) */
/* Opt-in card layout for tables that were MEASURED to overflow a 360px viewport — a fitting
   table keeps its grid (comparison down a column beats cards). Contract for .table-stack:
   every <td> carries data-label (the column header it stacks under), and the table carries
   explicit ARIA roles (table/rowgroup/row/columnheader/cell) because switching display strips
   the implicit ones in Chrome and Safari. The thead is display:none on phones; the per-cell
   ::before labels — announced by modern screen readers — take over its job.
   This block must stay AFTER the table.data base rules: the overrides (td padding/border,
   .num wrapping, row hover) tie them on specificity, so source order is what decides. */
@media (max-width: 40rem) {
    table.table-stack, table.table-stack tbody, table.table-stack tr, table.table-stack td { display: block; width: 100%; }
    table.table-stack thead { display: none; }
    table.table-stack tr {
        border: 1px solid var(--line-soft);
        border-radius: 8px;
        background: var(--card);
        margin: .6rem;
        padding: .35rem .85rem;
        width: auto;
    }
    table.table-stack tbody tr:hover { background: var(--card); } /* row hover is a grid affordance */
    /* Cards paint their own background and border on the <tr>, so the grid's row marking would
       vanish here. Same three cues, restated in the card's own shape. */
    table.table-stack tbody tr.row-bad,
    table.table-stack tbody tr.row-bad:hover { background: #fdf2f1; border-color: var(--bad); border-left-width: 3px; }
    table.table-stack tbody tr.row-bad td:first-child { box-shadow: none; }
    table.table-stack td { border-bottom: 0; padding: .35rem 0; }
    table.table-stack td::before {
        content: attr(data-label);
        display: block;
        font-family: var(--font-display);
        font-size: .75rem;
        font-weight: 600;
        letter-spacing: .04em;
        text-transform: uppercase;
        color: var(--ink-soft);
    }
    /* Values may wrap freely in cards — the no-wrap rule exists for grid columns. */
    table.table-stack tbody .num { white-space: normal; }
    table.table-stack caption { display: block; }
    /* Once the table stacks, nothing overflows and the scroll wrapper is just a frame around
       cards — drop it so they read as cards, not as cards boxed inside a card. Browsers without
       :has() keep the frame; the layout still works, it is merely double-framed. */
    .table-wrap:has(> table.table-stack) { border: 0; background: transparent; }
    .table-wrap:has(> table.table-stack) table.table-stack tr { margin: .6rem 0; }

    /* The card treatment above paints var(--card) — light — on the <tr>. Inside a dark section
       that puts the section's light text on a light card at 1.1:1: the whole table disappears on
       phones, in the RESTING state. Restate the card in the dark palette so the stacked view keeps
       the same contrast the grid view has. */
    .section-dark table.table-stack tr,
    .section-dark table.table-stack tbody tr:hover { background: var(--dark-bg-2); border-color: var(--dark-line); }
    .section-dark table.table-stack td { color: var(--dark-ink); }
    .section-dark table.table-stack td::before { color: var(--dark-soft); }
}

/* ---------------------------------------------------------------- research figures */
figure.chart { margin: clamp(1.5rem, 4vw, 2.25rem) 0; padding: clamp(.9rem, 2.5vw, 1.4rem); background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-1); }
figure.chart img { width: 100%; height: auto; display: block; }
figure.chart figcaption { margin-top: .8rem; font-size: .9rem; color: var(--muted); border-top: 1px solid var(--line-soft); padding-top: .7rem; }
/* A video's transcript, collapsed under its player (research articles). */
.transcript { margin-top: 1rem; padding: .8rem 1.1rem; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); }
.transcript > summary { cursor: pointer; font-weight: 600; color: var(--brand-strong); min-height: 44px; display: flex; align-items: center; }
.transcript > div { margin-top: .5rem; }
.transcript p { font-size: .95rem; }
.transcript-time { color: var(--muted); font-size: .85rem; font-variant-numeric: tabular-nums; margin-right: .35rem; }
/* Format cards: an article's other formats (video, PDF). Outlined and quiet, so they read as
   alternatives and never compete with the primary CTA; the whole card is the link (44px+ target). */
.format-cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .75rem; margin-top: var(--space-4); }
.format-cards-single { grid-template-columns: minmax(0, 32rem); margin-top: .6rem; }
.format-card { display: flex; align-items: center; gap: .9rem; min-height: 44px; padding: .55rem .9rem .55rem .55rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); color: var(--ink); text-decoration: none; transition: border-color .15s, box-shadow .15s; }
.format-card:hover { border-color: var(--brand); box-shadow: var(--shadow-1); }
.format-card:hover .format-card-title { text-decoration: underline; text-underline-offset: .18em; }
.format-card:focus-visible { border-radius: var(--radius); }
.format-card-media { position: relative; flex: none; display: block; height: 72px; border-radius: 6px; overflow: hidden; border: 1px solid var(--line-soft); background: var(--wash); }
.format-card-media img { display: block; height: 72px; width: auto; }
.format-card-video::after { content: ""; position: absolute; inset: 0; margin: auto; width: 30px; height: 30px; border-radius: 50%; background: rgb(7 57 31 / .88) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M6 4.2v7.6L12 8z" fill="%23fff"/></svg>') center/16px no-repeat; }
.format-card-doc::after { content: "PDF"; position: absolute; right: 3px; bottom: 3px; padding: 0 .3rem; border-radius: 3px; background: var(--brand-deep); color: #fff; font-size: .62rem; font-weight: 700; letter-spacing: .04em; line-height: 1.5; }
.format-card-text { display: flex; flex-direction: column; gap: .15rem; min-width: 0; }
.format-card-title { font-weight: 600; color: var(--brand-deep); line-height: 1.3; }
.format-card-meta { font-size: var(--text-sm); color: var(--muted); line-height: 1.35; }
.take-it { margin-top: 2rem; }
.take-it-label { margin: 0; font-size: .8rem; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
@media (max-width: 600px) {
    .format-cards, .format-cards-single { grid-template-columns: minmax(0, 1fr); }
}
.article-meta { display: flex; flex-wrap: wrap; gap: .4rem 1.4rem; color: var(--muted); font-size: var(--text-sm); margin-top: var(--space-4); }
.article-meta strong { color: var(--ink-soft); font-weight: 600; }

/* ---------------------------------------------------------------- cloaking-test verdict */
.verdict { border: 1px solid var(--line); border-left-width: 5px; border-radius: var(--radius); padding: clamp(1rem,3vw,1.4rem) clamp(1.1rem,3vw,1.6rem); background: var(--card); box-shadow: var(--shadow-1); }
.verdict-band { margin: 0 0 .3rem; font-family: var(--font-display); font-weight: 650; font-size: .8rem; letter-spacing: .06em; text-transform: uppercase; }
.verdict-headline { margin: 0; font-size: var(--text-lg); color: var(--ink); font-weight: 600; }
.verdict-url { margin: .6rem 0 0; color: var(--muted); font-size: var(--text-sm); overflow-wrap: anywhere; }
.verdict-good { border-left-color: var(--good); } .verdict-good .verdict-band { color: var(--good); }
.verdict-warn { border-left-color: var(--warn); } .verdict-warn .verdict-band { color: var(--warn); }
.verdict-bad { border-left-color: var(--bad); } .verdict-bad .verdict-band { color: var(--bad); }
.verdict-neutral { border-left-color: var(--muted); } .verdict-neutral .verdict-band { color: var(--muted); }
.finding-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .6rem; }
.finding { border: 1px solid var(--line-soft); border-left: 3px solid var(--line); border-radius: var(--radius); padding: .7rem .9rem; background: var(--card); }
.finding strong { display: block; color: var(--ink); }
.finding span { color: var(--muted); font-size: .94rem; }
.finding-good { border-left-color: var(--good); } .finding-bad { border-left-color: var(--bad); }
.finding-warn { border-left-color: var(--warn); } .finding-info { border-left-color: var(--brand); }

/* ---------------------------------------------------------------- sections */
section.block { padding-block: clamp(3rem, 7vw, 5.5rem); }
section.block.alt { background: var(--wash); border-block: 1px solid var(--line-soft); }
.section-dark {
    background:
        radial-gradient(60rem 30rem at 85% -10%, rgb(141 240 166 / .07), transparent 60%),
        linear-gradient(180deg, var(--dark-bg), var(--dark-bg-2));
    color: var(--dark-soft);
}
.section-dark h1, .section-dark h2, .section-dark h3, .section-dark .display { color: var(--dark-ink); }
.section-dark .lede { color: var(--dark-soft); }
.section-dark a:not(.btn) { color: var(--signal); }
/* min(…, 100%) caps each track's minimum at the container: a bare 19rem minimum forces
   a 304px column on 320px phones and the whole section scrolls sideways by the excess. */
.grid-3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(15.5rem, 100%), 1fr)); gap: var(--space-4); }
.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr)); gap: clamp(1.5rem, 4vw, 3rem); align-items: start; }
.section-head { max-width: 44rem; margin-bottom: clamp(1.5rem, 4vw, 2.5rem); }
.section-num { font-family: var(--font-display); color: var(--muted); font-weight: 500; font-size: .9rem; letter-spacing: .12em; }

/* ---------------------------------------------------------------- hero */
.hero {
    position: relative;
    padding-block: clamp(3.5rem, 8vw, 6.5rem) clamp(2.5rem, 6vw, 4.5rem);
    background:
        radial-gradient(50rem 26rem at 12% -20%, rgb(13 107 60 / .08), transparent 60%),
        linear-gradient(180deg, #f3f8f4, var(--paper) 70%);
    border-bottom: 1px solid var(--line-soft);
    overflow: clip;
}
.hero::before {
    content: ""; position: absolute; inset: 0; pointer-events: none;
    background:
        linear-gradient(rgb(13 107 60 / .045) 1px, transparent 1px),
        linear-gradient(90deg, rgb(13 107 60 / .045) 1px, transparent 1px);
    background-size: 2.5rem 2.5rem;
    mask-image: radial-gradient(46rem 30rem at 75% 0%, #000 30%, transparent 75%);
    -webkit-mask-image: radial-gradient(46rem 30rem at 75% 0%, #000 30%, transparent 75%);
}
.hero > .container { position: relative; }
.hero-grid { display: grid; grid-template-columns: 1.15fr .85fr; gap: clamp(2rem, 5vw, 4rem); align-items: center; }
@media (max-width: 56rem) { .hero-grid { grid-template-columns: 1fr; } }
.hero-grid > * { min-width: 0; } /* grid children may never force the column past the viewport */
.hero h1 { font-size: var(--text-hero); font-weight: 650; max-width: 16ch; }
.hero h1 em { font-style: normal; color: var(--brand); position: relative; white-space: nowrap; }
.hero h1 em::after {
    content: ""; position: absolute; left: 0; right: 0; bottom: .04em; height: .14em;
    background: linear-gradient(90deg, var(--signal), #bff5cd); z-index: -1; border-radius: 3px;
}
.hero-cta { display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: center; margin-top: var(--space-8); }
.hero-note { color: var(--muted); font-size: var(--text-sm); margin-top: var(--space-4); }
.hero-note strong { color: var(--ink-soft); }

/* score dials (pure CSS conic gauges) */
.dials { display: flex; gap: clamp(1rem, 3vw, 2rem); align-items: center; justify-content: center; }
.dial { text-align: center; }
.dial-ring {
    --val: 58; --col: var(--bad);
    width: clamp(7rem, 16vw, 10.5rem); aspect-ratio: 1; border-radius: 50%;
    background:
        radial-gradient(closest-side, var(--card) 79%, transparent 80% 100%),
        conic-gradient(var(--col) calc(var(--val) * 1%), var(--wash-deep) 0);
    display: grid; place-items: center;
    box-shadow: var(--shadow-2);
    border: 1px solid var(--line-soft);
}
.dial-ring b { font-family: var(--font-display); font-size: clamp(1.9rem, 4.5vw, 2.9rem); font-weight: 650; color: var(--col); letter-spacing: -.03em; }
.dial-label { display: block; margin-top: .6rem; font-family: var(--font-display); font-weight: 600; font-size: .9rem; color: var(--ink-soft); }
.dial-sub { display: block; color: var(--muted); font-size: .8rem; }
.dial.good .dial-ring { --col: var(--good); }
.dial.bad .dial-ring { --col: var(--bad); }
.dial.warn .dial-ring { --col: var(--warn); }
.dial-sep { font-family: var(--font-display); font-size: 1.6rem; color: var(--muted); }
.dial-sm .dial-ring { width: 5.2rem; box-shadow: none; }
.dial-sm .dial-ring b { font-size: 1.5rem; }

/* waterfall decorative motif */
.waterfall { display: grid; gap: .45rem; margin-block: var(--space-4); }
.wf-row { display: grid; grid-template-columns: 7.5rem 1fr; align-items: center; gap: .8rem; font-size: .8rem; color: var(--muted); font-family: var(--font-mono); }
.wf-bar { height: .85rem; border-radius: 4px; background: linear-gradient(90deg, var(--brand), #1d9c5d); opacity: .9; }
.wf-bar.slow { background: linear-gradient(90deg, #c4471f, #e07a52); }

/* big stat callouts */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: var(--space-4); }
.stat { padding: var(--space-4) var(--space-6); border-left: 3px solid var(--brand); background: var(--card); border-radius: 0 var(--radius) var(--radius) 0; box-shadow: var(--shadow-1); }
.stat b { display: block; font-family: var(--font-display); font-size: var(--text-2xl); font-weight: 650; letter-spacing: -.03em; color: var(--brand-strong); }
.stat span { color: var(--muted); font-size: .92rem; }
.stat.metric-good { border-left-color: var(--good); }
.stat.metric-good b { color: var(--good); }
.stat.metric-warn { border-left-color: var(--warn); }
.stat.metric-warn b { color: var(--warn); }
.stat.metric-bad { border-left-color: var(--bad); }
.stat.metric-bad b { color: var(--bad); }
.section-dark .stat { background: var(--dark-bg-2); border-left-color: var(--signal); box-shadow: none; }
.section-dark .stat b { color: var(--signal); }
.section-dark .stat span { color: var(--dark-soft); }

/* checklist + steps */
.check-list { list-style: none; padding: 0; margin: 0 0 var(--space-4); }
.check-list li { padding-left: 2rem; position: relative; margin-bottom: .7rem; }
.check-list li::before {
    content: ""; position: absolute; left: 0; top: .22em;
    width: 1.25rem; height: 1.25rem; border-radius: 50%;
    background: var(--brand-wash) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M3.5 8.5 6.5 11.5 12.5 4.5" fill="none" stroke="%230a4f2d" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>') center/0.85rem no-repeat;
}
.section-dark .check-list li::before { background-color: rgb(141 240 166 / .15); }
.steps { counter-reset: step; list-style: none; padding: 0; margin: 0; display: grid; gap: var(--space-6); }
.steps li { counter-increment: step; padding-left: 3.6rem; position: relative; }
.steps li::before {
    content: counter(step, decimal-leading-zero); position: absolute; left: 0; top: -.15rem;
    font-family: var(--font-display); font-weight: 650; font-size: 1.05rem;
    width: 2.6rem; height: 2.6rem; display: grid; place-items: center;
    background: var(--brand-deep); color: var(--signal); border-radius: 12px;
}
.steps h3 { margin-bottom: .25rem; }
.steps p:last-child { margin-bottom: 0; }

/* price card */
.price-card { border: 2px solid var(--brand); position: relative; overflow: clip; }
.price-card::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 6px; background: linear-gradient(90deg, var(--brand), var(--signal)); }
.price { font-family: var(--font-display); font-size: var(--text-hero); font-weight: 650; letter-spacing: -.03em; line-height: 1; margin: .4rem 0 .2rem; }
.price small { font-family: var(--font-body); font-size: 1rem; font-weight: 500; color: var(--muted); letter-spacing: 0; }
.guarantee { display: flex; gap: .65rem; align-items: flex-start; background: var(--brand-wash); border-radius: var(--radius-sm); padding: .8rem 1rem; font-size: .94rem; }
.guarantee svg { flex: none; width: 1.3rem; height: 1.3rem; color: var(--brand-strong); margin-top: .1rem; }

/* faq */
.faq details { border-bottom: 1px solid var(--line); }
.faq summary {
    font-family: var(--font-display); font-weight: 600; font-size: 1.06rem;
    cursor: pointer; padding: 1.05rem 2.2rem 1.05rem .25rem; min-height: 24px;
    list-style: none; position: relative;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
    content: "+"; position: absolute; right: .35rem; top: 50%; translate: 0 -50%;
    font-size: 1.5rem; font-weight: 400; color: var(--brand); transition: rotate .15s ease;
}
.faq details[open] summary::after { rotate: 45deg; }
.faq summary:hover { color: var(--brand-strong); }
.faq details > p { padding: 0 .25rem 1.1rem; margin: 0; color: var(--ink-soft); max-width: 60ch; }

blockquote.quote { margin: 0; padding: var(--space-6); border-radius: var(--radius); background: var(--dark-bg); color: var(--dark-soft); position: relative; }
blockquote.quote p { position: relative; font-size: var(--text-lg); line-height: 1.55; margin: 0; color: var(--dark-ink); }
blockquote.quote footer { color: var(--dark-soft); font-size: var(--text-sm); margin-top: var(--space-4); }
/* Symmetric inline quotation marks: the opening mark sits on the text line (no orphaned
   oversized glyph above the paragraph), styled identically to the closing one. */
blockquote.quote p:first-of-type::before { content: "\201C"; font-family: var(--font-display); font-size: 1.6em; line-height: 0; vertical-align: -.35em; color: var(--signal); opacity: .85; margin-right: .12em; }
blockquote.quote p:last-of-type::after { content: "\201D"; font-family: var(--font-display); font-size: 1.6em; line-height: 0; vertical-align: -.35em; color: var(--signal); opacity: .85; margin-left: .12em; }

/* browser mockup frame */
.browser {
    border: 1px solid var(--line); border-radius: var(--radius); overflow: clip;
    background: var(--card); box-shadow: var(--shadow-2);
}
.browser-bar { display: flex; align-items: center; gap: .5rem; padding: .6rem .9rem; background: var(--wash); border-bottom: 1px solid var(--line-soft); }
.browser-dots { display: flex; gap: .35rem; }
.browser-dots i { width: .65rem; height: .65rem; border-radius: 50%; background: var(--wash-deep); border: 1px solid var(--line); }
.browser-url { flex: 1; font-family: var(--font-mono); font-size: .78rem; color: var(--muted); background: var(--card); border: 1px solid var(--line-soft); border-radius: 6px; padding: .25rem .7rem; text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.browser iframe { display: block; width: 100%; height: min(72dvh, 56rem); border: 0; background: #fff; }

/* CTA band */
.cta-band { text-align: center; }
.cta-band h2 { font-size: var(--text-3xl); max-width: 24ch; margin-inline: auto; }
.cta-band .hero-cta { justify-content: center; }

/* ---------------------------------------------------------------- app/dashboard */
.page-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); justify-content: space-between; padding-block: var(--space-8) var(--space-4); }
.page-head h1 { margin: 0; font-size: var(--text-2xl); }
.kv { display: grid; grid-template-columns: max-content 1fr; gap: .3rem var(--space-6); margin: 0; }
.kv dt { color: var(--muted); font-size: .93rem; }
.kv dd { margin: 0; overflow-wrap: anywhere; font-weight: 550; }

/* run progress stepper */
.stepper { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; margin-block: var(--space-3); }
.step-node { display: inline-flex; align-items: center; gap: .45rem; font-family: var(--font-display); font-size: .88rem; font-weight: 600; color: var(--muted); }
.step-node::before { content: ""; width: .8rem; height: .8rem; border-radius: 50%; border: 2px solid var(--line); background: var(--card); }
.step-node.done { color: var(--good); }
.step-node.done::before { background: var(--good); border-color: var(--good); }
.step-node.now { color: #155cb0; }
.step-node.now::before { background: #155cb0; border-color: #155cb0; animation: pulse 1.2s ease-in-out infinite; }
.step-link { width: 1.4rem; height: 2px; background: var(--line); border-radius: 2px; }

/* runs-remaining pips */
.pips { display: inline-flex; gap: .3rem; vertical-align: -2px; margin-left: .4rem; }
.pips i { width: .8rem; height: .8rem; border-radius: 3px; background: var(--brand); }
.pips i.used { background: var(--wash-deep); border: 1px solid var(--line); }

.report-frame { width: 100%; height: min(75dvh, 60rem); border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow-1); }
.actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-block: var(--space-4); }
.key-reveal { background: var(--brand-wash); border: 1.5px dashed var(--brand); border-radius: var(--radius); padding: var(--space-4) var(--space-6); overflow-wrap: anywhere; }
.key-reveal code { background: transparent; border: none; font-size: 1rem; padding: 0; }

.empty-state { text-align: center; padding: clamp(2.5rem, 7vw, 4.5rem) var(--space-6); }
.empty-state svg { width: 4rem; height: 4rem; margin: 0 auto var(--space-4); color: var(--brand); opacity: .8; }

/* alerts */
.alert { display: flex; gap: .7rem; border-radius: var(--radius); padding: var(--space-4) var(--space-6); margin-block: var(--space-4); border: 1.5px solid; align-items: flex-start; }
.alert::before { content: ""; flex: none; width: 1.2rem; height: 1.2rem; border-radius: 50%; margin-top: .15rem; background: currentColor; mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" fill="none" stroke="black" stroke-width="1.6"/><path d="M8 4.6v4.4" stroke="black" stroke-width="1.8" stroke-linecap="round"/><circle cx="8" cy="11.5" r="1" fill="black"/></svg>') center/contain no-repeat;
    -webkit-mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" fill="none" stroke="black" stroke-width="1.6"/><path d="M8 4.6v4.4" stroke="black" stroke-width="1.8" stroke-linecap="round"/><circle cx="8" cy="11.5" r="1" fill="black"/></svg>') center/contain no-repeat; }
.alert > :last-child { margin-bottom: 0; }
.alert-info { background: #edf4fd; border-color: #a6c4ef; color: #143a75; }
.alert-success { background: #e9f6ee; border-color: #93d2ab; color: #0c5a2e; }
.alert-error { background: #fbeae9; border-color: #e7a49f; color: #7f1b15; }
/* Warnings: something needs attention but nothing failed. Pages used both spellings with no rule
   behind either, so a warning rendered as a bare outlined box. */
.alert-warn, .alert-warning { background: #fdf5e6; border-color: #e3c07a; color: #6a4500; }

/* Paste-ready command line + one-click Copy (admin diagnostics). Sits on its own paper card so the
   mono command reads cleanly even inside a coloured .alert; the command scrolls rather than wraps so
   it stays a single copy-paste line. */
.cmd-snippet { display: flex; align-items: stretch; margin: .6rem 0 .2rem; border: 1px solid var(--line-soft); border-radius: var(--radius-sm); background: #fff; overflow: hidden; max-width: 100%; }
.cmd-snippet-text { flex: 1 1 auto; min-width: 0; font-family: var(--font-mono); font-size: .8rem; line-height: 1.6; color: var(--ink); background: transparent; border: 0; border-radius: 0; padding: .5rem .7rem; white-space: nowrap; overflow-x: auto; }
.cmd-copy-btn { flex: none; cursor: pointer; font: inherit; font-size: .78rem; font-weight: 600; color: #fff; background: var(--brand-deep); border: 0; border-left: 1px solid var(--line-soft); padding: 0 1rem; min-width: 4.75rem; }
.cmd-copy-btn:hover { background: var(--brand-strong); }
.cmd-copy-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.cmd-copy-btn.is-copied { background: var(--brand); }

/* ---------------------------------------------------------------- footer */
.site-footer { background: var(--dark-bg); color: var(--dark-soft); margin-top: var(--space-16); }
.site-footer a { color: var(--dark-soft); }
.site-footer a:hover { color: var(--signal); }
.footer-grid { display: grid; grid-template-columns: 1.1fr 2fr; gap: var(--space-8); padding-block: var(--space-12) var(--space-8); }
@media (max-width: 44rem) { .footer-grid { grid-template-columns: 1fr; } }

/* Grouped footer columns: titled lists instead of one flat wrap-anywhere blob. */
.footer-cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-6) var(--space-8); align-content: start; }
@media (max-width: 56rem) { .footer-cols { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 26rem) { .footer-cols { grid-template-columns: 1fr; } }
.footer-heading {
    font-family: var(--font-display); font-weight: 600; font-size: .78rem;
    letter-spacing: .09em; text-transform: uppercase; color: var(--signal); margin: 0 0 .5rem;
}
.footer-col ul { list-style: none; margin: 0; padding: 0; }
.footer-col a { text-decoration: none; padding-block: .3rem; display: inline-flex; align-items: center; min-height: 44px; }
.footer-col a:hover { text-decoration: underline; }
.footer-brand { font-family: var(--font-display); font-weight: 700; font-size: 1.3rem; color: var(--dark-ink); letter-spacing: -.02em; }
.footer-links { display: flex; flex-wrap: wrap; gap: .4rem var(--space-6); list-style: none; margin: var(--space-3) 0 0; padding: 0; }
.footer-links a { text-decoration: none; padding-block: .35rem; display: inline-block; }
.footer-links a:hover { text-decoration: underline; }
.footer-bottom { border-top: 1px solid var(--dark-line); padding-block: var(--space-4); display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-6); justify-content: space-between; font-size: var(--text-sm); }
/* /contact collapses the three footer columns to one: the page's job is a message, and the full
   footer was taller than the form on a phone. */
.footer-cols-compact { display: block; }
.footer-cols-compact .footer-col { max-width: 22rem; }

.newsletter-form { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: end; margin-top: var(--space-4); }
.newsletter-form input { background: var(--dark-bg-2); border-color: #5f6e66; color: var(--dark-ink); max-width: 18rem; }
.newsletter-form input::placeholder { color: #7e948a; }
.newsletter-form label { color: var(--dark-ink); }

/* drip-form: email-course sign-up row on light marketing sections (base input styling applies). */
.drip-form { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: end; margin-top: var(--space-4); }
.drip-form .field { margin: 0; flex: 1 1 16rem; max-width: 24rem; }

/* course-days: the five-line curriculum in the sign-up box. */
.course-days { list-style: none; padding: 0; margin: var(--space-4) 0 0; display: grid; gap: .45rem; }
.course-days li { display: flex; gap: .65rem; align-items: baseline; }
.course-days .cd-n { flex: none; font-family: var(--font-display); font-weight: 700; font-size: .8rem; color: var(--brand-strong); background: var(--brand-wash); border: 1px solid var(--brand); border-radius: 999px; padding: .1rem .55rem; white-space: nowrap; }

/* ---------------------------------------------------------------- auth shell
   Account routes render in AuthLayout: brand + one help link up top, the card, and a
   legal-only footer. The card is deliberately the only thing on the page — sign-in is
   a single task, and everything below exists to keep it that way without JavaScript
   (the method switch is a radio pair; panels show/hide via :checked siblings). */
.auth-shell { display: grid; place-items: start center; padding-block: clamp(1.6rem, 4.5vw, 3.6rem) clamp(1.5rem, 4vw, 2.5rem); }
.auth-card { width: min(100%, 33rem); }
.auth-card .card { box-shadow: var(--shadow-2); }

.auth-head { border-bottom: 1px solid var(--line-soft); background: rgb(251 253 251 / .86); }
.auth-head-row { min-height: 64px; display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
/* The ≤56rem marketing-nav rules set .brand { order: 1 } globally; pin the help link
   after it so the auth header keeps brand-left at every width. */
.auth-help { order: 2; display: inline-flex; align-items: center; gap: .45rem; min-height: 44px; color: var(--ink-soft); font-weight: 600; text-decoration: none; }
.auth-help:hover { color: var(--brand-strong); }

main:has(> .auth-shell) { min-height: calc(100dvh - 10.5rem); }

.auth-foot { border-top: 1px solid var(--line-soft); font-size: var(--text-sm); color: var(--muted); }
.auth-foot-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-6); padding-block: var(--space-4); }
.auth-foot .footer-links a { color: var(--muted); }
.auth-foot .footer-links a:hover { color: var(--brand); }

.auth-panel { position: relative; overflow: hidden; padding-top: calc(clamp(1.25rem, 3vw, 1.75rem) + 4px); }
.auth-panel::before { content: ""; position: absolute; inset: 0 0 auto; height: 4px; background: linear-gradient(90deg, var(--brand-strong), var(--brand), var(--signal)); }

.auth-title { margin: 0; font-size: clamp(1.9rem, 1.5rem + 1.6vw, 2.4rem); text-align: center; }
.auth-lede { margin: .55rem auto 0; max-width: 30ch; color: var(--ink-soft); text-align: center; }
.auth-context { display: flex; align-items: flex-start; justify-content: center; gap: .5rem; margin: .8rem auto 0; max-width: 40ch; color: var(--muted); font-size: var(--text-sm); line-height: 1.45; }
.auth-context svg { flex: none; margin-top: .15rem; color: var(--brand); }

/* Error alert: outcome + the recovery actions, in one place. */
.auth-alert { display: flex; align-items: flex-start; gap: .7rem; margin-top: 1.2rem; padding: .9rem 1rem; border: 1px solid #efc3c0; border-left: 4px solid var(--bad); border-radius: var(--radius-sm); background: #fdf3f2; color: #7d1d18; }
.auth-alert svg { flex: none; margin-top: .1rem; }
.auth-alert p { margin: 0; font-size: .92rem; line-height: 1.5; }
.auth-alert-title { font-family: var(--font-display); font-weight: 650; font-size: .98rem; }
.auth-alert-title + p { margin-top: .2rem; }
.auth-alert-actions { display: flex; flex-wrap: wrap; gap: .25rem 1.1rem; margin-top: .5rem !important; }
.auth-alert-actions a { display: inline-flex; align-items: center; min-height: 30px; color: #7d1d18; font-weight: 650; }
.auth-alert.is-good { border-color: #b7dcc3; border-left-color: var(--good); background: #f2faf5; color: #14532d; }

/* A method that cannot work right now says so in its own panel, instead of offering a submit
   that would promise an email nothing will send. */
.auth-method-down { margin: .15rem 0 .5rem !important; padding: .8rem .9rem; border: 1px solid #f0d8a8; border-left: 4px solid var(--warn, #b8860b); border-radius: var(--radius-sm); background: #fdf7ea; color: #5c4408; font-size: .92rem; line-height: 1.55; }
.auth-method-down strong { display: block; font-family: var(--font-display); font-weight: 650; }
.auth-method-down a { color: #5c4408; font-weight: 650; text-decoration: underline; }

/* Method switch: two visually-hidden radios drive which panel shows. Keyboard users get
   native radio arrow-key behaviour; the focus ring is drawn on the active label. */
.auth-methods { margin-top: 1.25rem; }
.method-radio { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.method-tabs { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; padding: 4px; border: 1px solid var(--line-soft); border-radius: 12px; background: var(--wash); }
.method-tabs label { display: flex; align-items: center; justify-content: center; min-height: 44px; border-radius: 8px; color: var(--muted); font-family: var(--font-display); font-weight: 600; font-size: .95rem; cursor: pointer; }
#method-password:checked ~ .method-tabs label[for="method-password"],
#method-link:checked ~ .method-tabs label[for="method-link"] { color: var(--brand-strong); background: var(--card); box-shadow: var(--shadow-1); }
#method-password:focus-visible ~ .method-tabs label[for="method-password"],
#method-link:focus-visible ~ .method-tabs label[for="method-link"] { outline: 3px solid var(--focus); outline-offset: 1px; }
#method-link:checked ~ .auth-form .method-panel-password { display: none; }
#method-password:checked ~ .auth-form .method-panel-link { display: none; }

.auth-form { margin-top: 1.15rem; }
.field-split { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.field-split a { font-size: .88rem; white-space: nowrap; }

.password-wrap { position: relative; }
.password-wrap input { width: 100%; padding-right: 4.9rem; }
.password-toggle { position: absolute; top: 50%; right: .35rem; transform: translateY(-50%); min-width: 4rem; min-height: 38px; border: 0; border-radius: 7px; color: var(--brand-strong); background: transparent; font-weight: 650; font-size: .88rem; cursor: pointer; }
.password-toggle:hover { background: var(--brand-wash); }

.auth-submit { width: 100%; }
/* The code step of a two-step sign-in: digits spaced out so a typed code can be checked against
   the app at a glance. A recovery code is letters too, so it gets less tracking. */
.auth-code-input { font-family: var(--font-mono, ui-monospace, monospace); font-size: 1.35rem; letter-spacing: .3em; text-align: center; }
.auth-code-input.is-recovery { font-size: 1.15rem; letter-spacing: .12em; }
.auth-remember { display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: .6rem; align-items: start; margin: -.2rem 0 1.1rem; }
.auth-remember input { width: 1.15rem; height: 1.15rem; margin: .18rem 0 0; accent-color: var(--brand); }
.auth-remember label { font-weight: 600; cursor: pointer; }
.auth-remember .hint { grid-column: 2; margin: .15rem 0 0; }
.auth-submit[disabled] { opacity: .75; cursor: wait; }

/* Passkey shortcut. Deliberately quiet — a link-weight control under the form rather than a
   third method tab, because most visitors have no passkey and a tab would invite them to try a
   method they have never set up. Hidden until passkeys.js confirms the browser supports it. */
.auth-passkey { margin: .9rem 0 0; text-align: center; }
.auth-passkey-btn {
    display: inline-flex; align-items: center; gap: .45rem; padding: .35rem .2rem;
    border: 0; background: none; color: var(--brand); font: inherit; font-size: .9rem;
    font-weight: 650; cursor: pointer; text-decoration: underline; text-underline-offset: .2em;
}
.auth-passkey-btn:hover { text-decoration-thickness: 2px; }
.auth-passkey-btn[disabled] { color: var(--muted); cursor: default; text-decoration: none; }
.auth-passkey-btn svg { flex: none; }
.auth-passkey-note { margin: .5rem 0 0; color: var(--ink-soft); font-size: .85rem; line-height: 1.5; text-align: center; }

.auth-trust { display: flex; align-items: center; justify-content: center; gap: .45rem; margin: 1.1rem 0 0; color: var(--muted); font-size: .84rem; text-align: center; }
.auth-trust svg { flex: none; color: var(--brand); }

.auth-below { margin: 1.1rem 0 0; color: var(--muted); text-align: center; }
.auth-below a { font-weight: 650; }

/* Sent state: replaces the card body — success and "start again" never show together. */
.auth-sent { text-align: center; }
.auth-sent h1 { margin: 0; font-size: clamp(1.7rem, 1.4rem + 1.2vw, 2.1rem); }
.auth-sent-icon { display: grid; place-items: center; width: 56px; height: 56px; margin: .2rem auto 1rem; border: 1px solid var(--line); border-radius: 50%; color: var(--good); background: var(--brand-wash); }
.auth-sent > p[role="status"] { margin: .7rem auto 0; max-width: 44ch; color: var(--ink-soft); }
.auth-sent .actions { justify-content: center; }
.auth-sent-links { display: flex; flex-wrap: wrap; justify-content: center; gap: .3rem 1.25rem; margin: 1rem 0 0; font-size: .9rem; }
.auth-sent-links a { display: inline-flex; align-items: center; min-height: 32px; }

@media (max-width: 22.5rem) {
    .field-split { flex-direction: column; align-items: flex-start; gap: .15rem; }
    .field-split a { white-space: normal; }
}

/* ------------------------------------------------- low-cognition trust elements */
/* Verify-chip: a one-glance, one-click "check us yourself" link (Google PSI, security scan …). */
.verify-chip { display: inline-flex; align-items: center; gap: .5rem; min-height: 44px; padding: .45rem 1.05rem; border: 1.5px solid var(--line); border-radius: 999px; background: var(--card); color: var(--ink); text-decoration: none; font-weight: 600; font-size: .92rem; }
.verify-chip:hover { border-color: var(--brand); color: var(--brand-strong); }
.verify-chip svg { flex: none; color: var(--brand-strong); }
.verify-chip .vc-sub { font-weight: 400; color: var(--muted); font-size: .8rem; }
.section-dark .verify-chip { background: var(--dark-bg-2); border-color: #2c4437; color: var(--dark-soft); }
.section-dark .verify-chip:hover { border-color: var(--signal); color: var(--signal); }
.section-dark .verify-chip svg { color: var(--signal); }
/* Glance strip: a row of ≤5-word fact chips near a CTA — trust at skim speed. */
.glance-strip { display: flex; flex-wrap: wrap; gap: .4rem .6rem; margin: 1rem 0 0; padding: 0; list-style: none; }
.glance-strip li { display: inline-flex; align-items: center; gap: .4rem; border: 1px solid var(--line); border-radius: 999px; background: var(--card); padding: .35rem .8rem; font-size: .85rem; font-weight: 600; color: var(--ink); }
.glance-strip li svg { flex: none; color: var(--brand-strong); }
.glance-strip li a { color: inherit; text-decoration: none; }
.glance-strip li a:hover { color: var(--brand-strong); }
.section-dark .glance-strip { justify-content: center; }
.section-dark .glance-strip li { background: var(--dark-bg-2); border-color: #2c4437; color: var(--dark-soft); }
.section-dark .glance-strip li svg { color: var(--signal); }
.section-dark .glance-strip li a:hover { color: var(--signal); }

/* Generic segmented control for interactive tools. */
.mode-seg { display: inline-flex; gap: .35rem; flex-wrap: wrap; align-items: center; }
.mode-seg button {
    display: inline-flex; align-items: center; gap: .5rem;
    min-height: 44px; padding: .45rem 1rem; border: 1.5px solid var(--line); border-radius: 8px;
    background: var(--card); font-weight: 600; font-size: .92rem; cursor: pointer; color: var(--muted);
    transition: border-color .12s ease, background-color .12s ease, color .12s ease;
}
/* Selection needs a NON-COLOUR channel. The fill difference between selected and unselected
   measures 1.16:1, so the 1.5px border was carrying the entire signal — invisible at a glance,
   in greyscale, and to anyone with a colour-vision deficiency. The dot is present or absent. */
.mode-seg button::before {
    content: ""; width: .55rem; height: .55rem; border-radius: 50%; flex: none;
    border: 1.5px solid var(--line); background: transparent;
    transition: background-color .12s ease, border-color .12s ease;
}
.mode-seg button:hover { border-color: var(--muted); color: var(--ink); }
.mode-seg button.on, .mode-seg button[aria-pressed="true"] {
    border-color: var(--brand); color: var(--brand-strong); background: var(--brand-wash);
    box-shadow: inset 0 0 0 1px var(--brand);
}
.mode-seg button.on::before, .mode-seg button[aria-pressed="true"]::before { background: var(--brand); border-color: var(--brand); }
.mode-seg button[disabled] { opacity: .55; cursor: default; }
/* Two independent choices are two groups, each with its own visible label. */
/* The calculator's answer column. Sticks below the header so the current answer stays on screen
   while the inputs above it are being changed; capped and scrollable so a short laptop screen
   never loses the bottom of it. */
.result-col { display: flex; flex-direction: column; gap: 1rem; }
@media (min-width: 56.0625rem) {
    .result-col {
        position: sticky; top: calc(var(--header-h) + 1rem);
        max-height: calc(100dvh - var(--header-h) - 2rem); overflow-y: auto;
    }
}
.assumptions { margin: 0; padding: .85rem 1.1rem; border: 1px solid var(--line-soft); border-radius: var(--radius); background: var(--wash); font-size: .94rem; }
.assumptions__row { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; padding: .38rem 0; border-bottom: 1px solid var(--line-soft); }
.assumptions__row:last-child { border-bottom: 0; }
.assumptions dt { color: var(--muted); margin: 0; }
.assumptions dd { margin: 0; font-weight: 600; text-align: right; }
.assumptions__src { font-weight: 500; color: var(--muted); font-size: .88rem; }

/* Drop target. The input is stretched over the whole box at zero opacity, so the OS drop and the
   click both land on it anywhere inside the dashed border — the affordance the copy promises,
   with no JavaScript and no loss of the plain picker when scripting is off. */
.dropzone { position: relative; }
.dropzone__inner {
    display: flex; flex-direction: column; align-items: center; gap: .5rem;
    padding: 1.4rem 1rem; text-align: center;
    border: 2px dashed var(--line); border-radius: var(--radius);
    background: var(--wash); color: var(--ink-soft);
    transition: border-color .12s ease, background-color .12s ease;
}
.dropzone__icon { width: 26px; height: 26px; flex: none; color: var(--muted); }
.dropzone__prompt { font-weight: 600; }
.dropzone__hint { font-size: .9rem; color: var(--muted); }
.dropzone__input {
    position: absolute; inset: 0; width: 100%; height: 100%;
    min-height: 0; padding: 0; margin: 0; border: 0; opacity: 0; cursor: pointer;
}
.dropzone:hover .dropzone__inner { border-color: var(--brand); background: var(--brand-wash); }
.dropzone__input:focus-visible + .dropzone__inner,
.dropzone:focus-within .dropzone__inner { outline: 3px solid var(--focus); outline-offset: 1px; border-color: var(--focus); }
.dropzone--loaded .dropzone__inner { border-style: solid; border-color: var(--brand); background: var(--brand-wash); }
@media (prefers-reduced-motion: reduce) { .dropzone__inner { transition: none; } }

.field-row { display: flex; flex-wrap: wrap; gap: var(--space-4); margin-bottom: var(--space-4); }
.field-row > .field { margin-bottom: 0; flex: 0 1 auto; }
.field__label { display: block; font-weight: 600; margin-bottom: .35rem; font-size: .97rem; }

/* ------------------------------------------------- accessibility (WCAG 2.2) */
/* Screen-reader-only text (table captions etc.) — visually removed, programmatically present. */
.visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
/* 1.4.8 AAA: readable measure and paragraph rhythm. 70ch of proportional text renders at ~75
   actual characters per line, keeping every block under the 80-character ceiling. */
.container p, .container-narrow p, .container li, .container-narrow li { max-width: 70ch; }
p { margin-block-end: 1.6em; }
/* 1.4.3: .hint inherits --muted, which is tuned for light surfaces (7.03:1 on paper) but lands at
   2.53:1 on the dark footer — dark surfaces need their own muted tone (7.07:1 on --dark-bg). */
.site-footer .hint, .section-dark .hint { color: #8aa89b; }
/* 2.4.11/2.4.12: the sticky header must never cover a focused or anchor-jumped element. */
/* Anchor offset comes from the SAME variable as the header height, so the two cannot drift.
   Hard-coding 6rem is what broke it: correct for the 71px desktop header, 119px short of the
   215px mobile one, which put every deep-linked heading fully behind the bar. */
html { scroll-padding-top: calc(var(--header-h) + 1.5rem); }
/* Belt and braces: a target's own scroll-margin wins even if the global ever goes stale. */
[id]:target { scroll-margin-top: calc(var(--header-h) + 1.5rem); }
/* 2.4.13: on the dark footer the single-tone blue input outline measures 2.91:1 — give inputs on
   dark surfaces the same two-tone ring (white inner, blue outer) links use everywhere. */
.site-footer input:focus-visible { outline: 2px solid #fff; outline-offset: 0; box-shadow: 0 0 0 4px var(--focus); }
/* Coined-term and abbreviation semantics without a visual jolt. */
dfn { font-style: inherit; font-weight: inherit; }
abbr[title] { text-decoration: underline dotted; text-underline-offset: 2px; cursor: help; }

/* ------------------------------------------------- mobile ergonomics (≤ phones) */
/* "PageSpeed Insights" in the hero H1 is a no-break unit (the highlighter underline);
   at phone widths its min-content forces the hero column wider than the viewport and
   .hero { overflow: clip } silently cuts the H1, lede and note. On small screens the
   em must wrap — the underline moves from the single-box ::after to a per-line-fragment
   background — and the hero type steps down one notch. */
@media (max-width: 30rem) {
    :root { --text-hero: clamp(2rem, 1.2rem + 5vw, 2.5rem); }
    .hero h1 em {
        white-space: normal;
        -webkit-box-decoration-break: clone; box-decoration-break: clone;
        background: linear-gradient(90deg, var(--signal), #bff5cd) 0 calc(100% - .04em) / 100% .14em no-repeat;
    }
    .hero h1 em::after { content: none; }
}

/* Readability + tap-target floors on phones: secondary text up to 16px, uppercase
   labels off the 12.5px floor, and standalone link-paragraphs get a ≥24px hit area
   (visually unchanged — padding offset by negative margin). */
@media (max-width: 21.5rem) { .btn { white-space: normal; } } /* tiny phones: buttons may wrap, never overflow */
@media (max-width: 40rem) {
    :root { --text-sm: 1rem; }
    .eyebrow { font-size: .8125rem; }
    table.data { font-size: 1rem; }
    table.data thead th { font-size: .875rem; }
    main p > a:only-child { display: inline-block; padding-block: .3rem; margin-block: -.3rem; }
}

/* Responsive compression funnel (framework §6): row on wide, stacked on phones. */
.viz-funnel { display: grid; grid-template-columns: 1fr auto 1.4fr; gap: .8rem; align-items: center; }
@media (max-width: 40rem) {
    .viz-funnel { grid-template-columns: 1fr; }
    .viz-funnel-arrow { transform: rotate(90deg); justify-self: center; }
}

/* ---------------------------------------------------------------- print */
@media print {
    .site-header, .site-footer, .skip-link, .actions, .hero-cta, .nav-cta { display: none; }
    body { color: #000; background: #fff; }
    .hero, .section-dark { background: #fff; color: #000; }
}

/* ---------------------------------------------------------------- touch targets
   On phone layouts and touch screens every control a thumb aims at is at least 44px tall (Apple and
   Android guidance; WCAG 2.5.5). Inline links inside running text are exempt, as WCAG allows. */
@media (max-width: 56rem), (pointer: coarse) {
    .btn-sm { min-height: 44px; }
    .footer-links a { display: inline-flex; align-items: center; min-height: 44px; }
    .password-toggle { min-height: 44px; }
    .auth-passkey-btn { min-height: 44px; }
    summary { min-height: 44px; }
    .field-split > a { display: inline-flex; align-items: center; min-height: 44px; }
    input[type="file"]::file-selector-button { min-height: 44px; }
    .auth-alert-actions a { min-height: 44px; }
}
