/* ==========================================================================
   weTrack — rptindustries.com

   Direction: editorial-industrial, in the manner of the best hardware company
   sites. Deep blue is the page, near-black panels carry the product, off-white
   panels carry the proof, and RPT red is the working accent. Type does the
   heavy lifting: big uppercase statements set tight, and Geist Mono labels in
   [ square brackets ] doing the wayfinding.

   Theming: every component reads its colours from the token block below.
   - :root is the whole theme — the page is printed on off-white. There is no
     mode switch; the site has one palette.
   - Sections that are not off-white re-issue those tokens for themselves:
     product panels and the footer stay near-black, and the hero photography is
     dark whatever sits over it. One selector list per palette, so a section
     joins by name rather than by copying tokens.

   Order: tokens · reset · type · labels · buttons · chrome · hero · sections ·
   blocks · forms · error · footer · intro · ergonomics · reduced motion · print
   ========================================================================== */

:root {
    /* Form controls, scrollbars and other UA-painted chrome follow the page. */
    color-scheme: light;

    /* --- page surfaces: off-white ---------------------------------------- */
    --ink-0: #eef2f5;
    --ink-1: #e7edf2;
    --ink-2: #dfe6ed;
    --ink-3: #d4dde6;

    --line: rgba(28, 43, 68, 0.12);
    --line-strong: rgba(28, 43, 68, 0.26);

    --text-hi: #16233a;
    --text: rgba(22, 35, 58, 0.92);
    --text-lo: rgba(22, 35, 58, 0.7);
    --text-dim: rgba(22, 35, 58, 0.66);
    --text-chrome: rgba(22, 35, 58, 0.36);

    /* Frosted veil behind the fixed nav once the page scrolls. The --media
       variant is for the stretch where the bar is still over a hero photo:
       those images are dark, so the veil over them has to be dark too. */
    --nav-veil: rgba(238, 242, 245, 0.78);
    --nav-veil-media: rgba(19, 29, 47, 0.78);

    /* Dropdown panels hung from the bar: solid enough to read over anything. */
    --nav-panel-bg: rgba(238, 242, 245, 0.94);
    --nav-panel-edge: rgba(28, 43, 68, 0.14);
    --nav-panel-specular: rgba(255, 255, 255, 0.9);

    /* --- liquid glass: frosted plates over photography and colour ---------
       Fill is a gradient so the pane catches light unevenly, the way real
       glass does; edge is the rim; specular is the bright line along the top
       where the light source sits. Components add backdrop-filter themselves,
       because the blur radius is a per-surface decision. */
    --glass-fill: linear-gradient(160deg, rgba(255, 255, 255, 0.72), rgba(255, 255, 255, 0.42) 38%, rgba(255, 255, 255, 0.6));
    --glass-edge: rgba(28, 43, 68, 0.14);
    --glass-specular: rgba(255, 255, 255, 0.9);
    --glass-shadow: 0 26px 64px -24px rgba(16, 27, 46, 0.48);

    /* --- elevation: one shadow is a smudge, a stack is depth ---------------
       Four layers, doubling in throw: contact, near, mid, ambient. Rest is a
       panel lying on the page; high is the same panel picked up. */
    --elev-rest: 0 2px 4px rgba(16, 27, 46, 0.2),
                 0 8px 18px -3px rgba(16, 27, 46, 0.22),
                 0 22px 44px -8px rgba(16, 27, 46, 0.26),
                 0 52px 96px -16px rgba(16, 27, 46, 0.32);
    --elev-high: 0 3px 6px rgba(16, 27, 46, 0.18),
                 0 14px 28px -4px rgba(16, 27, 46, 0.24),
                 0 36px 70px -10px rgba(16, 27, 46, 0.30),
                 0 80px 132px -20px rgba(16, 27, 46, 0.36);

    /* --- signal: RPT red carries the accent, blue supports --------------- */
    --signal: #e63329;
    --signal-hot: #ff4a3f;
    /* Red as running text at label size — deeper than the accent, because the
       accent itself sits below AA on off-white. The near-black panels below
       re-issue this one lighter, for the same reason in reverse. */
    --signal-text: #c0261c;
    /* Red as a filled control — deep enough that white type on it clears AA.
       The eye reads it as the same red; the meter does not. */
    --signal-fill: #dd2d23;
    --signal-ink: rgba(230, 51, 41, 0.08);
    --signal-edge: rgba(230, 51, 41, 0.4);
    --signal-glow: rgba(230, 51, 41, 0.3);
    --accent: #6290cc;
    --accent-ink: rgba(98, 144, 204, 0.16);

    /* --- lamp state: form success panel only ------------------------------ */
    --lamp-nominal: #35c46a;

    /* --- type ------------------------------------------------------------- */
    --display: 'Inter', 'Segoe UI', system-ui, -apple-system, sans-serif;
    --mono: 'Geist Mono', ui-monospace, 'Cascadia Mono', Consolas, monospace;

    --step--1: clamp(0.78rem, 0.76rem + 0.1vw, 0.85rem);
    --step-0: clamp(0.94rem, 0.91rem + 0.16vw, 1.04rem);
    --step-1: clamp(1.08rem, 1rem + 0.36vw, 1.32rem);
    --step-2: clamp(1.45rem, 1.2rem + 1.1vw, 2.1rem);
    --step-3: clamp(1.9rem, 1.4rem + 2.2vw, 3.1rem);
    --step-4: clamp(2.1rem, 1.4rem + 2.9vw, 3.8rem);
    --step-5: clamp(2.4rem, 1.5rem + 3.4vw, 4.5rem);

    /* --- structure ---------------------------------------------------------- */
    --shell: 1360px;
    --gutter: clamp(1.25rem, 4vw, 3.5rem);

    --radius: 6px;
    --radius-sm: 3px;

    --nav-h: 4rem;

    --ease: cubic-bezier(0.22, 1, 0.36, 1);
    --ease-io: cubic-bezier(0.4, 0, 0.2, 1);
}

/*
 * Pinned palettes. The near-black set is where the product lives — feature
 * panels, the numbers strip, the closer, the footer — and it does not flip
 * with the theme. Same for the off-white proof bands. One selector list per
 * palette, so a new section joins by name rather than by copying tokens.
 */
.feature,
.telemetry,
.duo,
.closer,
.site-footer,
.intro {
    --ink-0: #0f0f15;
    --ink-1: #13131b;
    --ink-2: #1a1a24;
    --ink-3: #22222e;

    --line: rgba(238, 242, 245, 0.09);
    --line-strong: rgba(238, 242, 245, 0.2);

    --text-hi: #eef2f5;
    --text: rgba(238, 242, 245, 0.9);
    --text-lo: rgba(238, 242, 245, 0.68);
    --text-dim: rgba(238, 242, 245, 0.58);
    --text-chrome: rgba(238, 242, 245, 0.34);

    --signal-text: #ff6b60;
    --signal-ink: rgba(230, 51, 41, 0.12);
    --signal-edge: rgba(230, 51, 41, 0.34);

    --glass-fill: linear-gradient(160deg, rgba(238, 242, 245, 0.10), rgba(238, 242, 245, 0.03) 38%, rgba(238, 242, 245, 0.07));
    --glass-edge: rgba(238, 242, 245, 0.16);
    --glass-specular: rgba(255, 255, 255, 0.25);
    --glass-shadow: 0 24px 60px -28px rgba(4, 8, 16, 0.7);

    --elev-rest: 0 2px 4px rgba(0, 0, 0, 0.5),
                 0 8px 18px -3px rgba(0, 0, 0, 0.5),
                 0 22px 44px -8px rgba(0, 0, 0, 0.55),
                 0 52px 96px -16px rgba(0, 0, 0, 0.62);
    --elev-high: 0 3px 6px rgba(0, 0, 0, 0.55),
                 0 14px 28px -4px rgba(0, 0, 0, 0.58),
                 0 36px 70px -10px rgba(0, 0, 0, 0.62),
                 0 80px 132px -20px rgba(0, 0, 0, 0.7);
}

/* The hero stays deep blue in both modes — it is the brand surface. */
.hero {
    --ink-0: #1c2b44;
    --ink-1: #22304b;
    --ink-2: #283852;
    --ink-3: #30425f;

    --glass-fill: linear-gradient(160deg, rgba(238, 242, 245, 0.10), rgba(238, 242, 245, 0.03) 38%, rgba(238, 242, 245, 0.07));
    --glass-edge: rgba(238, 242, 245, 0.16);
    --glass-specular: rgba(255, 255, 255, 0.25);
    --glass-shadow: 0 24px 60px -28px rgba(4, 8, 16, 0.7);

    --elev-rest: 0 2px 4px rgba(0, 0, 0, 0.5),
                 0 8px 18px -3px rgba(0, 0, 0, 0.5),
                 0 22px 44px -8px rgba(0, 0, 0, 0.55),
                 0 52px 96px -16px rgba(0, 0, 0, 0.62);
    --elev-high: 0 3px 6px rgba(0, 0, 0, 0.55),
                 0 14px 28px -4px rgba(0, 0, 0, 0.58),
                 0 36px 70px -10px rgba(0, 0, 0, 0.62),
                 0 80px 132px -20px rgba(0, 0, 0, 0.7);

    --line: rgba(238, 242, 245, 0.09);
    --line-strong: rgba(238, 242, 245, 0.2);

    --text-hi: #eef2f5;
    --text: rgba(238, 242, 245, 0.9);
    --text-lo: rgba(238, 242, 245, 0.68);
    --text-dim: rgba(238, 242, 245, 0.58);
    --text-chrome: rgba(238, 242, 245, 0.34);
}

/*
 * Off-white proof bands — identical in both modes. The customer marks join
 * this palette: every logo file carries a baked white background, so an
 * off-white sheet is the only surface where 200 third-party trademarks read
 * as one wall instead of 200 white stickers.
 */
.band--light,
.strap {
    --ink-0: #eef2f5;
    --ink-1: #e7edf2;
    --ink-2: #dfe6ed;
    --ink-3: #d4dde6;

    --line: rgba(28, 43, 68, 0.12);
    --line-strong: rgba(28, 43, 68, 0.26);

    --text-hi: #16233a;
    --text: rgba(22, 35, 58, 0.92);
    --text-lo: rgba(22, 35, 58, 0.7);
    --text-dim: rgba(22, 35, 58, 0.66);
    --text-chrome: rgba(22, 35, 58, 0.36);

    --signal-text: #c0261c;
    --signal-ink: rgba(230, 51, 41, 0.08);
    --signal-edge: rgba(230, 51, 41, 0.4);

    --glass-fill: linear-gradient(160deg, rgba(255, 255, 255, 0.72), rgba(255, 255, 255, 0.42) 38%, rgba(255, 255, 255, 0.6));
    --glass-edge: rgba(28, 43, 68, 0.14);
    --glass-specular: rgba(255, 255, 255, 0.9);
    --glass-shadow: 0 26px 64px -24px rgba(16, 27, 46, 0.48);

    --elev-rest: 0 2px 4px rgba(16, 27, 46, 0.2),
                 0 8px 18px -3px rgba(16, 27, 46, 0.22),
                 0 22px 44px -8px rgba(16, 27, 46, 0.26),
                 0 52px 96px -16px rgba(16, 27, 46, 0.32);
    --elev-high: 0 3px 6px rgba(16, 27, 46, 0.18),
                 0 14px 28px -4px rgba(16, 27, 46, 0.24),
                 0 36px 70px -10px rgba(16, 27, 46, 0.30),
                 0 80px 132px -20px rgba(16, 27, 46, 0.36);
}

/* ==========================================================================
   Reset and base
   ========================================================================== */

*,
*::before,
*::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html {
    scroll-behavior: smooth;
    -webkit-text-size-adjust: 100%;
}

/* Lenis drives the scroll when scripting is up; its classes are set on <html>. */
html.lenis,
html.lenis body {
    height: auto;
}

.lenis.lenis-smooth {
    scroll-behavior: auto !important;
}

.lenis.lenis-stopped {
    overflow: hidden;
}

body {
    font-family: var(--display);
    font-size: var(--step-0);
    line-height: 1.6;
    letter-spacing: -0.01em;
    color: var(--text);
    background: var(--ink-0);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    transition: background 0.35s var(--ease-io), color 0.35s var(--ease-io);
}

body.nav-open {
    overflow: hidden;
}

.page {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

main {
    flex: 1;
}

img,
svg,
video {
    display: block;
    max-width: 100%;
}

/* Width/height attributes reserve layout space, but once CSS resizes the width
   the attribute height must not stay literal — that reads as a stretch. Class
   rules that set an explicit height (badges, covers) still win over this. */
img {
    height: auto;
}

ul,
ol {
    list-style: none;
}

a {
    color: inherit;
}

button {
    font: inherit;
    color: inherit;
    background: none;
    border: 0;
    cursor: pointer;
}

::selection {
    background: var(--signal);
    color: #fff;
}

/*
 * Film grain over the whole page — one fixed, never-repainted layer of static
 * noise at a level you feel rather than see. It is what stops large flat fills
 * (and there are many here) from reading as untextured vector colour on a
 * good display. Held under the skip link, and off for print.
 */
body::after {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 2500;
    pointer-events: none;
    opacity: 0.05;
    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0.5 0 0 0 0 0.5 0 0 0 0 0.5 0 0 0 0.9 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

@media print {
    body::after {
        display: none;
    }
}

/*
 * The window furniture, in the page's own palette. Both properties read
 * theme-following tokens, so the bar flips with the light switch on its own.
 */
html {
    scrollbar-width: thin;
    scrollbar-color: var(--line-strong) transparent;
}

/* Chrome and Safari builds that predate scrollbar-color. Ignored by the ones
   that don't, so the two never fight. */
::-webkit-scrollbar {
    width: 11px;
    height: 11px;
}

::-webkit-scrollbar-track {
    background: transparent;
}

::-webkit-scrollbar-thumb {
    background: var(--line-strong);
    background-clip: content-box;
    border: 3px solid transparent;
    border-radius: 99px;
}

::-webkit-scrollbar-thumb:hover {
    background: var(--text-dim);
    background-clip: content-box;
}

/* Keyboard focus has to be obvious on both palettes. */
:focus-visible {
    outline: 2px solid var(--signal-hot);
    outline-offset: 3px;
    border-radius: 1px;
}

.skip-link {
    position: fixed;
    top: 0;
    left: 50%;
    z-index: 3000;
    transform: translate(-50%, -110%);
    padding: 0.75rem 1.5rem;
    background: var(--signal-fill);
    color: #fff;
    font-family: var(--mono);
    font-size: 0.72rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    text-decoration: none;
    transition: transform 0.2s var(--ease-io);
}

/* Plain :focus as well as :focus-visible — this link is only ever reached by
   keyboard, so there is no mouse-click case to suppress. */
.skip-link:focus,
.skip-link:focus-visible {
    transform: translate(-50%, 0);
}

.icon-sprite {
    position: absolute;
    width: 0;
    height: 0;
    overflow: hidden;
}

/* ==========================================================================
   Type system — statements in caps, sentences in sentence case
   ========================================================================== */

h1,
h2,
h3,
h4 {
    font-family: var(--display);
    color: var(--text-hi);
    font-weight: 600;
    line-height: 1.04;
    letter-spacing: -0.015em;
    text-wrap: balance;
}

/* The reference register: statements in caps, set heavy and tight — at 600 the
   uppercase reads like a default; at 650 it reads like it was drawn for this. */
h1,
h2 {
    font-weight: 650;
    letter-spacing: -0.022em;
    text-transform: uppercase;
    line-height: 0.96;
}

/* Cap-height trim, where the browser can do it: the box hugs the letterforms,
   so rules and images align to the type optically instead of to its leading. */
@supports (text-box: trim-both cap alphabetic) {
    h1,
    h2 {
        text-box: trim-both cap alphabetic;
        padding-block: 0.08em;
    }
}

.t-h3 {
    font-size: var(--step-1);
    font-weight: 550;
    letter-spacing: -0.01em;
}

.t-lead {
    font-size: var(--step-1);
    color: var(--text-lo);
    line-height: 1.55;
    max-width: 58ch;
    text-wrap: pretty;
}

/*
 * The label language: Geist Mono, uppercase, held between square brackets the
 * way a caption sits on an engineering drawing. Brackets are drawn by the
 * component so no page has to remember to type them.
 */
.tag {
    display: inline-flex;
    align-items: baseline;
    gap: 0.6em;
    font-family: var(--mono);
    font-size: 0.72rem;
    font-weight: 450;
    letter-spacing: 0.07em;
    line-height: 1.4;
    text-transform: uppercase;
    color: var(--text-dim);
    font-variant-numeric: tabular-nums;
}

.tag::before {
    content: '[';
    color: var(--text-chrome);
}

.tag::after {
    content: ']';
    color: var(--text-chrome);
}

/* Emphasised variant — the label reads in the accent. */
.tag--plate {
    color: var(--signal-text);
}

/* Hero eyebrow — a step brighter than a body label. */
.tag--rule {
    color: var(--text-lo);
}

.tag--spaced {
    margin-top: clamp(2rem, 4vw, 2.75rem);
}

/* Divider between a code and its name, e.g. "[ EAM-01 | EAM system ]". */
.tag-code {
    padding-right: 0.7em;
    border-right: 1px solid var(--line-strong);
    color: var(--text-lo);
}

.ico {
    width: 1em;
    height: 1em;
    flex: none;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
    overflow: visible;
}

/* Brand marks are solid forms, not line drawings. */
.ico--solid {
    fill: currentColor;
    stroke: none;
}

/* ==========================================================================
   Buttons — generous, quiet, sentence case
   ========================================================================== */

.btn {
    display: inline-flex;
    align-items: center;
    gap: 0.65em;
    padding: 1.05rem 1.9rem;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    font-family: var(--display);
    font-size: 0.95rem;
    font-weight: 500;
    letter-spacing: -0.01em;
    text-decoration: none;
    cursor: pointer;
    transition: background 0.22s var(--ease-io), border-color 0.22s var(--ease-io),
                color 0.22s var(--ease-io), box-shadow 0.28s var(--ease-io),
                transform 0.28s var(--ease);
}

.btn:hover {
    transform: translateY(-2px);
}

/* The press has to land quickly or the button feels like it is on elastic. */
.btn:active {
    transform: translateY(0);
    transition-duration: 0.06s;
}

.btn .ico {
    font-size: 1.05em;
    transition: transform 0.28s var(--ease);
}

.btn:hover .ico {
    transform: translateX(4px);
}

/* A hairline of light along the top edge is what stops a flat fill reading as
   a coloured rectangle — the shadow ramps from seated to lifted on hover. */
.btn--primary {
    position: relative;
    overflow: hidden;
    background: var(--signal-fill);
    border-color: var(--signal-fill);
    color: #fff;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18),
                0 6px 18px -10px rgba(230, 51, 41, 0.55);
}

/* A sheen crosses the fill once per hover — light moving over a lacquered
   surface. No transition at rest, so it resets offscreen instantly and
   replays clean on the next hover. */
.btn--primary::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(115deg, transparent 32%, rgba(255, 255, 255, 0.32) 50%, transparent 68%);
    transform: translateX(-135%);
    pointer-events: none;
}

.btn--primary:hover::after {
    transform: translateX(135%);
    transition: transform 0.85s var(--ease);
}

.btn--primary:hover {
    background: var(--signal);
    border-color: var(--signal);
    color: #fff;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.24),
                0 14px 34px -12px rgba(230, 51, 41, 0.5);
}

/* The quiet button is a pane of glass — over the hero photo it frosts what
   passes behind it, which no flat fill can counterfeit. */
.btn--ghost {
    background: var(--glass-fill);
    border-color: var(--glass-edge);
    color: var(--text);
    -webkit-backdrop-filter: blur(14px) saturate(1.4);
    backdrop-filter: blur(14px) saturate(1.4);
    box-shadow: inset 0 1px 0 var(--glass-specular);
}

.btn--ghost:hover {
    border-color: var(--text-dim);
    background: var(--glass-fill), var(--accent-ink);
    color: var(--text-hi);
    box-shadow: inset 0 1px 0 var(--glass-specular), var(--glass-shadow);
}

.btn[disabled],
.btn.is-sending {
    opacity: 0.55;
    cursor: progress;
    pointer-events: none;
}

.btn-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.85rem;
}

.btn-row--center {
    justify-content: center;
}

/* Text link with a travelling arrow. */
.link-arrow {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 0.55em;
    align-self: flex-start;
    margin-top: 0.4rem;
    padding-block: 0.4rem;
    border-bottom: 1px solid var(--line-strong);
    font-family: var(--display);
    font-size: 0.92rem;
    font-weight: 500;
    letter-spacing: -0.01em;
    color: var(--text-hi);
    text-decoration: none;
    transition: gap 0.28s var(--ease), border-color 0.22s var(--ease-io), color 0.22s var(--ease-io);
}

.link-arrow .ico {
    color: var(--signal-hot);
}

/* The rule under the link is drawn on, left to right, rather than recoloured
   in place — the same gesture the arrow is already making. */
.link-arrow::after {
    content: '';
    position: absolute;
    inset: auto 0 -1px;
    height: 1px;
    background: var(--signal);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform 0.45s var(--ease);
}

.link-arrow:hover::after,
.duo-card:hover .link-arrow::after {
    transform: none;
}

.link-arrow:hover {
    gap: 1em;
    color: var(--text-hi);
}

/* ==========================================================================
   Backdrop — the survey grid, shared by the hero and page headers
   ========================================================================== */

.backdrop {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
}

/* Almost subliminal — a page texture, not graph paper. */
.backdrop-grid {
    position: absolute;
    inset: 0;
    background:
        linear-gradient(var(--line) 1px, transparent 1px),
        linear-gradient(90deg, var(--line) 1px, transparent 1px);
    background-size: 280px 280px;
    mask-image: radial-gradient(ellipse 110% 80% at 50% 0%, black 20%, transparent 70%);
    opacity: 0.2;
}

/* The old wordmark watermark is retired — the new register keeps surfaces clean. */
.backdrop-mark {
    display: none;
}

.backdrop-wash {
    position: absolute;
    inset: 0;
    background:
        radial-gradient(circle 720px at 84% 8%, var(--accent-ink), transparent 62%),
        radial-gradient(circle 560px at 8% 96%, var(--signal-ink), transparent 60%);
}

/* The hero carries a photograph (or its gradient stand-in) — no grid at all. */
.backdrop--hero .backdrop-grid {
    display: none;
}

/* ==========================================================================
   Scroll reveal
   ========================================================================== */

/* Scoped to .js — the layout sets that before first paint. Without scripting the
   content is simply there, never hidden behind an animation that cannot run. */
.js [data-reveal] {
    opacity: 0;
    transform: translateY(28px);
    transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
}

.js [data-reveal].is-revealed {
    opacity: 1;
    transform: none;
}

.js [data-reveal-child] {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 0.7s var(--ease) var(--reveal-delay, 0ms),
                transform 0.7s var(--ease) var(--reveal-delay, 0ms);
}

.js [data-reveal].is-revealed [data-reveal-child] {
    opacity: 1;
    transform: none;
}

/* ==========================================================================
   Navigation — a slim transparent bar that frosts once the page moves
   ========================================================================== */

.site-nav {
    position: fixed;
    inset-inline: 0;
    top: 0;
    z-index: 1000;
    background: transparent;
    border-bottom: 1px solid transparent;
    transition: background 0.3s var(--ease-io), border-color 0.3s var(--ease-io),
                backdrop-filter 0.3s var(--ease-io), transform 0.45s var(--ease);
}

/* Reading down, the bar steps out of the way; one flick up brings it back. */
.site-nav.is-hidden {
    transform: translateY(-105%);
}

.site-nav.is-condensed {
    background: var(--nav-veil);
    border-bottom-color: var(--line);
    -webkit-backdrop-filter: blur(16px) saturate(1.5);
    backdrop-filter: blur(16px) saturate(1.5);
    box-shadow: 0 14px 44px -22px rgba(5, 10, 20, 0.55);
}

/*
 * The bar frosts after 24px, but a hero photo runs for several hundred more —
 * so in the light theme it used to turn light-grey while the picture behind it
 * was still dark, which read as the header changing colour mid-scroll. While
 * the bar still overlaps that photo it keeps the dark veil and light type, and
 * only takes the theme's own veil once it has cleared the image. Toggled by
 * initNav(), which compares the media's bottom edge against the bar's height.
 */
.site-nav.is-condensed.is-over-media {
    background: var(--nav-veil-media);
    border-bottom-color: rgba(238, 242, 245, 0.16);
}

/*
 * How far down the page you are, drawn along the bottom edge of the frosted
 * bar. Scroll-timeline only — no script, and nothing at all in browsers that
 * cannot run it, which is the right failure for a decoration.
 */
@supports (animation-timeline: scroll()) {
    @media (prefers-reduced-motion: no-preference) {
        .site-nav::after {
            content: '';
            position: absolute;
            inset: auto 0 -1px;
            height: 2px;
            background: var(--signal);
            transform-origin: left center;
            opacity: 0;
            transition: opacity 0.3s var(--ease-io);
            animation: nav-progress linear both;
            animation-timeline: scroll(root block);
        }

        .site-nav.is-condensed::after {
            opacity: 1;
        }
    }
}

@keyframes nav-progress {
    from { transform: scaleX(0); }
    to { transform: scaleX(1); }
}

/* The home hero and the photo headers are pinned dark in both themes, so for as
   long as the bar sits over one it must read light-on-dark regardless of the
   theme choice — before it frosts, and after, until it has scrolled clear. */
body:has(.log-header--photo) .site-nav:not(.is-condensed),
body:has(.hero) .site-nav:not(.is-condensed),
body.home-page .site-nav:not(.is-condensed),
.site-nav.is-over-media {
    --text-hi: #eef2f5;
    --text: rgba(238, 242, 245, 0.9);
    --text-lo: rgba(238, 242, 245, 0.68);
    --text-dim: rgba(238, 242, 245, 0.58);
    --line-strong: rgba(238, 242, 245, 0.2);
    --accent-ink: rgba(98, 144, 204, 0.14);
    --nav-panel-bg: rgba(19, 29, 47, 0.95);
    --nav-panel-edge: rgba(238, 242, 245, 0.14);
    --nav-panel-specular: rgba(238, 242, 245, 0.16);
    --brand-dark: none;
    --brand-light: block;
}

.nav-container {
    display: flex;
    align-items: center;
    gap: 1.5rem;
    max-width: var(--shell);
    margin-inline: auto;
    padding: 0 var(--gutter);
    height: var(--nav-h);
}

/* Brand left; menu and actions gather on the right. */
.nav-brand {
    display: inline-flex;
    align-items: baseline;
    flex: none;
    margin-right: auto;
    text-decoration: none;
}

/* The brand is set as type — same split the product logo makes. */
.brand-word {
    font-size: 1.35rem;
    font-weight: 650;
    letter-spacing: -0.03em;
    color: var(--text-hi);
    line-height: 1;
}

.brand-we {
    color: var(--signal-text);
}

.brand-suffix {
    margin-left: 0.75rem;
    font-family: var(--mono);
    font-size: 0.6rem;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.nav-menu {
    display: flex;
    align-items: center;
}

.nav-list {
    display: flex;
    align-items: center;
    gap: clamp(1.1rem, 2.2vw, 2rem);
}

.nav-link {
    position: relative;
    font-size: 0.95rem;
    font-weight: 450;
    letter-spacing: -0.01em;
    color: var(--text-lo);
    text-decoration: none;
    padding-block: 0.4rem;
    transition: color 0.2s var(--ease-io);
}

/* Underline slides in from the left — drawn, not decorated. */
.nav-link::after {
    content: '';
    position: absolute;
    left: 0;
    bottom: 0;
    height: 1px;
    width: 100%;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.3s var(--ease);
}

.nav-link:hover,
.nav-link[aria-current='page'] {
    color: var(--text-hi);
}

.nav-link:hover::after,
.nav-link[aria-current='page']::after {
    transform: scaleX(1);
}

/* Quiet in the bar — the page below carries the red. */
/* The one filled control in the bar — the ask is stated, not whispered. */
.nav-cta {
    display: inline-flex;
    align-items: center;
    padding: 0.62rem 1.15rem;
    border: 1px solid var(--signal-fill);
    border-radius: 999px;
    background: var(--signal-fill);
    color: #fff;
    font-size: 0.88rem;
    font-weight: 500;
    letter-spacing: -0.01em;
    text-decoration: none;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.16);
    transition: background 0.22s var(--ease-io), border-color 0.22s var(--ease-io),
                color 0.22s var(--ease-io), box-shadow 0.22s var(--ease-io);
}

.nav-cta:hover {
    background: var(--signal);
    border-color: var(--signal);
    color: #fff;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.22),
                0 8px 22px -10px rgba(230, 51, 41, 0.6);
}

/* Theme flip — one small instrument on the right of the bar. */
/*
 * Phone navigation: the top bar keeps only the brand; the
 * links live in an always-visible bar along the bottom of the screen, app
 * style. No hamburger, no drawer — every page is one thumb-tap away.
 */
.tab-bar {
    position: fixed;
    inset-inline: 0;
    bottom: 0;
    z-index: 1200;
    display: none;
    grid-template-columns: repeat(5, 1fr);
    background: var(--nav-veil);
    -webkit-backdrop-filter: blur(22px) saturate(1.7);
    backdrop-filter: blur(22px) saturate(1.7);
    border-top: 1px solid var(--line);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
    padding: 0.45rem 0.35rem calc(0.55rem + env(safe-area-inset-bottom, 0px));
}

.tab-item {
    display: grid;
    justify-items: center;
    gap: 0.3rem;
    padding: 0.45rem 0.25rem 0.35rem;
    border-radius: var(--radius);
    text-decoration: none;
    color: var(--text-dim);
    transition: color 0.2s var(--ease-io), background 0.2s var(--ease-io);
}

.tab-item .ico {
    font-size: 1.35rem;
}

.tab-item span {
    font-family: var(--mono);
    font-size: 0.58rem;
    font-weight: 450;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    line-height: 1;
}

.tab-item[aria-current='page'] {
    color: var(--text-hi);
}

.tab-item[aria-current='page'] .ico {
    color: var(--signal-hot);
}

/* The demo tab sits as quiet as the rest — the page sells, the bar navigates. */

@media (max-width: 992px) {
    /* Links move to the bottom bar; the drawer is gone. */
    .nav-menu {
        display: none;
    }

    .tab-bar {
        display: grid;
    }

    /* Keep the last of every page clear of the fixed bar. */
    body {
        padding-bottom: calc(4.4rem + env(safe-area-inset-bottom, 0px));
    }
}

/* ==========================================================================
   Section shell
   ========================================================================== */

.band {
    position: relative;
    padding: clamp(4.5rem, 10vw, 8.5rem) var(--gutter);
    background: var(--ink-0);
}

.band--1 { background: var(--ink-1); }
.band--2 { background: var(--ink-1); }
.band--light { background: var(--ink-0); }

/* Two proof bands running back to back read as one sheet; a hairline and a
   shortened top margin keep them from looking like a doubled gap. */
.band--seam {
    padding-top: clamp(2.5rem, 5vw, 4rem);
}

.band--seam::before {
    content: '';
    position: absolute;
    inset: 0 var(--gutter) auto;
    height: 1px;
    background: var(--line);
}

.shell {
    max-width: var(--shell);
    margin-inline: auto;
}

/* A narrow reading column that keeps the shell's left edge rather than
   re-centring, so the whole page shares one margin. */
.shell--narrow > * {
    max-width: 60rem;
}

/* ==========================================================================
   Intro — the statement the home page opens on
   ========================================================================== */

/* Hidden without scripting: an overlay nothing can dismiss must never exist. */
.intro {
    display: none;
}

.js .intro {
    position: fixed;
    inset: 0;
    z-index: 2000;
    display: grid;
    place-items: center;
    background: var(--ink-0);
    transition: opacity 0.7s var(--ease-io), visibility 0.7s;
}

.js .intro.is-done {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
}

.intro-inner {
    display: grid;
    justify-items: center;
    gap: 1.5rem;
    padding: 0 var(--gutter);
    text-align: center;
    transition: opacity 0.6s var(--ease-io), transform 0.7s var(--ease-io);
}

/* The statement recedes a step as the overlay lifts — an exit, not a cut. */
.js .intro.is-done .intro-inner {
    opacity: 0;
    transform: scale(0.96);
}

.intro-title {
    font-size: var(--step-4);
    font-weight: 600;
    line-height: 0.96;
    letter-spacing: -0.02em;
    text-transform: uppercase;
    color: var(--text-hi);
    max-width: 16ch;
    opacity: 0;
    transform: translateY(16px);
    animation: intro-rise 0.9s var(--ease) 0.15s forwards;
}

.intro .tag {
    opacity: 0;
    animation: intro-rise 0.7s var(--ease) 0.05s forwards;
}

@keyframes intro-rise {
    to {
        opacity: 1;
        transform: none;
    }
}

/* A hairline filling along the bottom — the intro admits it is a beat, not a wall. */
.intro-progress {
    position: absolute;
    left: 0;
    bottom: 0;
    height: 2px;
    width: 100%;
    background: var(--signal);
    transform-origin: left;
    animation: intro-load 2.4s linear both;
}

@keyframes intro-load {
    from { transform: scaleX(0); }
    to { transform: scaleX(1); }
}

.intro-skip {
    position: absolute;
    right: var(--gutter);
    bottom: 1.75rem;
    font-family: var(--mono);
    font-size: 0.72rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-dim);
    padding: 0.6rem 0.9rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    transition: color 0.2s var(--ease-io), border-color 0.2s var(--ease-io);
}

.intro-skip:hover {
    color: var(--text-hi);
    border-color: var(--text-dim);
}

/* ==========================================================================
   Home hero — one statement, bottom-set, full screen
   ========================================================================== */

.hero {
    position: relative;
    display: flex;
    align-items: flex-end;
    min-height: max(100svh, 640px);
    padding: calc(var(--nav-h) + clamp(3rem, 8vh, 6rem)) var(--gutter) clamp(3rem, 7vh, 5rem);
    /* The gradient stand-in until the photograph lands in images/gen/. */
    background:
        radial-gradient(ellipse 1200px 640px at 78% -10%, rgba(98, 144, 204, 0.16), transparent 62%),
        radial-gradient(ellipse 900px 520px at 12% 108%, rgba(230, 51, 41, 0.1), transparent 60%),
        linear-gradient(178deg, #141f33 0%, #1c2b44 44%, #16223a 100%);
    overflow: hidden;
}

/* The photograph, when present: full-bleed, held down under a duotone scrim so
   the statement always reads. The slow drift is what makes a still feel like
   footage — the reference site runs video here. */
.hero-media {
    position: absolute;
    inset: 0;
    z-index: 1;
    width: 100%;
    height: 100%;
    object-fit: cover;
    animation: media-drift 28s var(--ease-io) infinite alternate;
}

/* Kept deliberately shallow: every extra percent of zoom is another percent of
   magnified softness on a source that is already being upscaled to cover the
   viewport. The translate carries most of the movement instead, and stays
   inside the margin the scale opens up so no edge is ever exposed. */
@keyframes media-drift {
    from { transform: scale(1); }
    to { transform: scale(1.03) translateX(-0.7%); }
}

.hero-scrim {
    position: absolute;
    inset: 0;
    z-index: 2;
    background:
        linear-gradient(180deg, rgba(16, 24, 40, 0.55) 0%, rgba(16, 24, 40, 0.1) 34%, rgba(16, 24, 40, 0.72) 88%),
        linear-gradient(90deg, rgba(16, 24, 40, 0.66) 0%, rgba(16, 24, 40, 0.15) 55%, rgba(16, 24, 40, 0.05) 100%);
    pointer-events: none;
}

/* A slow pass of light across the photograph — the difference between a still
   and a scene. Sits under the scrim so the statement always outranks it, and
   moves by transform alone, so it costs the compositor and nothing else. */
.hero-beam {
    position: absolute;
    top: -30%;
    bottom: -30%;
    left: -35%;
    width: 55%;
    z-index: 2;
    background: linear-gradient(90deg, transparent, rgba(238, 242, 245, 0.075) 45%, rgba(238, 242, 245, 0.075) 55%, transparent);
    mix-blend-mode: screen;
    transform: rotate(12deg) translateX(-20vw);
    animation: beam-drift 17s linear infinite;
    pointer-events: none;
}

@keyframes beam-drift {
    from { transform: rotate(12deg) translateX(-20vw); }
    to { transform: rotate(12deg) translateX(200vw); }
}

/*
 * The way down. A hairline with a bead of signal running it, parked bottom
 * centre — it scrolls away with the hero, so it never needs dismissing.
 * Centring rides the separate translate property, so the entrance animation
 * (which owns transform) cannot knock the cue off axis.
 */
.scroll-cue {
    position: absolute;
    left: 50%;
    bottom: 1.1rem;
    z-index: 3;
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: 0.6rem;
    translate: -50% 0;
    text-decoration: none;
    font-family: var(--mono);
    font-size: 0.6rem;
    font-weight: 450;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: var(--text-dim);
    transition: color 0.25s var(--ease-io);
    animation: hero-soft-rise 0.8s var(--ease) 1s both;
}

.scroll-cue:hover {
    color: var(--text-hi);
}

.scroll-cue-line {
    position: relative;
    width: 1px;
    height: 2.6rem;
    background: var(--line-strong);
    overflow: hidden;
}

.scroll-cue-line::after {
    content: '';
    position: absolute;
    top: 0;
    left: -1px;
    width: 3px;
    height: 0.9rem;
    background: var(--signal-hot);
    animation: cue-drop 2.1s var(--ease-io) infinite;
}

@keyframes cue-drop {
    0% { transform: translateY(-1rem); opacity: 0; }
    28% { opacity: 1; }
    100% { transform: translateY(2.8rem); opacity: 0; }
}

@media (max-width: 992px) {
    /* The hero is not full-height on a phone — there is nothing to cue. */
    .scroll-cue {
        display: none;
    }
}

.hero-inner {
    position: relative;
    z-index: 3;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: end;
    gap: clamp(2rem, 5vw, 4.5rem);
    width: 100%;
    max-width: var(--shell);
    margin-inline: auto;
}

.hero-eyebrow {
    margin-bottom: clamp(1.5rem, 3.5vh, 2.5rem);
}

.hero h1 {
    font-size: var(--step-5);
    font-weight: 600;
    line-height: 0.98;
    letter-spacing: -0.02em;
    color: var(--text-hi);
    margin-bottom: 1.5rem;
}

.hero h1 em {
    font-style: normal;
    color: var(--signal-hot);
    /* The last line lands white with the others, then ignites — the cost is
       the point of the sentence, and the delay is what makes the eye follow. */
    animation: em-ignite 0.9s var(--ease-io) 1.05s both;
}

@keyframes em-ignite {
    from {
        color: var(--text-hi);
        text-shadow: none;
    }

    to {
        color: var(--signal-hot);
        /* em, not px: a fixed glow reads as a wash once the type scales down. */
        text-shadow: 0 0 0.32em rgba(230, 51, 41, 0.3);
    }
}

.hero-sub {
    font-size: var(--step-1);
    color: var(--text-lo);
    line-height: 1.55;
    max-width: 52ch;
    margin-bottom: clamp(1.75rem, 4vh, 2.75rem);
    text-wrap: pretty;
}

.hero-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.85rem;
}

/*
 * Right column: the run sheet. Three logged facts on a pane of frosted glass
 * floating over the photograph — the one place on the page where the glass
 * has real imagery to refract, so it earns the blur.
 */
.hero-runsheet {
    display: grid;
    gap: 1.5rem;
    padding: 1.75rem 1.9rem;
    min-width: 15rem;
    background: var(--glass-fill);
    border: 1px solid var(--glass-edge);
    border-radius: 16px;
    -webkit-backdrop-filter: blur(20px) saturate(1.5);
    backdrop-filter: blur(20px) saturate(1.5);
    box-shadow: inset 0 1px 0 var(--glass-specular),
                inset 0 -1px 0 rgba(238, 242, 245, 0.04),
                var(--glass-shadow);
}

/* One step up from --text-dim: these labels sit low on the photograph, where
   the scrim is fighting a brighter patch of sky, and 0.58 alpha measures 4.1:1
   against it — just under AA at this size. */
.hero-runsheet dt {
    font-family: var(--mono);
    font-size: 0.66rem;
    font-weight: 450;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--text-lo);
    margin-bottom: 0.35rem;
}

.hero-runsheet dt::before {
    content: '[ ';
    color: var(--text-chrome);
}

.hero-runsheet dt::after {
    content: ' ]';
    color: var(--text-chrome);
}

.hero-runsheet dd {
    font-family: var(--display);
    font-size: 1.35rem;
    font-weight: 550;
    color: var(--text-hi);
    letter-spacing: -0.015em;
    font-variant-numeric: tabular-nums;
}

@media (max-width: 992px) {
    .hero {
        min-height: 0;
        align-items: flex-start;
        padding-top: calc(var(--nav-h) + 3.5rem);
    }

    .hero-inner {
        grid-template-columns: minmax(0, 1fr);
    }

    /* The pane lies flat under the copy on a phone — same glass, less frame. */
    .hero-runsheet {
        display: flex;
        flex-wrap: wrap;
        gap: 1.25rem 2.25rem;
        padding: 1.25rem 1.4rem;
        min-width: 0;
        border-radius: 12px;
    }
}

@media (max-width: 560px) {
    .hero h1 {
        max-width: none;
    }

    .hero-actions .btn {
        flex: 1 1 100%;
        justify-content: center;
    }
}

/* --------------------------------------------------------------------------
   Hero entrance — each line surfaces from its own mask, then the rest follows.
   Runs as pure CSS with backwards fill, so no-JS visitors still see everything;
   while the intro overlay is up the sequence holds, releasing when it ends.
   -------------------------------------------------------------------------- */

.hero-line {
    display: block;
    overflow: hidden;
}

.hero-line > span {
    display: block;
    animation: hero-line-rise 0.85s var(--ease) both;
}

.hero-line:nth-child(1) > span { animation-delay: 0.1s; }
.hero-line:nth-child(2) > span { animation-delay: 0.22s; }
.hero-line:nth-child(3) > span { animation-delay: 0.34s; }

@keyframes hero-line-rise {
    from { transform: translateY(108%); }
    to { transform: none; }
}

.hero-eyebrow,
.hero-sub,
.hero-actions,
.hero-runsheet {
    animation: hero-soft-rise 0.8s var(--ease) both;
}

.hero-eyebrow { animation-delay: 0.05s; }
.hero-sub { animation-delay: 0.5s; }
.hero-actions { animation-delay: 0.62s; }
.hero-runsheet { animation-delay: 0.74s; }

@keyframes hero-soft-rise {
    from { opacity: 0; transform: translateY(18px); }
    to { opacity: 1; transform: none; }
}

/* The overlay holds the stage until it has said its line. */
body.has-intro .hero-line > span,
body.has-intro .hero h1 em,
body.has-intro .hero-eyebrow,
body.has-intro .hero-sub,
body.has-intro .hero-actions,
body.has-intro .hero-runsheet,
body.has-intro .scroll-cue {
    animation-play-state: paused;
}

body.has-intro.is-live .hero-line > span,
body.has-intro.is-live .hero h1 em,
body.has-intro.is-live .hero-eyebrow,
body.has-intro.is-live .hero-sub,
body.has-intro.is-live .hero-actions,
body.has-intro.is-live .hero-runsheet,
body.has-intro.is-live .scroll-cue {
    animation-play-state: running;
}

/* Lazily loaded images surface instead of popping — scripted, so a no-JS
   visitor never waits on a fade that cannot run. */
.js img.img-fade {
    opacity: 0;
    transition: opacity 0.6s var(--ease-io);
}

.js img.img-fade.is-loaded {
    opacity: 1;
}

/* ==========================================================================
   Log header — every page other than home
   ========================================================================== */

.log-header {
    position: relative;
    padding: calc(var(--nav-h) + clamp(4rem, 10vh, 7rem)) var(--gutter) clamp(3rem, 7vw, 5rem);
    background: var(--ink-0);
    overflow: hidden;
}

/*
 * Photo header — the page opens on a photograph, pinned dark in both themes
 * so the title always reads light-on-dark.
 */
.log-header--photo {
    --ink-0: #1c2b44;
    --line: rgba(238, 242, 245, 0.09);
    --line-strong: rgba(238, 242, 245, 0.2);
    --text-hi: #eef2f5;
    --text: rgba(238, 242, 245, 0.9);
    --text-lo: rgba(238, 242, 245, 0.68);
    --text-dim: rgba(238, 242, 245, 0.58);
    --text-chrome: rgba(238, 242, 245, 0.34);

    display: flex;
    align-items: flex-end;
    min-height: clamp(420px, 58vh, 640px);
    background: #16223a;
}

.log-header--photo .log-header-inner {
    width: 100%;
}

.log-media {
    position: absolute;
    inset: 0;
    z-index: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    animation: media-drift 32s var(--ease-io) infinite alternate;
}

.log-scrim {
    position: absolute;
    inset: 0;
    z-index: 1;
    background:
        linear-gradient(180deg, rgba(16, 24, 40, 0.6) 0%, rgba(16, 24, 40, 0.12) 36%, rgba(16, 24, 40, 0.8) 92%),
        linear-gradient(90deg, rgba(16, 24, 40, 0.55) 0%, rgba(16, 24, 40, 0.1) 60%);
    pointer-events: none;
}

.log-header-inner {
    position: relative;
    z-index: 2;
    max-width: var(--shell);
    margin-inline: auto;
}

/* The run line: code, name, then a rule out to the margin. */
.log-run {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: clamp(1.5rem, 4vh, 2.5rem);
    font-family: var(--mono);
    font-size: 0.72rem;
    font-weight: 450;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.log-run-code {
    color: var(--signal-hot);
}

.log-run-code::before {
    content: '[ ';
    color: var(--text-chrome);
}

.log-run-name::after {
    content: ' ]';
    color: var(--text-chrome);
}

.log-rule {
    flex: 1;
    height: 1px;
    background: var(--line);
}

.log-title {
    font-size: var(--step-4);
    font-weight: 600;
    line-height: 0.96;
    letter-spacing: -0.02em;
    max-width: 22ch;
    margin-bottom: 1.25rem;
}

.log-sub {
    font-size: var(--step-1);
    color: var(--text-lo);
    line-height: 1.55;
    max-width: 56ch;
    text-wrap: pretty;
}

/* ==========================================================================
   Customer marquee — the wall, moving
   ========================================================================== */

.strap {
    padding: clamp(3rem, 6vw, 4.5rem) 0 clamp(3.25rem, 6.5vw, 5rem);
    background: var(--ink-0);
    overflow: hidden;
}

.strap-heading {
    text-align: center;
    margin-bottom: clamp(1.75rem, 4vw, 2.5rem);
    font-family: var(--mono);
    font-size: 0.7rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.strap-heading::before { content: '[ '; color: var(--text-chrome); }
.strap-heading::after { content: ' ]'; color: var(--text-chrome); }

/* Edges dissolve so the row reads as passing through the page. */
.marquee {
    overflow: hidden;
    mask-image: linear-gradient(90deg, transparent, black 12%, black 88%, transparent);
}

/* The track holds the logo list twice; sliding half its width loops seamlessly. */
.marquee-track {
    display: flex;
    align-items: center;
    gap: clamp(1rem, 2vw, 1.75rem);
    width: max-content;
    animation: marquee-slide 46s linear infinite;
}

.marquee:hover .marquee-track {
    animation-play-state: paused;
}

@keyframes marquee-slide {
    to {
        transform: translateX(calc(-50% - clamp(0.5rem, 1vw, 0.875rem)));
    }
}

.marquee .logo-tile {
    flex: none;
    width: clamp(7rem, 12vw, 11rem);
}

/* Reduced motion: the row parks and simply overflows out of view. */
@media (prefers-reduced-motion: reduce) {
    .marquee-track {
        animation: none;
    }
}

/* One combined customer banner on Who uses it; no separate category totals. */
.customer-banner { overflow: hidden; }
.customer-banner-head { text-align: center; margin-bottom: 2rem; }
.customer-banner-head h2 { margin: 0.75rem auto; max-width: 26ch; }
.customer-banner-subtitle { font-size: clamp(1rem, 2vw, 1.3rem); color: var(--text-lo); }
.customer-banner-toggle {
    margin-top: 1rem; padding: 0.5rem 0.9rem; border: 1px solid var(--line);
    border-radius: 999px; background: transparent; color: var(--text-hi);
    font: inherit; font-size: 0.8rem; cursor: pointer;
}
.customer-banner-toggle[hidden] { display: none; }
.customer-banner-toggle:focus-visible, .customer-banner-window:focus-visible {
    outline: 2px solid var(--signal); outline-offset: 3px;
}
.customer-banner-window { padding-block: 0.75rem; }
.customer-banner-track {
    display: flex; width: max-content;
    animation: customer-banner-slide var(--customer-duration, 1000s) linear infinite;
}
.customer-banner-group {
    display: flex; align-items: center; gap: 1.25rem; flex: none;
    margin: 0; padding: 0 1.25rem 0 0; list-style: none;
}
.customer-banner .logo-tile { width: clamp(9rem, 15vw, 12rem); }
.customer-banner-window:hover .customer-banner-track,
.customer-banner-window:focus-within .customer-banner-track,
.customer-banner.is-paused .customer-banner-track { animation-play-state: paused; }
.customer-banner.is-paused .customer-banner-window { overflow-x: auto; }
@keyframes customer-banner-slide { to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) {
    .customer-banner-track { animation: none; }
    .customer-banner-window { overflow-x: auto; }
    .customer-banner-group[aria-hidden="true"], .customer-banner-toggle { display: none; }
}

/* ==========================================================================
   The field board — the page's one big visual moment
   ========================================================================== */

.moment {
    padding: clamp(5rem, 11vw, 9rem) var(--gutter);
    background: var(--ink-0);
}

.moment-inner {
    max-width: var(--shell);
    margin-inline: auto;
}

.moment-head {
    text-align: center;
    margin-bottom: clamp(2.5rem, 6vw, 4rem);
}

.moment-head .tag {
    justify-content: center;
    margin-bottom: 1.1rem;
}

.moment-head h2 {
    font-size: var(--step-3);
    margin-inline: auto;
    max-width: 24ch;
}

.moment-head p:not(.tag) {
    margin: 1.1rem auto 0;
    color: var(--text-lo);
    line-height: 1.7;
    max-width: 56ch;
    text-wrap: pretty;
}

.moment-frame {
    border: 1px solid var(--line);
    border-radius: 20px;
    overflow: hidden;
    background: #10141d;
    box-shadow: var(--elev-high);
}

.field-board {
    display: block;
    width: 100%;
    height: auto;
}

/* Generated replacement for the drawn board — natural height, so the pulse
   overlay percentages map straight onto the artwork with no crop math. */
.moment-media {
    position: relative;
}

.moment-img {
    display: block;
    width: 100%;
    height: auto;
}

/*
 * Real type over generated artwork. The renders are made with blank label
 * chips on purpose — image models garble text — and these overlays put crisp
 * live text on top, positioned as percentages measured against each image.
 * Sizes are container-relative (cqi) so the labels scale with the artwork.
 */
.moment-media,
.art-media {
    container-type: inline-size;
}

.art-media {
    position: relative;
}

.art-label {
    position: absolute;
    transform: translate(-50%, -50%);
    display: grid;
    align-content: center;
    gap: 0.3em;
    justify-items: start;
    font-family: var(--mono);
    line-height: 1.2;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    white-space: nowrap;
    pointer-events: none;
    background: rgba(12, 17, 27, 0.94);
    border: 1px solid rgba(230, 51, 41, 0.28);
    border-radius: max(5px, 0.5cqi);
    padding: 0.5em 0.75em;
}

/*
 * The plate is the box. Each render has a blank chip baked in behind its
 * label, but an image model's chip and a live text box never agree on size —
 * the text has a legibility floor the chip doesn't know about. So instead of
 * shrinking type to fit a painted rectangle, every label carries its own
 * plate, sized by the min-widths below to blanket the baked one. One box on
 * screen at every viewport, and the artwork can be regenerated without
 * re-measuring a coordinate.
 */
.art-label--due {
    border-color: rgba(255, 74, 63, 0.6);
    background: rgba(26, 12, 12, 0.94);
}

.art-label strong {
    font-weight: 500;
    color: rgba(238, 242, 245, 0.92);
}

.art-label span {
    color: rgba(238, 242, 245, 0.55);
}

.art-label--due strong {
    color: var(--signal-hot);
}

.art-label--due span {
    color: rgba(255, 74, 63, 0.75);
}

/* Corner chrome — the board's own readouts. */
.art-chrome {
    position: absolute;
    font-family: var(--mono);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: rgba(238, 242, 245, 0.5);
    pointer-events: none;
}

/*
 * Board chips are small relative to the frame; card labels run larger. The
 * min sizes are the chips measured off the renders — 108x43 within 1774x887
 * for the board, 273x130 within 1400x933 for the manifest — expressed as a
 * share of the container's inline size so the plate tracks the artwork as it
 * scales. Text can push a plate past these; it can never fall short of them.
 */
.moment-media .art-label {
    min-width: 6.6cqi;
    min-height: 2.7cqi;
}
.moment-media .art-label strong { font-size: max(0.92cqi, 8px); }
.moment-media .art-label span { font-size: max(0.7cqi, 6.5px); }
.moment-media .art-chrome { font-size: max(0.95cqi, 8px); }

.art-media--manifest .art-label {
    min-width: 20.4cqi;
    min-height: 9.6cqi;
    border-radius: max(6px, 0.9cqi);
    padding: 0.55em 1.4cqi;
}
.art-media--manifest .art-label strong { font-size: max(1.85cqi, 8.5px); }
.art-media--manifest .art-label span { font-size: max(1.3cqi, 7.5px); }

/* The live readout has no room to sit clear of the plates on a narrow board. */
@container (max-width: 780px) {
    .art-chrome--live {
        display: none;
    }
}

/*
 * Phone-width board: the plates hold a legibility floor while the artwork
 * keeps shrinking, so at this size they crowd the corner readouts. The
 * readouts are the ones that give way — they're frame decoration, and unlike
 * a plate nothing is painted underneath them. Every plate stays put, because
 * hiding one uncovers the blank chip baked into the render.
 */
@container (max-width: 560px) {
    .moment-media .art-chrome {
        display: none;
    }

    /*
     * A 1774px board drawn 350px wide cannot carry five two-line labels at a
     * readable size. The well number is the information; the status line is
     * colour commentary, and it's the one that goes — which buys the number
     * enough room to be legible instead of leaving both lines too small to
     * read. Every plate still stays, so no baked chip is left uncovered.
     */
    .moment-media .art-label span {
        display: none;
    }

    .moment-media .art-label strong {
        font-size: max(0.92cqi, 11px);
    }
}

/* The manifest keeps all three lines — it only has three cards to place, and
   they're far enough apart to hold readable type. Same 11px floor as the board. */
@container (max-width: 640px) {
    .art-media--manifest .art-label strong { font-size: max(1.85cqi, 11px); }
    .art-media--manifest .art-label span { font-size: max(1.3cqi, 11px); }
}

/*
 * The three cards are spaced 27% of the render apart, so once the type stops
 * shrinking with the artwork their plates start closing on each other. Tighten
 * the internal rhythm instead of dropping a line — a serial number is the point
 * of the panel, and this buys back the millimetres needed to keep all three.
 */
@container (max-width: 420px) {
    .art-media--manifest .art-label {
        gap: 0.18em;
        padding: 0.35em 1.1cqi;
        min-height: 8.4cqi;
        line-height: 1.15;
    }
}

/* Below this the type would be squint-sized — the chips go quiet instead. */
@container (max-width: 300px) {
    .art-label,
    .art-chrome {
        display: none;
    }
}

/* The wells in the artwork breathe — same pulse as the drawn board's rings. */
.map-pulse {
    position: absolute;
    width: 14px;
    height: 14px;
    margin: -7px 0 0 -7px;
    border: 1.5px solid var(--signal-hot);
    border-radius: 50%;
    opacity: 0.9;
    pointer-events: none;
    animation: fb-pulse 3.2s var(--ease-io) infinite;
}

.map-pulse--due {
    border-width: 2px;
    animation-duration: 1.9s;
}

/* The wells breathe. One shared pulse; the flagged well runs hotter. */
.fb-ring {
    transform-box: fill-box;
    transform-origin: center;
    animation: fb-pulse 3.2s var(--ease-io) infinite;
}

.fb-ring--due {
    animation-duration: 1.9s;
}

@keyframes fb-pulse {
    0% { transform: scale(1); opacity: 0.9; }
    70% { transform: scale(2.6); opacity: 0; }
    100% { transform: scale(2.6); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .fb-ring {
        animation: none;
        opacity: 0.5;
    }
}

/* ==========================================================================
   Pillars — the off-white read, one capability at a time
   ========================================================================== */

.pillars {
    padding: clamp(4rem, 9vw, 7rem) var(--gutter);
}

.pillars-inner {
    max-width: var(--shell);
    margin-inline: auto;
}

.pillars-head {
    text-align: center;
    margin-bottom: clamp(1.5rem, 4vw, 2.5rem);
}

.pillars-head .tag {
    justify-content: center;
    margin-bottom: 1.1rem;
}

.pillars-head h2 {
    font-size: var(--step-3);
    margin-inline: auto;
    max-width: 24ch;
}

.pillar {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: center;
    gap: clamp(2.5rem, 6vw, 5.5rem);
    padding-block: clamp(3.5rem, 7vw, 5.5rem);
}

.pillar + .pillar {
    border-top: 1px solid var(--line);
}

/* Every other pillar reads from the other side. */
.pillar:nth-child(even) .pillar-media {
    order: 2;
}

/* A dark card floating on the paper — the product is a dark object here. */
.pillar-media {
    border-radius: 16px;
    overflow: hidden;
    border: 1px solid rgba(28, 43, 68, 0.14);
    background: #10141d;
    box-shadow: var(--elev-rest);
    transition: box-shadow 0.45s var(--ease-io);
}

/* The exhibit lifts toward the reader — no transform, so nothing fights the
   reveal; the deepening stack is the whole illusion. */
.pillar-media:hover {
    box-shadow: var(--elev-high);
}

.pillar-media img,
.pillar-media svg {
    display: block;
    width: 100%;
    max-width: none;
    height: auto;
    border: 0;
    border-radius: 0;
}

/* Generated renders arrive already graded — no brightness lift on the base
   card. The padded screenshot variant below keeps its own lift. */

/* Screenshots (as opposed to drawn sheets) float padded on a lit panel, so a
   dark capture never reads as a black hole in the card. */
.pillar-media--pad {
    display: grid;
    place-items: center;
    aspect-ratio: 8 / 5;
    padding: clamp(1.5rem, 3vw, 2.75rem);
    background:
        radial-gradient(ellipse 85% 75% at 50% 8%, rgba(98, 144, 204, 0.22), transparent 72%),
        #141a28;
}

.pillar-media--pad img {
    width: 100%;
    max-width: 30rem;
    filter: brightness(1.5) contrast(1.06);
}

.pillar-copy .tag {
    margin-bottom: 1.2rem;
}

.pillar-copy h3 {
    font-size: var(--step-2);
    font-weight: 600;
    letter-spacing: -0.02em;
    text-transform: uppercase;
    line-height: 0.98;
    max-width: 20ch;
    margin-bottom: 1rem;
}

.pillar-copy p:not(.tag) {
    color: var(--text-lo);
    line-height: 1.75;
    max-width: 48ch;
}

.pillar-copy p:not(.tag) + p:not(.tag) {
    margin-top: 0.9rem;
}

.pillar-copy .link-arrow {
    margin-top: 1.4rem;
}

@media (max-width: 900px) {
    .pillar {
        grid-template-columns: minmax(0, 1fr);
        gap: 1.75rem;
    }

    .pillar:nth-child(even) .pillar-media {
        order: 0;
    }
}

/* ==========================================================================
   Duo — the two sides of the record, as two large cards
   ========================================================================== */

.duo {
    position: relative;
    padding: clamp(5rem, 10vw, 8.5rem) var(--gutter);
    background: var(--ink-0);
}

.duo-inner {
    max-width: var(--shell);
    margin-inline: auto;
}

.duo-head {
    text-align: center;
    margin-bottom: clamp(2.5rem, 6vw, 4rem);
}

.duo-head .tag {
    justify-content: center;
    margin-bottom: 1.1rem;
}

.duo-head h2 {
    font-size: var(--step-3);
    margin-inline: auto;
    max-width: 26ch;
}

.duo-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(26rem, 100%), 1fr));
    gap: clamp(1rem, 2vw, 1.5rem);
}

.duo-card {
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    min-height: clamp(26rem, 44vw, 34rem);
    border-radius: 20px;
    border: 1px solid var(--line);
    overflow: hidden;
    background: #10141d;
    text-decoration: none;
    color: inherit;
}

.duo-media {
    position: absolute;
    inset: 0;
    z-index: 0;
}

.duo-media img,
.duo-media svg {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform 0.9s var(--ease);
}

/* No transform transition on hover: the pointer tilt (script) owns this
   element's transform, so lift is carried by the shadow and the edge. */
.duo-card {
    box-shadow: inset 0 1px 0 rgba(238, 242, 245, 0.14), var(--elev-rest);
    transition: border-color 0.35s var(--ease-io), box-shadow 0.35s var(--ease-io);
}

.duo-card:hover {
    border-color: var(--signal-edge);
    box-shadow: inset 0 1px 0 rgba(238, 242, 245, 0.2), var(--elev-high);
}

.duo-card:hover .duo-media img,
.duo-card:hover .duo-media svg {
    transform: scale(1.045);
}

/*
 * Until the photography lands, each card carries its own weather — a lit
 * gradient and a ghost of its run code. Swapped out automatically the moment
 * a file appears in images/gen/.
 */
.duo-fallback {
    position: absolute;
    inset: 0;
}

.duo-fallback::after {
    content: '[ ' attr(data-code) ' ]';
    position: absolute;
    top: 44%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-family: var(--mono);
    font-size: clamp(4rem, 9vw, 7.5rem);
    font-weight: 500;
    letter-spacing: 0.06em;
    color: rgba(238, 242, 245, 0.055);
    white-space: nowrap;
}

.duo-fallback--ep {
    background:
        radial-gradient(ellipse 90% 70% at 82% 0%, rgba(98, 144, 204, 0.24), transparent 60%),
        radial-gradient(ellipse 80% 60% at 10% 100%, rgba(230, 51, 41, 0.16), transparent 62%),
        linear-gradient(170deg, #131a29, #0f0f15 70%);
}

.duo-fallback--sup {
    background:
        radial-gradient(ellipse 90% 70% at 16% 0%, rgba(98, 144, 204, 0.18), transparent 60%),
        radial-gradient(ellipse 85% 65% at 92% 96%, rgba(230, 51, 41, 0.12), transparent 62%),
        linear-gradient(190deg, #141b2b, #0f0f15 72%);
}

/* Legibility floor for the caption: the bottom of the photograph frosts as it
   approaches the text, so the caption sits on glass instead of on a shadow.
   The mask fades the blur in, and the gradient still guarantees contrast. */
.duo-card::after {
    content: '';
    position: absolute;
    inset: 40% 0 0;
    z-index: 1;
    background: linear-gradient(180deg, transparent, rgba(10, 14, 22, 0.82));
    -webkit-backdrop-filter: blur(18px) saturate(1.3);
    backdrop-filter: blur(18px) saturate(1.3);
    -webkit-mask-image: linear-gradient(180deg, transparent, black 55%);
    mask-image: linear-gradient(180deg, transparent, black 55%);
    pointer-events: none;
}

.duo-card .tag {
    position: absolute;
    top: 1.5rem;
    left: 1.6rem;
    z-index: 2;
}

.duo-caption {
    position: relative;
    z-index: 2;
    display: grid;
    gap: 0.5rem;
    padding: 2rem 2rem 1.85rem;
}

.duo-caption h3 {
    font-size: var(--step-2);
    font-weight: 600;
    letter-spacing: -0.02em;
    text-transform: uppercase;
    line-height: 0.98;
}

.duo-caption p {
    color: var(--text-lo);
    line-height: 1.6;
    max-width: 44ch;
    font-size: var(--step--1);
}

/* Side by side the two cards must agree on where the headline sits. Holding
   the paragraph to three lines does that without padding the copy out. */
@media (min-width: 900px) {
    .duo-caption p {
        min-height: calc(3 * 1.6em);
    }
}

.duo-caption .link-arrow {
    margin-top: 0.5rem;
    border-bottom: 0;
}

.duo-card:hover .link-arrow {
    gap: 1em;
}
/* ==========================================================================
   Feature panels — pinned near-black, image beside text, alternating
   ========================================================================== */

.feature {
    position: relative;
    overflow: hidden;
    background: var(--ink-0);
}

.feature--1 {
    background: var(--ink-1);
}

.feature-inner {
    display: grid;
    grid-template-columns: 1.05fr 1fr;
    align-items: center;
    gap: clamp(2.5rem, 6vw, 5.5rem);
    max-width: var(--shell);
    margin-inline: auto;
    padding: clamp(4.5rem, 9vw, 7.5rem) var(--gutter);
}

.feature--flip .feature-media {
    order: 2;
}

/* One rounded panel, media filling it — a card, not a box in a box. */
.feature-media {
    position: relative;
    display: grid;
    place-items: center;
    aspect-ratio: 4 / 3;
    padding: clamp(1.5rem, 3vw, 2.5rem);
    background:
        radial-gradient(ellipse 90% 70% at 50% 0%, rgba(98, 144, 204, 0.08), transparent 70%),
        var(--ink-2);
    border: 1px solid var(--line);
    border-radius: 18px;
    overflow: hidden;
    box-shadow: var(--elev-rest);
    transition: box-shadow 0.45s var(--ease-io);
}

.feature-media:hover {
    box-shadow: var(--elev-high);
}

.feature-media img {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 30rem;
    filter: brightness(1.35) contrast(1.05);
    transition: transform 0.7s var(--ease);
}

.feature-media:hover img {
    transform: scale(1.02);
}

/* Screen-blended logo art, not a screenshot — no frame, no brightness lift. */
.feature-media--art img {
    border: 0;
    max-width: 22rem;
    filter: none;
    mix-blend-mode: screen;
}

/* A photograph filling the whole panel — no padding, no brightness lift. */
.feature-media--photo {
    padding: 0;
}

.feature-media--photo img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    max-width: none;
    object-fit: cover;
    filter: none;
}

/*
 * Drawn instrument panels — the survey sheet and the field ticket. Framed like
 * the screenshots so the feature rhythm holds, but with no brightness lift:
 * they are already drawn in the page's own palette.
 */
.feature-art {
    position: relative;
    z-index: 1;
    display: block;
    width: 100%;
    max-width: 32rem;
    height: auto;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--ink-1);
    transition: transform 0.7s var(--ease);
}

.feature-media:hover .feature-art {
    transform: scale(1.02);
}

.feature-text {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: flex-start;
}

.feature-text .tag {
    margin-bottom: 1.4rem;
}

.feature-text h2 {
    font-size: var(--step-3);
    margin-bottom: 1.2rem;
    max-width: 24ch;
}

.feature-text p:not(.tag) {
    color: var(--text-lo);
    line-height: 1.7;
    max-width: 52ch;
}

.feature-text p:not(.tag) + p:not(.tag) {
    margin-top: 0.9rem;
}

@media (max-width: 992px) {
    .feature-inner {
        grid-template-columns: 1fr;
        min-height: 0;
    }

    .feature--flip .feature-media {
        order: 0;
    }
}

/* ==========================================================================
   Spec list — the parts manifest
   ========================================================================== */

.spec-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: 0;
    width: 100%;
    margin: 1.5rem 0;
    border-top: 1px solid var(--line);
    counter-reset: spec-counter;
}

.spec-list li {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
    padding: 0.75rem 0;
    border-bottom: 1px solid var(--line);
    font-size: var(--step--1);
    color: var(--text-lo);
    transition: color 0.2s var(--ease-io);
}

.spec-list li:hover {
    color: var(--text-hi);
}

/* Row index doubles as the item number on a manifest. */
.spec-list li::before {
    content: counter(spec-counter, decimal-leading-zero);
    counter-increment: spec-counter;
    font-family: var(--mono);
    font-size: 0.66rem;
    letter-spacing: 0.06em;
    color: var(--signal-hot);
    opacity: 0.75;
    font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   Industry plates — where the record is kept
   ========================================================================== */

.industry-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.55rem;
    margin-top: clamp(1.5rem, 3vw, 2rem);
}

.industry {
    display: inline-flex;
    align-items: center;
    gap: 0.6em;
    padding: 0.68rem 1rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    font-family: var(--mono);
    font-size: 0.7rem;
    font-weight: 450;
    letter-spacing: 0.07em;
    line-height: 1;
    text-transform: uppercase;
    color: var(--text-lo);
}

.industry .ico {
    font-size: 1.05rem;
    color: var(--text-dim);
}

/* The home vertical: red-edged, filled, and first in the row. */
.industry--home {
    border-color: var(--signal-edge);
    background: var(--signal-ink);
    color: var(--signal-text);
}

.industry--home .ico {
    color: var(--signal-text);
}

/* Not a vertical — the open end of the row. */
.industry--open {
    border-style: dashed;
    color: var(--text-dim);
}

@media (max-width: 640px) {
    .industry {
        font-size: 0.72rem;
    }
}

/* ==========================================================================
   Telemetry strip — the counting numbers, pinned near-black
   ========================================================================== */

.telemetry {
    position: relative;
    background: var(--ink-0);
    border-block: 1px solid var(--line);
    padding: clamp(4rem, 8vw, 6.5rem) var(--gutter);
}

/*
 * The strip reads as a spread: the claim on the left, the figures backing it
 * in a block on the right. Stacking them instead would leave half the row
 * empty, which is the thing that makes a page look unfinished.
 */
.telemetry > .shell {
    display: grid;
    grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
    gap: clamp(2.5rem, 6vw, 5.5rem);
    align-items: start;
}

/* The strip can carry a heading — the home page frames its numbers with one. */
.telemetry .prose-head {
    margin-bottom: 0;
}

.telemetry .prose-head h2 {
    margin-bottom: 1rem;
}

.telemetry .prose-head p:not(.tag) {
    color: var(--text-lo);
    line-height: 1.7;
    max-width: 44ch;
}

.telemetry .prose-head a {
    color: var(--text-hi);
    text-decoration-color: var(--signal);
    text-underline-offset: 3px;
}

/* Two by two: the counted numbers on the top row, the stated facts below.
   Dropped by the height of a label line so the first figure starts level with
   the headline rather than with the label above it. */
.telemetry-inner {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: clamp(2rem, 4vw, 3.25rem) clamp(1.5rem, 3vw, 2.5rem);
    padding-top: 2.1rem;
}

@media (max-width: 900px) {
    .telemetry > .shell {
        grid-template-columns: minmax(0, 1fr);
        gap: clamp(2.5rem, 6vw, 3.5rem);
    }

    .telemetry .prose-head p:not(.tag) {
        max-width: 62ch;
    }

    .telemetry-inner {
        padding-top: 0;
    }
}

/* Two columns at phone width leaves the labels wrapping to three lines, which
   reads worse than a taller single column. */
@media (max-width: 600px) {
    .telemetry-inner {
        grid-template-columns: minmax(0, 1fr);
        gap: 1.75rem;
    }
}

.telemetry-item {
    position: relative;
    padding-left: 1.25rem;
    border-left: 1px solid var(--line-strong);
}

/* A tick of signal draws down the rail as each figure reports in. */
.telemetry-item::before {
    content: '';
    position: absolute;
    top: 0;
    left: -1px;
    width: 2px;
    height: 2.2rem;
    background: var(--signal);
    transform: scaleY(0);
    transform-origin: top;
    transition: transform 0.7s var(--ease) calc(var(--reveal-delay, 0ms) + 250ms);
}

.js .telemetry.is-revealed .telemetry-item::before {
    transform: scaleY(1);
}

.telemetry-value {
    display: block;
    font-family: var(--display);
    font-size: clamp(2.1rem, 3.2vw, 3.2rem);
    font-weight: 600;
    line-height: 1;
    letter-spacing: -0.03em;
    color: var(--text-hi);
    font-variant-numeric: tabular-nums;
    margin-bottom: 0.7rem;
    transform-origin: left bottom;
}

/* The gauge settles with a breath once its count lands (class set by script). */
.telemetry-value.is-done {
    animation: count-settle 0.55s var(--ease);
}

@keyframes count-settle {
    0% { transform: scale(1); }
    35% { transform: scale(1.05); color: var(--signal-hot); }
    100% { transform: scale(1); }
}

/* Words, not a reading — set smaller so the numeric gauges stay dominant. */
.telemetry-value--words {
    font-size: clamp(1.55rem, 2.3vw, 2.3rem);
    letter-spacing: -0.02em;
}

.telemetry-label {
    display: block;
    font-family: var(--mono);
    font-size: 0.68rem;
    font-weight: 450;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.telemetry-label::before { content: '[ '; color: var(--text-chrome); }
.telemetry-label::after { content: ' ]'; color: var(--text-chrome); }

/* ==========================================================================
   Module datasheet — Products and Suppliers capabilities
   ========================================================================== */

/*
 * One panel, not six: a single framed surface divided by internal hairlines.
 * The 1px grid gap over a hairline background IS the divider, so no cell ever
 * carries its own border.
 */
.module-index {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1px;
    background: var(--line);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
}

.module {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    padding: clamp(1.9rem, 3vw, 2.75rem);
    background: var(--ink-1);
    transition: background 0.25s var(--ease-io), transform 0.35s var(--ease),
                box-shadow 0.35s var(--ease-io);
}

/* The cell steps out of the sheet — it can, because the hairline grid behind
   it stays put and the risen cell casts back onto it. */
.module:hover {
    background: var(--ink-2);
    transform: translateY(-3px);
    box-shadow: var(--elev-rest);
    z-index: 1;
}

/* The red annotation tick: parked at the left of the cell's top edge, running
   the full width on hover. */
.module::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    height: 2px;
    width: 2.25rem;
    background: var(--signal);
    opacity: 0.65;
    transition: width 0.5s var(--ease), opacity 0.3s var(--ease-io);
}

.module:hover::before {
    width: 100%;
    opacity: 1;
}

.module-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    margin-bottom: 1.4rem;
}

.module-no {
    font-family: var(--mono);
    font-size: 0.68rem;
    font-weight: 450;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-dim);
    font-variant-numeric: tabular-nums;
}

.module-no::before { content: '[ '; color: var(--text-chrome); }
.module-no::after { content: ' ]'; color: var(--text-chrome); }

/* Bare glyph, no plate — the panel supplies the containment. */
.module-head .ico {
    font-size: 1.7rem;
    color: var(--text-dim);
    transition: color 0.3s var(--ease-io);
}

.module:hover .module-head .ico {
    color: var(--signal-hot);
}

.module h3 {
    font-size: var(--step-1);
    font-weight: 550;
    letter-spacing: -0.01em;
    margin-bottom: 0.6rem;
}

.module p:not(.tag) {
    font-size: var(--step--1);
    color: var(--text-lo);
    line-height: 1.7;
    max-width: 36ch;
}

.module .link-arrow {
    margin-top: auto;
    padding-top: 1.35rem;
}

@media (max-width: 1040px) {
    .module-index {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 640px) {
    .module-index {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* Careers openings — a list of roles, each its own row */
.role-list {
    border-top: 1px solid var(--line);
}

.role {
    position: relative;
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 1.35rem;
    padding: 1.6rem clamp(0.5rem, 2vw, 1.5rem);
    border-bottom: 1px solid var(--line);
    transition: background 0.22s var(--ease-io);
}

.role::before {
    content: '';
    position: absolute;
    inset: 0 auto 0 0;
    width: 2px;
    background: var(--signal);
    transform: scaleY(0);
    transition: transform 0.3s var(--ease);
}

.role:hover {
    background: var(--ink-1);
}

.role:hover::before {
    transform: scaleY(1);
}

.role-icon {
    display: grid;
    place-items: center;
    width: 2.6rem;
    height: 2.6rem;
    border: 1px solid var(--signal-edge);
    border-radius: var(--radius-sm);
    background: var(--signal-ink);
    color: var(--signal-hot);
    font-size: 1.25rem;
}

.role h3 {
    font-size: var(--step-0);
    font-weight: 550;
    margin-bottom: 0.2rem;
}

.role p:not(.tag) {
    font-size: var(--step--1);
    color: var(--text-lo);
    line-height: 1.7;
    max-width: 62ch;
}

.role-code {
    font-family: var(--mono);
    font-size: 0.66rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-dim);
    white-space: nowrap;
}

@media (max-width: 700px) {
    .role {
        grid-template-columns: auto 1fr;
        gap: 1rem;
    }

    .role-code {
        display: none;
    }
}

/* ==========================================================================
   Prose blocks
   ========================================================================== */

.split {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(24rem, 100%), 1fr));
    gap: clamp(2.5rem, 6vw, 5rem);
}

/*
 * A section laid out as a spread: the heading holds a left rail and stays put
 * while the list runs past it. Used wherever a single narrow reading column
 * would otherwise leave a third of the row empty for a dozen rows running —
 * the FAQ and the openings list are both that shape.
 */
.spread {
    display: grid;
    grid-template-columns: minmax(0, 0.78fr) minmax(0, 1.22fr);
    gap: clamp(2.5rem, 6vw, 5.5rem);
    align-items: start;
}

.spread > .prose-head {
    position: sticky;
    top: calc(var(--nav-h) + clamp(1.5rem, 4vh, 3rem));
    margin-bottom: 0;
}

.spread > .prose-head h2 {
    max-width: 21ch;
}

@media (max-width: 900px) {
    .spread {
        grid-template-columns: minmax(0, 1fr);
        gap: clamp(2rem, 5vw, 3rem);
    }

    .spread > .prose-head {
        position: static;
    }

    .spread > .prose-head h2 {
        max-width: 26ch;
    }
}

.prose-head {
    margin-bottom: clamp(2rem, 4.5vw, 3rem);
}

.prose-head .tag {
    margin-bottom: 1.1rem;
}

.prose-head h2 {
    font-size: var(--step-3);
    max-width: 26ch;
}

/* A lede under a masthead needs air; caps set at 0.96 leading do not give it
   any of their own. */
.prose-head h2 + p:not(.tag) {
    margin-top: 1.1rem;
}

/* A masthead: the rule runs the full measure, so a heading that only occupies
   the left of the row reads as deliberate rather than as an orphan. */
.prose-head--rule {
    padding-bottom: clamp(1.5rem, 3vw, 2.25rem);
    border-bottom: 1px solid var(--line);
}

.prose-head--center {
    text-align: center;
}

.prose-head--center .tag,
.prose-head--center h2 {
    margin-inline: auto;
}

/* A centred head has to sit on the centre of the page, not on the centre of a
   reading column that deliberately hugs the left margin. */
.shell--narrow > .prose-head--center {
    margin-inline: auto;
}

.prose .tag {
    margin-bottom: 1.1rem;
}

.prose h2,
.prose h3 {
    font-size: var(--step-2);
    margin-bottom: 1rem;
}

.prose h3 {
    text-transform: none;
    font-weight: 550;
    letter-spacing: -0.015em;
}

.prose p:not(.tag) {
    color: var(--text-lo);
    line-height: 1.75;
    max-width: 62ch;
}

.prose p:not(.tag) + p:not(.tag) {
    margin-top: 1rem;
}

/* A long document of headed sections (the privacy policy). The reset leaves
   headings no top margin, so without this each one sits hard against the
   paragraph above it. */
.prose--sections > * + h2 {
    margin-top: clamp(2.5rem, 5vw, 3.5rem);
}

.prose a {
    color: var(--text-hi);
    text-decoration-color: var(--signal);
    text-underline-offset: 3px;
}

/* Partner logos */
.partner-row {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: clamp(2rem, 6vw, 5rem);
    margin-top: clamp(2.5rem, 5vw, 3.5rem);
    padding-top: clamp(2rem, 4vw, 3rem);
    border-top: 1px solid var(--line);
}

.partner {
    display: grid;
    justify-items: center;
    gap: 0.75rem;
}

.partner img {
    height: 3rem;
    width: auto;
    object-fit: contain;
    opacity: 0.75;
    filter: grayscale(0.35);
    transition: opacity 0.3s var(--ease-io), filter 0.3s var(--ease-io);
}

.partner:hover img {
    opacity: 1;
    filter: none;
}

.partner span {
    font-family: var(--mono);
    font-size: 0.66rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-dim);
}

/* The industries coda under a split — spaced off the columns above it, and set
   as a spread so the plates read as a key beside the claim they qualify. */
.whereruns {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, auto);
    gap: clamp(2rem, 5vw, 4.5rem);
    align-items: start;
    margin-top: clamp(3rem, 6vw, 4.5rem);
    padding-top: clamp(2rem, 4vw, 3rem);
    border-top: 1px solid var(--line);
}

.whereruns .tag {
    margin-bottom: 1rem;
}

.whereruns p:not(.tag) {
    margin-top: 0.8rem;
}

/* Stacked and flush right, so the three plates line up as one column. */
.whereruns .industry-row {
    flex-direction: column;
    align-items: stretch;
    margin-top: 0;
    min-width: 17rem;
}

@media (max-width: 860px) {
    .whereruns {
        grid-template-columns: minmax(0, 1fr);
        gap: clamp(1.75rem, 4vw, 2.25rem);
    }

    .whereruns .industry-row {
        flex-direction: row;
        flex-wrap: wrap;
        align-items: center;
        min-width: 0;
    }
}

/* Pull-quote / bottom line */
.callout {
    display: flex;
    gap: 1.25rem;
    margin-top: 1.75rem;
    padding: 1.35rem 0 1.35rem 1.25rem;
    border-left: 2px solid var(--signal);
}

.callout p {
    color: var(--text);
    line-height: 1.7;
    max-width: 52ch;
}

.callout strong {
    color: var(--signal-hot);
    font-weight: 550;
}

/* ==========================================================================
   Customer logo wall
   ========================================================================== */

.logo-wall-head {
    margin-bottom: clamp(1.75rem, 4vw, 2.5rem);
}

.logo-wall-head .tag {
    margin-bottom: 1rem;
}

.logo-wall-head h3 {
    font-size: var(--step-2);
    font-weight: 600;
    letter-spacing: -0.02em;
    max-width: 34ch;
}

.logo-wall-count {
    margin-top: 0.6rem;
    font-family: var(--mono);
    font-size: 0.68rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.logo-wall-count span {
    color: var(--signal-hot);
    font-weight: 500;
    font-variant-numeric: tabular-nums;
}

/* The hot red does not carry on off-white at label size — the count reads as
   data there instead of as an accent. */
.band--light .logo-wall-count span {
    color: var(--text-hi);
    font-weight: 600;
}

.logo-wall {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
    gap: clamp(0.5rem, 1.2vw, 0.85rem);
}

/*
 * Each mark keeps its own colours. These are other companies' trademarks —
 * inverting or greyscaling them to suit a dark page would alter artwork we do
 * not own. Instead the wall sits on the off-white proof band, where the white
 * background baked into every file all but disappears: no borders, no glare,
 * no 200 stickers. The plate only asserts itself under the pointer.
 */
.logo-tile {
    display: grid;
    place-items: center;
    aspect-ratio: 224 / 96;
    padding: 0.4rem 0.75rem;
    background: #fff;
    border-radius: 4px;
    transition: transform 0.3s var(--ease), box-shadow 0.3s var(--ease-io);
}

.logo-tile:hover {
    transform: translateY(-3px);
    box-shadow: 0 0 0 1px rgba(28, 43, 68, 0.08), 0 10px 24px -8px rgba(22, 35, 58, 0.28);
}

.logo-tile img {
    width: 100%;
    height: auto;
}

@media (max-width: 560px) {
    .logo-wall {
        grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr));
    }
}

/*
 * Home strip — a sample of the wall rather than all of it. The homepage needs
 * the marks to register in one band and then get out of the way, so the count
 * and the link carry the rest.
 */
.logo-strip-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.6rem 1.5rem;
    margin-bottom: clamp(1.25rem, 3vw, 1.75rem);
}

.logo-strip-head .link-arrow {
    margin-top: 0;
}

.logo-strip {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
    gap: clamp(0.5rem, 1.2vw, 0.85rem);
}

/* Fewer, larger tiles on a phone. */
@media (max-width: 700px) {
    .logo-strip {
        grid-template-columns: repeat(auto-fill, minmax(6.75rem, 1fr));
    }

    .logo-strip .logo-tile:nth-child(n + 13) {
        display: none;
    }
}

/* Two columns at phone width — twelve tiles would run six rows deep. */
@media (max-width: 480px) {
    .logo-strip .logo-tile:nth-child(n + 9) {
        display: none;
    }
}

/* ==========================================================================
   FAQ — the questions that arrive before the demo does
   ========================================================================== */

/*
 * Native <details> rather than scripted panels: every answer is readable with
 * no JavaScript, and the browser handles the keyboard and the screen-reader
 * announcement for us.
 */
.faq {
    border-top: 1px solid var(--line);
    counter-reset: faq-counter;
    interpolate-size: allow-keywords;
}

/* Answers glide open where the browser can animate to auto height. */
.faq-item::details-content {
    block-size: 0;
    overflow: clip;
    transition: block-size 0.4s var(--ease), content-visibility 0.4s allow-discrete;
}

.faq-item[open]::details-content {
    block-size: auto;
}

.faq-item {
    border-bottom: 1px solid var(--line);
}

.faq-item summary {
    position: relative;
    display: flex;
    align-items: baseline;
    gap: 1.1rem;
    padding: 1.5rem 2.75rem 1.5rem 0;
    cursor: pointer;
    list-style: none;
    font-size: var(--step-1);
    font-weight: 500;
    letter-spacing: -0.01em;
    color: var(--text);
    transition: color 0.2s var(--ease-io);
}

/* Safari still draws its own triangle without this. */
.faq-item summary::-webkit-details-marker {
    display: none;
}

.faq-item summary:hover {
    color: var(--text-hi);
}

.faq-item summary:hover::before {
    color: var(--signal-hot);
}

.faq-item summary::before {
    content: '[ ' counter(faq-counter, decimal-leading-zero) ' ]';
    counter-increment: faq-counter;
    flex: none;
    font-family: var(--mono);
    font-size: 0.68rem;
    font-weight: 450;
    letter-spacing: 0.06em;
    color: var(--text-dim);
    font-variant-numeric: tabular-nums;
}

/*
 * Open/closed is a drawn cross: two hairlines making a plus that turns an
 * eighth of a turn into an x. Centred off the box, so it stays put when a
 * long question wraps to two lines.
 */
.faq-item summary::after {
    content: '';
    position: absolute;
    top: 50%;
    right: 0.25rem;
    width: 12px;
    height: 12px;
    background:
        linear-gradient(var(--signal-hot), var(--signal-hot)) center / 100% 1px no-repeat,
        linear-gradient(var(--signal-hot), var(--signal-hot)) center / 1px 100% no-repeat;
    transform: translateY(-50%);
    transition: transform 0.3s var(--ease);
}

.faq-item[open] summary::after {
    transform: translateY(-50%) rotate(45deg);
}

/* Indented to clear the row number, so the answer hangs off the question. */
.faq-answer {
    padding: 0 2.75rem 1.7rem 3.4rem;
    color: var(--text-lo);
    line-height: 1.75;
    max-width: 72ch;
}

/* The words surface a half-beat behind the panel that reveals them. */
.faq-item[open] .faq-answer {
    animation: faq-surface 0.5s var(--ease) both;
}

@keyframes faq-surface {
    from {
        opacity: 0;
        transform: translateY(8px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

.faq-answer p + p {
    margin-top: 0.9rem;
}

.faq-answer a {
    color: var(--text-hi);
    text-decoration-color: var(--signal);
    text-underline-offset: 3px;
}

@media (max-width: 640px) {
    .faq-answer {
        padding-left: 0;
    }
}

/* ==========================================================================
   Closing call to action — pinned near-black
   ========================================================================== */

.closer {
    position: relative;
    padding: clamp(5.5rem, 12vw, 10rem) var(--gutter);
    background: var(--ink-0);
    overflow: hidden;
}

/* Optional photograph behind the ask, held well back. */
.closer-media {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0.38;
}

.closer-scrim {
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(15, 15, 21, 0.82), rgba(15, 15, 21, 0.55) 50%, rgba(15, 15, 21, 0.85));
    pointer-events: none;
}

.closer::before {
    content: '';
    position: absolute;
    inset: 0;
    background:
        radial-gradient(circle 700px at 50% 40%, rgba(230, 51, 41, 0.08), transparent 65%),
        radial-gradient(circle 900px at 80% 110%, rgba(98, 144, 204, 0.08), transparent 60%);
    pointer-events: none;
}

/* The ask is made through glass: the aurora and the photograph drift behind
   a frosted plate, and the plate is what holds still. */
.closer-inner {
    position: relative;
    max-width: 56rem;
    margin-inline: auto;
    padding: clamp(2.5rem, 6vw, 4.5rem) clamp(1.75rem, 5vw, 4rem);
    text-align: center;
    background: var(--glass-fill);
    border: 1px solid var(--glass-edge);
    border-radius: 22px;
    -webkit-backdrop-filter: blur(26px) saturate(1.5);
    backdrop-filter: blur(26px) saturate(1.5);
    box-shadow: inset 0 1px 0 var(--glass-specular),
                inset 0 -1px 0 rgba(238, 242, 245, 0.04),
                var(--glass-shadow);
}

.closer .tag {
    justify-content: center;
    margin-bottom: 1.5rem;
}

.closer h2 {
    font-size: var(--step-4);
    margin-bottom: 1.2rem;
}

.closer p:not(.tag) {
    font-size: var(--step-1);
    color: var(--text-lo);
    line-height: 1.6;
    margin-bottom: clamp(2rem, 4vw, 2.75rem);
    text-wrap: pretty;
}

.closer .btn {
    padding-inline: 2.4rem;
}

/* ==========================================================================
   Forms
   ========================================================================== */

.form-layout {
    display: grid;
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
    gap: clamp(2.5rem, 6vw, 5rem);
    align-items: start;
}

@media (max-width: 900px) {
    .form-layout {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* The form sits on glass over the band's own light. The band supplies two
   soft colour masses underneath (see .form-layout::before) so the frost has
   something to catch — glass over a flat fill is just grey. */
.form-panel {
    position: relative;
    padding: clamp(1.75rem, 4vw, 2.75rem);
    background: var(--glass-fill);
    border: 1px solid var(--glass-edge);
    border-radius: 14px;
    -webkit-backdrop-filter: blur(22px) saturate(1.4);
    backdrop-filter: blur(22px) saturate(1.4);
    box-shadow: inset 0 1px 0 var(--glass-specular), var(--glass-shadow);
}

/* Every shell that carries a form panel, on every page that has one. Browsers
   without :has() simply skip the garnish. */
.shell:has(.form-panel) {
    position: relative;
}

.shell:has(.form-panel)::before {
    content: '';
    position: absolute;
    inset: -8% -4%;
    z-index: -1;
    background:
        radial-gradient(ellipse 460px 340px at 74% 18%, rgba(98, 144, 204, 0.24), transparent 68%),
        radial-gradient(ellipse 400px 320px at 90% 82%, rgba(230, 51, 41, 0.13), transparent 66%);
    pointer-events: none;
}

/*
 * Registration marks — the crop ticks a printer leaves outside a plate. Drawn
 * just off the corners of the page's framed exhibits, they tie the panels to
 * the engineering-sheet language the labels already speak.
 */
.moment-frame::before,
.form-panel::before {
    content: '';
    position: absolute;
    inset: -11px;
    pointer-events: none;
    background:
        linear-gradient(var(--text-chrome), var(--text-chrome)) 0 0 / 15px 1px,
        linear-gradient(var(--text-chrome), var(--text-chrome)) 0 0 / 1px 15px,
        linear-gradient(var(--text-chrome), var(--text-chrome)) 100% 0 / 15px 1px,
        linear-gradient(var(--text-chrome), var(--text-chrome)) 100% 0 / 1px 15px,
        linear-gradient(var(--text-chrome), var(--text-chrome)) 0 100% / 15px 1px,
        linear-gradient(var(--text-chrome), var(--text-chrome)) 0 100% / 1px 15px,
        linear-gradient(var(--text-chrome), var(--text-chrome)) 100% 100% / 15px 1px,
        linear-gradient(var(--text-chrome), var(--text-chrome)) 100% 100% / 1px 15px;
    background-repeat: no-repeat;
}

.form-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
    gap: 1.1rem;
}

.field {
    margin-bottom: 1.15rem;
}

.field label {
    display: block;
    margin-bottom: 0.45rem;
    font-family: var(--mono);
    font-size: 0.66rem;
    font-weight: 450;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-lo);
}

.field-req {
    color: var(--signal-text);
}

.input {
    display: block;
    width: 100%;
    padding: 0.85rem 1rem;
    background: var(--ink-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    color: var(--text-hi);
    font-family: var(--display);
    /* Never below 16px: iOS Safari zooms the whole page when it focuses a
       smaller control, and the visitor has to pinch back out afterwards. */
    font-size: max(16px, var(--step-0));
    line-height: 1.5;
    transition: border-color 0.2s var(--ease-io), background 0.2s var(--ease-io),
                box-shadow 0.2s var(--ease-io);
}

.input::placeholder {
    color: var(--text-dim);
}

.input:hover {
    border-color: var(--line-strong);
}

.input:focus {
    outline: none;
    border-color: var(--signal);
    box-shadow: 0 0 0 3px var(--signal-ink);
}

.input[aria-invalid='true'] {
    border-color: #ff6b60;
}

textarea.input {
    resize: vertical;
    min-height: 7rem;
}

/* Which product the demo is for: two plates that behave like radio buttons and
   sit in the same register as the text inputs beside them. */
.choice-field {
    min-width: 0;
    margin: 0 0 1.15rem;
    padding: 0;
    border: 0;
}

.choice-field legend {
    display: block;
    margin-bottom: 0.45rem;
    padding: 0;
    font-family: var(--mono);
    font-size: 0.66rem;
    font-weight: 450;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-lo);
}

.choice-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(12rem, 100%), 1fr));
    gap: 0.75rem;
}

.field .choice {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    margin: 0;
    padding: 0.85rem 1rem;
    background: var(--ink-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    color: var(--text-hi);
    font-family: var(--display);
    font-size: max(16px, var(--step-0));
    font-weight: 400;
    letter-spacing: 0;
    text-transform: none;
    line-height: 1.4;
    cursor: pointer;
    transition: border-color 0.2s var(--ease-io), box-shadow 0.2s var(--ease-io);
}

.field .choice:hover {
    border-color: var(--line-strong);
}

.field .choice:has(input:checked) {
    border-color: var(--signal);
    box-shadow: 0 0 0 3px var(--signal-ink);
}

.field .choice:has(input:focus-visible) {
    outline: 2px solid var(--signal);
    outline-offset: 2px;
}

.choice input {
    flex: none;
    margin: 0.25rem 0 0;
    accent-color: var(--signal);
}

.choice > span {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}

.choice small {
    font-size: 0.82em;
    color: var(--text-lo);
}

.field-error {
    display: block;
    min-height: 1.1rem;
    margin-top: 0.35rem;
    font-family: var(--mono);
    font-size: 0.68rem;
    letter-spacing: 0.03em;
    color: #c22318;
}

.field-error:empty {
    min-height: 0;
}

/* Honeypot. Off-screen rather than display:none so bots that skip hidden
   inputs still fill it in. */
.field-trap {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

.form-alert {
    display: flex;
    gap: 0.8rem;
    margin-bottom: 1.5rem;
    padding: 0.95rem 1.1rem;
    border: 1px solid rgba(255, 107, 96, 0.4);
    border-radius: var(--radius);
    background: rgba(255, 107, 96, 0.08);
    color: #a91d13;
    font-size: var(--step--1);
    line-height: 1.6;
}

.form-alert ul {
    display: grid;
    gap: 0.3rem;
}

.file-drop {
    position: relative;
    display: grid;
    place-items: center;
    gap: 0.5rem;
    padding: 1.75rem 1.25rem;
    border: 1px dashed var(--line-strong);
    border-radius: var(--radius);
    background: var(--ink-2);
    text-align: center;
    cursor: pointer;
    transition: border-color 0.25s var(--ease-io), background 0.25s var(--ease-io);
}

.file-drop:hover,
.file-drop:focus-within {
    border-color: var(--signal-edge);
    background: var(--signal-ink);
}

.file-drop .ico {
    font-size: 1.5rem;
    color: var(--text-dim);
    transition: color 0.25s var(--ease-io);
}

.file-drop:hover .ico,
.file-drop.has-file .ico {
    color: var(--signal-hot);
}

.file-drop-text {
    font-family: var(--mono);
    font-size: 0.7rem;
    letter-spacing: 0.05em;
    color: var(--text-lo);
    word-break: break-all;
}

.file-drop-hint {
    font-family: var(--mono);
    font-size: 0.62rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.file-drop.has-file {
    border-style: solid;
    border-color: var(--signal-edge);
}

.file-drop input[type='file'] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
}

/* Confirmation panel after a successful post-redirect-get. */
.form-done {
    display: grid;
    justify-items: start;
    gap: 1rem;
    padding: clamp(1.75rem, 4vw, 2.75rem);
    border: 1px solid rgba(53, 196, 106, 0.35);
    border-radius: var(--radius);
    background: rgba(53, 196, 106, 0.06);
}

.form-done .ico {
    font-size: 2rem;
    color: var(--lamp-nominal);
}

.form-done h2 {
    font-size: var(--step-2);
}

.form-done p {
    color: var(--text);
    line-height: 1.7;
    max-width: 46ch;
}

/* Contact details list */
.detail-list {
    display: grid;
    border-top: 1px solid var(--line);
}

.detail {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: 1rem;
    padding: 1.1rem 0;
    border-bottom: 1px solid var(--line);
}

.detail .ico {
    font-size: 1.35rem;
    color: var(--signal);
}

.detail dt {
    font-family: var(--mono);
    font-size: 0.64rem;
    font-weight: 450;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--text-dim);
    grid-column: 2;
}

.detail dd {
    grid-column: 2;
    color: var(--text-hi);
    font-size: var(--step-0);
}

.detail dd a {
    color: inherit;
    text-decoration: none;
    border-bottom: 1px solid transparent;
    transition: border-color 0.2s var(--ease-io);
}

.detail dd a:hover {
    border-bottom-color: var(--signal);
}

/* Checklist of benefits */
.check-list {
    display: grid;
    gap: 0.8rem;
    margin-top: 1.75rem;
}

.check-list li {
    display: flex;
    align-items: center;
    gap: 0.8rem;
    font-size: var(--step--1);
    color: var(--text-lo);
}

.check-list .ico {
    font-size: 1.1rem;
    color: var(--signal);
}

/* ==========================================================================
   Error page
   ========================================================================== */

.fault {
    display: grid;
    place-items: center;
    min-height: 78vh;
    padding: calc(var(--nav-h) + 4rem) var(--gutter) 5rem;
    text-align: center;
}

.fault-inner {
    max-width: 34rem;
}

.fault-code {
    font-family: var(--mono);
    font-size: clamp(4rem, 16vw, 8rem);
    font-weight: 600;
    line-height: 1;
    letter-spacing: -0.04em;
    color: var(--signal);
    font-variant-numeric: tabular-nums;
    margin-bottom: 1rem;
}

/* The dry-hole survey sheet under the 404. */
.fault-art {
    display: block;
    width: min(100%, 26rem);
    height: auto;
    margin: 0 auto 1.75rem;
}

.fault h1 {
    font-size: var(--step-3);
    margin-bottom: 0.85rem;
}

/* :not() so the status code keeps its own colour — a bare `.fault p` outranks
   the single-class rule on .fault-code and would grey it out. */
.fault p:not(.fault-code):not(.fault-trace) {
    color: var(--text-lo);
    line-height: 1.75;
    margin-bottom: 2rem;
}

.fault-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.85rem;
}

.fault-trace {
    margin-top: 2.5rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--line);
    font-family: var(--mono);
    font-size: 0.66rem;
    letter-spacing: 0.05em;
    color: var(--text-dim);
    word-break: break-all;
}

/* ==========================================================================
   Footer — pinned near-black, ends on the wordmark
   ========================================================================== */

.site-footer {
    position: relative;
    background: var(--ink-0);
    border-top: 1px solid var(--line);
    overflow: hidden;
}

.footer-main {
    max-width: var(--shell);
    margin-inline: auto;
    padding: clamp(3.5rem, 7vw, 5.5rem) var(--gutter) clamp(2.5rem, 5vw, 3.5rem);
}

.footer-grid {
    display: grid;
    grid-template-columns: 1.6fr 1fr 1fr 1.2fr;
    gap: clamp(2rem, 4vw, 3.5rem);
}

.footer-logo-img {
    height: 2.1rem;
    width: auto;
    object-fit: contain;
    margin-bottom: 1.25rem;
}

.footer-brand p {
    font-size: var(--step--1);
    line-height: 1.8;
    color: var(--text-dim);
    max-width: 34ch;
}

.footer-stamp {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem 1.4rem;
    margin-top: 1.25rem;
    font-family: var(--mono);
    font-size: 0.64rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.footer-stamp span {
    white-space: nowrap;
}

.footer-heading {
    margin-bottom: 1.1rem;
    font-family: var(--mono);
    font-size: 0.64rem;
    font-weight: 450;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-lo);
}

.footer-heading::before { content: '[ '; color: var(--text-chrome); }
.footer-heading::after { content: ' ]'; color: var(--text-chrome); }

.footer-heading--spaced {
    margin-top: 1.75rem;
}

.footer-links,
.footer-contact {
    display: grid;
    gap: 0.55rem;
}

.footer-links a,
.footer-contact a {
    font-size: var(--step--1);
    color: var(--text-dim);
    text-decoration: none;
    transition: color 0.2s var(--ease-io);
}

.footer-links a:hover,
.footer-contact a:hover {
    color: var(--text-hi);
}

.footer-contact li {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    color: var(--text-dim);
}

.footer-contact .ico {
    font-size: 1.05rem;
    color: var(--signal-edge);
}

.footer-apps {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem 0.6rem;
}

/*
 * The official store badges, self-hosted. Both vendors supply dark variants
 * with their own field and border, which is why they need no framing from us.
 * Google's badge ships with ~10% internal padding, so it is set slightly
 * taller to match Apple's optical size.
 */
.app-badge {
    display: inline-flex;
    align-items: center;
    text-decoration: none;
    opacity: 0.82;
    transition: opacity 0.2s var(--ease-io);
}

.app-badge:hover,
.app-badge:focus-visible {
    opacity: 1;
}

.app-badge img {
    height: 2.5rem;
    width: auto;
}

.app-badge--play img {
    height: 2.95rem;
}

.social-link {
    display: grid;
    place-items: center;
    width: 2.5rem;
    height: 2.5rem;
    margin-top: 1.5rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    color: var(--text-lo);
    font-size: 1.2rem;
    transition: border-color 0.22s var(--ease-io), color 0.22s var(--ease-io),
                background 0.22s var(--ease-io);
}

.social-link:hover {
    border-color: var(--signal-edge);
    background: var(--signal-ink);
    color: var(--signal-hot);
}

/*
 * The site signs off on the wordmark, set as large as the shell allows. "we"
 * carries the brand red, the rest reads off-white — the same split the product
 * logo makes.
 */
.footer-word {
    max-width: var(--shell);
    margin-inline: auto;
    padding: clamp(2rem, 5vw, 3.5rem) var(--gutter) 0;
}

/* The sign-off is a statement, not a logo: the site's shortest line, set
   large, with the last word in brand red the way the hero ignites. */
.footer-word-line {
    max-width: 14ch;
    font-size: clamp(2.6rem, 8.5vw, 7.5rem);
    font-weight: 650;
    line-height: 0.92;
    letter-spacing: -0.035em;
    text-transform: uppercase;
    color: var(--text-hi);
    text-wrap: balance;
    user-select: none;
}

.footer-word-hot {
    color: var(--signal-hot);
    text-shadow: 0 0 0.32em rgba(230, 51, 41, 0.28);
}

/* Beneath the letters: the two products and where the phone rings, set on one
   hairline so the sign-off reads as a plate rather than a stray word. */
.footer-word-cap {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1.75rem;
    margin-top: clamp(1rem, 2vw, 1.5rem);
    padding-top: 1rem;
    border-top: 1px solid var(--line);
    font-family: var(--mono);
    font-size: 0.62rem;
    font-weight: 450;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.footer-word-cap span::before { content: '[ '; color: var(--text-chrome); }
.footer-word-cap span::after { content: ' ]'; color: var(--text-chrome); }

@media (prefers-reduced-motion: reduce) {
    .footer-word-line {
        animation: none;
    }
}

.footer-bottom {
    max-width: var(--shell);
    margin-inline: auto;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 1.5rem var(--gutter) 1.75rem;
}

.footer-bottom p {
    font-family: var(--mono);
    font-size: 0.66rem;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--text-dim);
}

@media (max-width: 900px) {
    .footer-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 560px) {
    .footer-grid {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* ==========================================================================
   Mobile ergonomics — touch targets and legibility floors
   ========================================================================== */

/* The honeypot is never seen, but give it the same 16px so a bot-triggered or
   accessibility-tool focus can't zoom the page either. */
.field-trap input {
    font-size: 16px;
}

@media (pointer: coarse), (max-width: 992px) {
    /* Footer link rows are far under the ~44px a thumb needs. Padding carries
       the hit area; the negative margin keeps the visual rhythm. */
    .footer-links a,
    .footer-contact a {
        display: inline-block;
        padding-block: 0.65rem;
        margin-block: -0.35rem;
    }

    .link-arrow {
        padding-block: 0.8rem;
    }

    .detail dd a {
        display: inline-block;
        padding-block: 0.55rem;
        margin-block: -0.3rem;
    }

    /* The brand mark is the way back to the front page, and two lines of small
       type only came to 22px of thumb. Padding brings it to 44 without moving
       the wordmark, which is optically aligned against the nav's own height. */
    .nav-brand {
        padding-block: 0.7rem;
        margin-block: -0.7rem;
    }
}

@media (max-width: 640px) {
    /* Photo headers were over half the screen on a phone — bring the title up. */
    .log-header--photo {
        min-height: clamp(280px, 44vh, 400px);
        padding-top: calc(var(--nav-h) + 2.5rem);
        padding-bottom: 2rem;
    }

    .band {
        padding-block: 3.25rem;
    }

    /* Contact rows: half the height, same tap targets. */
    .detail {
        grid-template-columns: auto 1fr;
        align-items: center;
        gap: 0.35rem 0.85rem;
        padding: 0.7rem 0;
    }

    .detail .ico {
        font-size: 1.15rem;
    }

    .detail dt {
        margin: 0;
    }

    .detail dd {
        font-size: 0.98rem;
    }

    /* Fax and address matter less than reaching a person — they live in the
       footer for whoever wants them. */
    .detail--minor {
        display: none;
    }

    .form-panel {
        padding: 1.25rem 1.1rem;
    }

    /*
     * Legibility floor for the mono micro-labels. The desktop sizes render at
     * 10-11px on a phone; ~11.5px keeps the label voice without squinting.
     */
    .tag,
    .module-no,


    .telemetry-label,
    .logo-wall-count,
    .file-drop-hint,
    .file-drop-text,
    .field label,
    .field-error,
    .footer-heading,
    .footer-stamp,
    .footer-bottom p,
    .log-run,
    .hero-runsheet dt,
    .detail dt,
    .partner span {
        font-size: 0.72rem;
    }

    /* Primary actions read and press better full-width on a phone. */
    .closer .btn,
    .btn-row .btn,
    form .btn,
    .fault-actions .btn {
        width: 100%;
        justify-content: center;
    }
}

/* ==========================================================================
   Visible motion layer — word reveals, pointer glow, magnetic buttons
   ========================================================================== */

/*
 * Section statements assemble word by word as they enter. The spans are built
 * by script, so a no-JS visitor just reads the finished sentence.
 */
.js .wr {
    display: inline-block;
    overflow: hidden;
    vertical-align: bottom;
}

.js .wr > span {
    display: inline-block;
    transform: translateY(112%);
    transition: transform 0.65s var(--ease);
    transition-delay: var(--wd, 0ms);
}

.js .wr.is-in > span {
    transform: none;
}

/*
 * Pointer glow — a soft lamp follows the cursor across the big surfaces.
 * Coordinates arrive as custom properties from script; without them the
 * gradient never shows.
 */
.glow-card {
    position: relative;
}

.glow-card::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 3;
    border-radius: inherit;
    background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 50%),
        rgba(98, 144, 204, 0.13), transparent 65%);
    opacity: 0;
    transition: opacity 0.35s var(--ease-io);
    pointer-events: none;
}

.glow-card:hover::after {
    opacity: 1;
}

/* The field board scans itself — a slow pass of light, radar-fashion. */
.moment-frame {
    position: relative;
}

.moment-frame::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 2;
    background: linear-gradient(105deg,
        transparent 42%, rgba(98, 144, 204, 0.075) 50%, transparent 58%);
    background-size: 260% 100%;
    animation: board-sweep 8s linear infinite;
    pointer-events: none;
}

@keyframes board-sweep {
    from { background-position: 130% 0; }
    to { background-position: -130% 0; }
}

/* ==========================================================================
   Atmosphere — seam light, aurora drift, pointer tilt
   ========================================================================== */

/*
 * Where a pinned-dark panel meets the page above it, the top edge catches
 * light: a hairline of signal at the centre, dissolving before the margins.
 * Drawn on the section itself, so it needs no markup and joins both themes.
 */
.telemetry::before,
.duo::before,
.closer::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 1px;
    background: linear-gradient(90deg,
        transparent 6%, rgba(98, 144, 204, 0.4) 38%, rgba(255, 74, 63, 0.7) 50%,
        rgba(98, 144, 204, 0.4) 62%, transparent 94%);
    box-shadow: 0 0 26px 1px rgba(98, 144, 204, 0.22);
    pointer-events: none;
}

/*
 * Living light for the pinned-dark closer: three soft masses of colour drift
 * on the compositor alone — pre-blurred by their own gradients, so there is
 * no filter and no repaint. Screen-blended over whatever sits behind them.
 */
.aurora {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
}

.aurora i {
    position: absolute;
    width: 46vw;
    height: 46vw;
    min-width: 30rem;
    min-height: 30rem;
    border-radius: 50%;
    mix-blend-mode: screen;
    will-change: transform;
}

.aurora i:nth-child(1) {
    top: -18%;
    left: -8%;
    background: radial-gradient(closest-side, rgba(98, 144, 204, 0.17), transparent 72%);
    animation: aurora-a 34s var(--ease-io) infinite alternate;
}

.aurora i:nth-child(2) {
    right: -12%;
    bottom: -22%;
    background: radial-gradient(closest-side, rgba(230, 51, 41, 0.13), transparent 70%);
    animation: aurora-b 41s var(--ease-io) infinite alternate;
}

.aurora i:nth-child(3) {
    left: 30%;
    bottom: -30%;
    width: 34vw;
    height: 34vw;
    background: radial-gradient(closest-side, rgba(238, 242, 245, 0.07), transparent 74%);
    animation: aurora-c 28s var(--ease-io) infinite alternate;
}

@keyframes aurora-a {
    from { transform: translate3d(0, 0, 0) scale(1); }
    to { transform: translate3d(14%, 18%, 0) scale(1.28); }
}

@keyframes aurora-b {
    from { transform: translate3d(0, 0, 0) scale(1.15); }
    to { transform: translate3d(-16%, -12%, 0) scale(0.92); }
}

@keyframes aurora-c {
    from { transform: translate3d(0, 0, 0) scale(1); }
    to { transform: translate3d(10%, -16%, 0) scale(1.22); }
}

/* Pointer tilt (script): transform is written frame by frame from a lerp;
   the class only parks the card on the compositor. */
.tilt-card {
    will-change: transform;
}

/*
 * The pillar exhibits breathe — a 2% swell over nine seconds, the pace of a
 * thing at rest rather than a thing performing. Applied to the artwork, not
 * the panel, so the frame and its shadow hold still.
 */
@media (prefers-reduced-motion: no-preference) {
    .pillar-media > svg,
    .pillar-media > img,
    .pillar-media > .art-media {
        animation: exhibit-breathe 9s var(--ease-io) infinite alternate;
    }
}

@keyframes exhibit-breathe {
    from { transform: scale(1); }
    to { transform: scale(1.02); }
}

/*
 * The field board is the one exhibit deep enough to be worth real 3D: when
 * the pointer tilts it, the well tags ride a higher plane than the terrain,
 * so the board reads as a model, not a poster. The pulses stay in the map
 * plane — their scale animation owns their transform.
 */
.moment-media.tilt-card {
    transform-style: preserve-3d;
}

.moment-media.tilt-card .art-label {
    transform: translate(-50%, -50%) translateZ(34px);
}

.moment-media.tilt-card .art-chrome {
    transform: translateZ(18px);
}

/*
 * The board answers the pointer like the instrument it is drawn as: crosshair
 * over the terrain, and each well tag plates up and lifts when touched. The
 * chips are display, not navigation — so they light, but nothing moves the
 * page out from under the reader.
 */
.moment-media {
    cursor: crosshair;
}

.moment-media .art-label {
    pointer-events: auto;
    transition: transform 0.3s var(--ease), background 0.25s var(--ease-io),
                border-color 0.25s var(--ease-io);
}

.moment-media .art-label:hover {
    background: rgba(18, 25, 38, 0.97);
    border-color: rgba(230, 51, 41, 0.55);
}

.moment-media.tilt-card .art-label:hover {
    transform: translate(-50%, -50%) translateZ(52px) scale(1.05);
}

.moment-media .art-label:hover strong {
    color: #fff;
}

/* ==========================================================================
   Scroll-driven motion and page transitions — progressive enhancements
   ========================================================================== */

/* The big panels rise and settle as they scroll into view, driven by scroll
   position rather than a one-shot observer. Browsers without scroll timelines
   simply keep the reveal-on-observe behaviour. */
@supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
        /* The duo cards are not here: the pointer tilt owns their transform. */
        .moment-frame {
            animation: panel-rise linear both;
            animation-timeline: view();
            animation-range: entry 0% entry 85%;
        }

        /* The pillar artwork travels a beat slower than the copy beside it —
           two planes instead of one flat sheet. */
        .pillar-media {
            animation: pillar-drift linear both;
            animation-timeline: view();
            animation-range: cover 0% cover 100%;
        }

        /* Leaving the hero, its content settles back and dims — depth on exit. */
        .hero-inner {
            animation: hero-recede linear both;
            animation-timeline: scroll();
            animation-range: 0 110vh;
        }

        /* The sign-off surfaces as the footer arrives; the sheen keeps moving. */
        .footer-word-line {
            animation: word-surface linear both;
            animation-timeline: view();
            animation-range: entry 0% entry 90%;
        }
    }
}

@keyframes hero-recede {
    from { transform: none; opacity: 1; }
    to { transform: translateY(90px); opacity: 0.05; }
}

@keyframes word-surface {
    from { transform: translateY(34%); opacity: 0; }
    to { transform: none; opacity: 1; }
}

@keyframes panel-rise {
    from {
        transform: scale(0.93) translateY(44px);
        opacity: 0.35;
    }

    to {
        transform: none;
        opacity: 1;
    }
}

@keyframes pillar-drift {
    from { transform: translateY(clamp(14px, 2.5vw, 30px)); }
    to { transform: translateY(calc(-1 * clamp(14px, 2.5vw, 30px))); }
}

/* Pages hand over with a short cross-fade instead of a hard cut. */
@media (prefers-reduced-motion: no-preference) {
    @view-transition {
        navigation: auto;
    }

    ::view-transition-old(root),
    ::view-transition-new(root) {
        animation-duration: 0.28s;
    }
}

/* ==========================================================================
   Reduced motion — everything settles to its end state
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }

    .js [data-reveal],
    .js [data-reveal-child] {
        opacity: 1;
        transform: none;
    }

    .js .intro {
        display: none;
    }
}

/* ==========================================================================
   Print — the record, on paper
   ========================================================================== */

@media print {
    body {
        background: #fff;
        color: #111;
    }

    .site-nav,
    .backdrop,
    .skip-link,
    .intro,
    .aurora,
    .hero-beam,
    .scroll-cue,
    .footer-word {
        display: none !important;
    }

    .band,
    .feature,
    .closer,
    .log-header,
    .telemetry,

    .hero {
        background: #fff !important;
        padding-block: 1.5rem;
    }

    h1, h2, h3, .log-title, .telemetry-value {
        color: #111 !important;
    }

    p, li, dd {
        color: #333 !important;
    }

    a::after {
        content: ' (' attr(href) ')';
        font-size: 0.8em;
        color: #666;
    }
}

/* ==========================================================================
   Landing — the company front door: one statement, then two products
   ========================================================================== */

/* The doors sit directly under the hero, so the section opens tighter than a
   duo in mid-page and the cards run a step shorter: the choice should be on
   the first scroll, not the second. */
.duo--doors {
    padding-top: clamp(3rem, 6vw, 4.5rem);
}

.duo--doors .duo-card {
    min-height: clamp(23rem, 38vw, 30rem);
}

.duo--doors .duo-caption h3 {
    font-size: var(--step-3);
}

/* Origin — three dated steps on one rule, each with a short red tick where
   its column starts. */
.origin {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
    gap: clamp(2rem, 4vw, 3.5rem);
}

.origin-step {
    position: relative;
    padding-top: 1.4rem;
    border-top: 1px solid var(--line);
}

.origin-step::before {
    content: '';
    position: absolute;
    top: -1px;
    left: 0;
    width: 2.75rem;
    height: 2px;
    background: var(--signal);
}

.origin-year {
    font-family: var(--mono);
    font-size: 0.72rem;
    font-weight: 450;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--signal-text);
    margin-bottom: 0.9rem;
}

.origin-year::before { content: '[ '; color: var(--text-chrome); }
.origin-year::after { content: ' ]'; color: var(--text-chrome); }

.origin-step h3 {
    font-size: var(--step-2);
    font-weight: 550;
    letter-spacing: -0.015em;
    margin-bottom: 0.8rem;
}

.origin-step p:not(.origin-year) {
    color: var(--text-lo);
    line-height: 1.7;
    max-width: 40ch;
}

/* ==========================================================================
   Top bar menus — dropdown panels, site search, client logins
   ========================================================================== */

.nav-item {
    position: relative;
}

/* A menu trigger is a button dressed as a link, chevron included. */
.nav-link--menu {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    background: none;
    border: 0;
    padding-inline: 0;
    font: inherit;
    font-size: 0.95rem;
    font-weight: 450;
    letter-spacing: -0.01em;
    color: var(--text-lo);
    cursor: pointer;
}

.nav-link--menu .ico--chev {
    font-size: 0.72em;
    transition: transform 0.25s var(--ease);
}

.has-menu.is-open > .nav-link--menu .ico--chev {
    transform: rotate(180deg);
}

.has-menu.is-open > .nav-link,
.nav-link.is-current {
    color: var(--text-hi);
}

.has-menu.is-open > .nav-link::after,
.nav-link.is-current::after {
    transform: scaleX(1);
}

/* The panel: a frosted plate hung under its trigger. Hidden by visibility so
   it is out of the tab order when closed; the short delay on the way out lets
   the fade finish before it disappears. */
.nav-panel {
    position: absolute;
    top: calc(100% + 0.55rem);
    left: -1rem;
    z-index: 5;
    min-width: 17rem;
    padding: 0.55rem;
    background: var(--nav-panel-bg);
    border: 1px solid var(--nav-panel-edge);
    border-radius: 14px;
    -webkit-backdrop-filter: blur(22px) saturate(1.5);
    backdrop-filter: blur(22px) saturate(1.5);
    box-shadow: inset 0 1px 0 var(--nav-panel-specular), var(--elev-high);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    pointer-events: none;
    transition: opacity 0.18s var(--ease-io), transform 0.25s var(--ease), visibility 0s linear 0.25s;
}

.has-menu.is-open > .nav-panel {
    opacity: 1;
    visibility: visible;
    transform: none;
    pointer-events: auto;
    transition: opacity 0.18s var(--ease-io), transform 0.25s var(--ease);
}

.nav-panel--right {
    left: auto;
    right: -0.5rem;
}

.nav-panel-inner {
    display: grid;
    grid-template-columns: repeat(var(--cols, 1), minmax(14.5rem, 1fr));
    gap: 0.25rem 1.25rem;
    padding: 0.3rem;
}

.nav-col-head {
    padding: 0.55rem 0.75rem 0.3rem;
    font-family: var(--mono);
    font-size: 0.6rem;
    font-weight: 450;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.nav-col-head::before { content: '[ '; color: var(--text-chrome); }
.nav-col-head::after { content: ' ]'; color: var(--text-chrome); }

.nav-panel-link {
    display: grid;
    gap: 0.12rem;
    padding: 0.58rem 0.75rem;
    border-radius: 8px;
    text-decoration: none;
    color: var(--text-lo);
    transition: background 0.18s var(--ease-io);
}

.nav-panel-link strong {
    font-size: 0.92rem;
    font-weight: 550;
    letter-spacing: -0.01em;
    color: var(--text-hi);
}

.nav-panel-link span {
    font-size: 0.76rem;
    line-height: 1.4;
    color: var(--text-dim);
}

.nav-panel-link:hover,
.nav-panel-link:focus-visible {
    background: var(--accent-ink);
    outline: none;
}

.nav-panel-link[aria-current='page'] strong {
    color: var(--signal-hot);
}

.nav-panel-link .ico {
    font-size: 0.85em;
    margin-left: 0.3rem;
    vertical-align: -0.05em;
    color: var(--text-dim);
}

/* Right cluster: search, logins, the ask. */
.nav-tools {
    display: flex;
    align-items: center;
    gap: clamp(0.75rem, 1.4vw, 1.25rem);
    flex: none;
}

.nav-tool {
    display: inline-grid;
    place-items: center;
    width: 2.3rem;
    height: 2.3rem;
    border-radius: 999px;
    border: 1px solid transparent;
    background: transparent;
    color: var(--text-lo);
    cursor: pointer;
    transition: color 0.2s var(--ease-io), border-color 0.2s var(--ease-io), background 0.2s var(--ease-io);
}

.nav-tool .ico {
    font-size: 1.1rem;
}

.nav-tool:hover,
.has-menu.is-open > .nav-tool {
    color: var(--text-hi);
    border-color: var(--line-strong);
    background: var(--accent-ink);
}

.nav-search {
    position: relative;
}

.nav-panel--search {
    left: auto;
    right: -0.5rem;
    width: min(27rem, calc(100vw - 2 * var(--gutter)));
    padding: 0.75rem;
}

.nav-search-field input {
    width: 100%;
    padding: 0.72rem 0.95rem;
    font: inherit;
    font-size: 0.95rem;
    color: var(--text-hi);
    background: transparent;
    border: 1px solid var(--line-strong);
    border-radius: 10px;
    outline: none;
    transition: border-color 0.2s var(--ease-io);
}

.nav-search-field input::placeholder {
    color: var(--text-dim);
}

.nav-search-field input:focus {
    border-color: var(--signal-edge);
}

.nav-search-results {
    display: grid;
    gap: 0.1rem;
    margin-top: 0.5rem;
    max-height: min(24rem, 55vh);
    overflow: auto;
}

.nav-search-results li[hidden] {
    display: none;
}

.nav-search-results .nav-panel-link {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: baseline;
    column-gap: 1rem;
}

.nav-search-results .nav-panel-link span {
    font-family: var(--mono);
    font-size: 0.6rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    white-space: nowrap;
}

.nav-search-empty {
    padding: 0.85rem 0.75rem 0.4rem;
    font-size: 0.85rem;
    color: var(--text-dim);
}

/* The bar stays put while a panel is open, whatever the scroll does. */
.site-nav.has-open.is-hidden {
    transform: none;
}

/* Anchored sections land below the fixed bar, not under it. */
[id] {
    scroll-margin-top: calc(var(--nav-h) + 2.5rem);
}

@media (max-width: 992px) {
    /* Phones navigate from the bottom tab bar; the top bar keeps the brand. */
    .nav-tools {
        display: none;
    }
}

/* At laptop widths the four menus plus the tools crowd the bar; tighten the
   rhythm before anything wraps. */
@media (max-width: 1180px) {
    .nav-list {
        gap: 1rem;
    }

    .nav-link,
    .nav-link--menu {
        font-size: 0.9rem;
    }

    .nav-cta {
        padding-inline: 0.95rem;
    }
}

/* ==========================================================================
   Product sub-navigation — the weTrack section, one click from any page in it
   ========================================================================== */

.subnav {
    position: relative;
    z-index: 4;
    background: var(--ink-1);
    border-bottom: 1px solid var(--line);
}

.subnav-inner {
    display: flex;
    align-items: center;
    gap: clamp(1rem, 3vw, 2.5rem);
    max-width: var(--shell);
    margin-inline: auto;
    padding: 0 var(--gutter);
    min-height: 3.4rem;
    overflow-x: auto;
    scrollbar-width: none;
}

.subnav-inner::-webkit-scrollbar {
    display: none;
}

.subnav-brand {
    flex: none;
    font-size: 1.05rem;
    font-weight: 650;
    letter-spacing: -0.03em;
    color: var(--text-hi);
    text-decoration: none;
}

.subnav-list {
    display: flex;
    align-items: center;
    gap: clamp(0.9rem, 2vw, 1.75rem);
    margin-right: auto;
}

.subnav-link {
    position: relative;
    display: inline-block;
    padding: 1rem 0;
    font-size: 0.9rem;
    font-weight: 450;
    letter-spacing: -0.01em;
    color: var(--text-lo);
    text-decoration: none;
    white-space: nowrap;
    transition: color 0.2s var(--ease-io);
}

/* The current option carries a red rule on the bottom edge of the bar. */
.subnav-link::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 2px;
    background: var(--signal);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.3s var(--ease);
}

.subnav-link:hover,
.subnav-link[aria-current='page'] {
    color: var(--text-hi);
}

.subnav-link[aria-current='page']::after {
    transform: scaleX(1);
}

.subnav-cta {
    flex: none;
    font-size: 0.85rem;
}

@media (max-width: 992px) {
    .subnav-cta {
        display: none;
    }

    .subnav-inner {
        min-height: 3rem;
    }

    .subnav-link {
        padding: 0.85rem 0;
        font-size: 0.85rem;
    }
}

/* ==========================================================================
   Hero background video — plays behind the scrim, the photograph underneath
   ========================================================================== */

/* The video sits over the still and carries its own motion, so it does not
   take the drift the photograph has. */
.log-media--video {
    animation: none;
    background: #16223a;
}

/* Under reduced motion the still stays and the video never paints; the
   script also stops it loading. */
@media (prefers-reduced-motion: reduce) {
    .log-media--video {
        display: none;
    }
}

/* ==========================================================================
   Brand mark in the bar — one image, two renderings
   ========================================================================== */

/* Dark lettering on the off-white page; the light rendering takes over via
   --brand-dark / --brand-light, which the over-media rule flips. */
.brand-logo {
    display: block;
    height: 2.15rem;
    width: auto;
}

.brand-logo--dark {
    display: var(--brand-dark, block);
}

.brand-logo--light {
    display: var(--brand-light, none);
}

@media (max-width: 560px) {
    .brand-logo {
        height: 1.85rem;
    }
}

/* The full-screen heroes take the same background video as the page headers:
   over the still, no drift of its own, hidden under reduced motion. */
.hero-media--video {
    animation: none;
    background: #16223a;
}

@media (prefers-reduced-motion: reduce) {
    .hero-media--video {
        display: none;
    }
}
/* ==========================================================================
   Mobile app demo — copy steps down the left, the phone holds on the right
   ========================================================================== */

.app-demo {
    position: relative;
    padding: clamp(4rem, 8vw, 6.5rem) var(--gutter) clamp(3rem, 6vw, 5rem);
    background: var(--ink-1);
}

.app-demo-inner {
    display: grid;
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
    gap: clamp(2.5rem, 6vw, 6rem);
    align-items: start;
    max-width: var(--shell);
    margin-inline: auto;
}

.app-steps .prose-head {
    margin-bottom: clamp(3rem, 6vw, 5rem);
}

/* Each step is a tall stop on the way down, so the phone has time to change. */
.app-step {
    min-height: 42vh;
    padding-block: clamp(1.5rem, 4vh, 3rem);
    border-top: 1px solid var(--line);
    opacity: 0.45;
    transition: opacity 0.45s var(--ease-io);
}

.app-step.is-active {
    opacity: 1;
}

.app-step .tag {
    margin-bottom: 1rem;
}

.app-step h3 {
    font-size: var(--step-2);
    font-weight: 550;
    letter-spacing: -0.015em;
    margin-bottom: 0.9rem;
    max-width: 22ch;
}

.app-step p:not(.tag) {
    color: var(--text-lo);
    line-height: 1.7;
    max-width: 46ch;
}

/* The stage holds the viewport while the steps run past it. */
.app-stage {
    position: sticky;
    top: calc(var(--nav-h) + 2.5rem);
    display: grid;
    justify-items: center;
    gap: 1.5rem;
    perspective: 1600px;
}

.phone {
    position: absolute;
    left: 50%;
    top: 50%;
    translate: -50% -50%;
    height: 94%;
    aspect-ratio: 828 / 1800;
    transform-style: preserve-3d;
    transform: rotateY(-18deg) rotateX(7deg);
    will-change: transform;
}

/* The device: near-black body, hairline rim, the elevation stack, and a
   second rim inside so the bezel reads as depth rather than a border. */
.phone-frame {
    position: absolute;
    inset: 0;
    border-radius: 12.5% / 5.8%;
    background: linear-gradient(160deg, #2a3140, #0f1219 55%, #1b2130);
    box-shadow:
        inset 0 0 0 1px rgba(238, 242, 245, 0.18),
        inset 0 0 0 3px #0b0e14,
        var(--elev-high);
}

.phone-screen {
    position: absolute;
    inset: 1.6% 3.2%;
    border-radius: 10.5% / 4.9%;
    overflow: hidden;
    background: #0f1219;
}

.phone-shot {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0;
    transition: opacity 0.5s var(--ease-io);
}

.phone-shot.is-active {
    opacity: 1;
}

/* The island covers the status bar clock of the screenshot exactly enough. */
.phone-island {
    position: absolute;
    top: 3.1%;
    left: 50%;
    width: 30%;
    height: 3.6%;
    transform: translateX(-50%);
    border-radius: 999px;
    background: #0b0e14;
}

/* Spotlight: a ring in signal red with a soft halo, positioned by the step. */
.phone-spot {
    position: absolute;
    border: 2px solid var(--signal-hot);
    border-radius: 14px;
    box-shadow: 0 0 0 4px rgba(230, 51, 41, 0.18), 0 0 28px rgba(230, 51, 41, 0.45);
    transition: left 0.5s var(--ease), top 0.5s var(--ease), width 0.5s var(--ease), height 0.5s var(--ease);
    animation: spot-breathe 2.4s var(--ease-io) infinite alternate;
    pointer-events: none;
}

.phone-spot[hidden] {
    display: none;
}

@keyframes spot-breathe {
    from { box-shadow: 0 0 0 3px rgba(230, 51, 41, 0.14), 0 0 18px rgba(230, 51, 41, 0.35); }
    to { box-shadow: 0 0 0 6px rgba(230, 51, 41, 0.2), 0 0 34px rgba(230, 51, 41, 0.55); }
}

/* A pane of light across the glass, sitting just above the screen. */
.phone-glare {
    position: absolute;
    inset: 0;
    border-radius: 12.5% / 5.8%;
    background: linear-gradient(115deg, rgba(255, 255, 255, 0.16) 0%, rgba(255, 255, 255, 0.04) 28%, transparent 46%, transparent 70%, rgba(255, 255, 255, 0.05) 100%);
    pointer-events: none;
    transform: translateZ(2px);
}

.app-stage-cap {
    display: flex;
    gap: 1.5rem;
    font-family: var(--mono);
    font-size: 0.62rem;
    font-weight: 450;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.app-stage-cap span::before { content: '[ '; color: var(--text-chrome); }
.app-stage-cap span::after { content: ' ]'; color: var(--text-chrome); }

/* Store badges on an off-white band need a frame of their own. */
.app-badges {
    margin-top: 1.5rem;
}

@media (max-width: 900px) {
    .app-demo-inner {
        grid-template-columns: minmax(0, 1fr);
    }

    /* Phone first, held at the top while the steps run underneath it. */
    .app-stage {
        order: -1;
        top: calc(var(--nav-h) + 0.5rem);
        z-index: 2;
        padding-bottom: 1rem;
        background: linear-gradient(180deg, var(--ink-1) 82%, transparent);
    }


    .app-stage-cap {
        gap: 1rem;
        font-size: 0.56rem;
    }

    .app-step {
        min-height: 0;
        padding-block: 1.75rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    .phone-spot {
        animation: none;
    }
}

/* ==========================================================================
   App screens — the weTrack app rebuilt at 402x874 (iPhone logical px) for
   capture into the 3D phone. Colors and sizes measured off the real screenshots.
   ========================================================================== */

.scr {
    position: relative;
    width: 402px;
    height: 874px;
    overflow: hidden;
    background: #fff;
    font-family: 'Inter', -apple-system, 'Segoe UI', Roboto, sans-serif;
    font-feature-settings: 'cv11', 'ss01';
    color: #111;
    -webkit-font-smoothing: antialiased;
    letter-spacing: -0.01em;
    line-height: 1.2;
}

.scr * { box-sizing: border-box; }

/* Status bar */
.scr-status {
    position: relative;
    height: 53px;
    background: #3a3d3e;
    color: #fff;
}

.scr-time {
    position: absolute;
    left: 46px;
    top: 18px;
    font-size: 17.5px;
    font-weight: 600;
    letter-spacing: -0.02em;
}

.scr-bell {
    position: absolute;
    left: 90px;
    top: 20px;
    width: 16px;
    height: 16px;
}

.scr-status-right {
    position: absolute;
    right: 34px;
    top: 21px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.scr-signal { width: 19px; height: 13px; }
.scr-wifi { width: 18px; height: 14px; }

.scr-batt {
    display: inline-block;
    min-width: 30px;
    padding: 0 6px;
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.55);
    color: #3a3d3e;
    font-size: 12px;
    font-weight: 700;
    text-align: center;
    line-height: 15px;
}

/* Toolbar */
.scr-toolbar {
    position: relative;
    height: 72px;
    background: #3a3d3e;
}

.scr-burger {
    position: absolute;
    left: 16px;
    top: 26px;
    width: 28px;
    height: 20px;
}

/* The stacked app logo: the R+P mark cropped out of the header lockup, with
   INDUSTRIES set beneath it the way the app draws it. */
.scr-logo {
    position: absolute;
    left: 56px;
    top: 23px;
    display: grid;
    justify-items: end;
    gap: 1px;
}

.scr-logo-mark {
    display: block;
    width: 37px;
    height: 18px;
    background: url('../images/logo-header-light.webp') left center / auto 100% no-repeat;
}

.scr-logo b {
    font-size: 6.4px;
    font-weight: 800;
    letter-spacing: 0.02em;
    color: #fff;
    line-height: 1;
}

.scr-title {
    position: absolute;
    left: 0;
    right: 0;
    top: 24px;
    text-align: center;
    font-size: 21px;
    font-weight: 600;
    color: #fff;
    letter-spacing: -0.01em;
    padding-left: 40px;
}

.scr-search-ico {
    position: absolute;
    right: 56px;
    top: 24px;
    width: 26px;
    height: 26px;
}

.scr-kebab {
    position: absolute;
    right: 28px;
    top: 24px;
    width: 7px;
    height: 26px;
}

/* Search field */
.scr-searchbar {
    padding: 8px 9px 12px;
    background: #fff;
}

.scr-searchfield {
    display: flex;
    align-items: center;
    gap: 12px;
    height: 55px;
    padding: 0 20px;
    border-radius: 999px;
    background: #f6f6f6;
    box-shadow: 0 2px 7px rgba(0, 0, 0, 0.22), inset 0 0 0 1px rgba(0, 0, 0, 0.04);
    color: #9a9a9a;
    font-size: 16px;
    letter-spacing: -0.01em;
}

.scr-searchfield svg {
    width: 20px;
    height: 20px;
    flex: none;
}

/* Table */
.scr-table {
    width: 396px;
    margin-left: 3px;
    border-collapse: collapse;
    table-layout: fixed;
    font-size: 13.5px;
    line-height: 1.25;
}

.scr-table th {
    height: 60px;
    background: #3a3d3e;
    color: #fff;
    font-weight: 400;
    font-size: 16px;
    letter-spacing: -0.005em;
    border: 1px solid #1c1f20;
    border-top: 0;
}

.scr-table td {
    height: 52px;
    padding: 3px 3px;
    border: 1px solid #1c1c1c;
    color: #1d1d1f;
    vertical-align: middle;
    overflow-wrap: break-word;
}

.scr-table td:first-child {
    padding-left: 7px;
}

.scr-table tr.is-flag td:first-child {
    box-shadow: inset 6px 0 0 #a6ec99;
    padding-left: 9px;
}

/* Menu: the drawer slides the page to the right and dims it. */
.scr--menu > .scr-status {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    z-index: 3;
}

.scr-under {
    position: absolute;
    inset: 0;
    transform: translateX(321px);
}

.scr-dim {
    position: absolute;
    inset: 53px 0 0 0;
    background: rgba(0, 0, 0, 0.7);
}

.scr-drawer {
    position: absolute;
    top: 53px;
    left: 0;
    bottom: 0;
    width: 321px;
    background: #fff;
}

.scr-user {
    position: absolute;
    top: 22px;
    left: 21px;
    width: 279px;
    height: 60px;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 0 14px 0 11px;
    border: 1px solid #a6a6a6;
    border-radius: 30px;
    overflow: hidden;
}

.scr-avatar {
    width: 39px;
    height: 39px;
    border-radius: 50%;
    background: #bb352c;
    color: #fff;
    font-size: 18px;
    font-weight: 500;
    display: grid;
    place-items: center;
    flex: none;
    letter-spacing: 0.01em;
}

.scr-user-text {
    flex: 1;
    min-width: 0;
}

.scr-user-name {
    font-size: 21px;
    font-weight: 600;
    letter-spacing: -0.02em;
    line-height: 1.1;
    white-space: nowrap;
}

.scr-user-org {
    display: flex;
    align-items: center;
    gap: 7px;
    margin-top: 2px;
    font-size: 14.5px;
    color: #6a6a6a;
    letter-spacing: -0.01em;
    white-space: nowrap;
}

.scr-user-org i {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: #81be69;
    flex: none;
}

/* Chevron: two strokes, grey */
.scr-chev {
    width: 10px;
    height: 10px;
    border-right: 2.2px solid #9a9a9a;
    border-top: 2.2px solid #9a9a9a;
    transform: rotate(45deg);
    flex: none;
}

.scr-menu-label {
    position: absolute;
    top: 89px;
    left: 24px;
    margin: 0;
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.22em;
    color: #7a7a7a;
    line-height: 1;
}

.scr-menu {
    position: absolute;
    top: 112px;
    left: 21px;
    width: 279px;
    margin: 0;
    padding: 0;
    list-style: none;
    border: 1px solid #a6a6a6;
    border-radius: 22px;
    background: #fff;
    overflow: hidden;
}

.scr-menu li {
    position: relative;
    display: flex;
    align-items: center;
    height: 61px;
    padding: 0 22px 0 26px;
    font-size: 20px;
    font-weight: 500;
    letter-spacing: -0.015em;
}

.scr-menu li + li::before {
    content: '';
    position: absolute;
    left: 60px;
    right: 0;
    top: 0;
    height: 1px;
    background: #cfcfcf;
}

.scr-menu li > svg {
    width: 24px;
    height: 24px;
    margin-right: 26px;
    fill: #111;
    flex: none;
}

.scr-menu li > svg.scr-ico-line {
    fill: none;
    stroke: #111;
    stroke-width: 2;
    stroke-linejoin: round;
    stroke-linecap: round;
}

.scr-menu li > span:first-of-type {
    flex: 1;
}

.scr-menu-bar {
    width: 4px;
    height: 24px;
    margin-right: 14px;
    border-radius: 2px;
    background: #eb5852;
}

.scr-foot {
    position: absolute;
    left: 21px;
    width: 279px;
    bottom: 40px;
}

.scr-foot-rule {
    height: 1px;
    background: #dedede;
    margin-bottom: 12px;
}

.scr-actions {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
}

.scr-action {
    height: 78px;
    border: 1px solid #a6a6a6;
    border-radius: 12px;
    display: grid;
    justify-items: center;
    align-content: center;
    gap: 9px;
    font-size: 16px;
    font-weight: 600;
    color: #222;
    letter-spacing: -0.01em;
}

.scr-action svg {
    width: 36px;
    height: 30px;
}

.scr-version {
    margin: 12px 0 0;
    text-align: center;
    font-size: 12.5px;
    font-weight: 600;
    letter-spacing: 0.06em;
    color: #999;
}

/* Capture page: the two screens side by side on a neutral field. */
.screens-sheet {
    display: flex;
    gap: 40px;
    padding: 40px;
    background: #888;
    width: max-content;
}

/* ==========================================================================
   3D stage — the WebGL phone replaces the CSS fallback once it is running
   ========================================================================== */

.phone3d {
    position: relative;
    width: 100%;
    max-width: 30rem;
    aspect-ratio: 0.72;
    perspective: 1600px;
}

.phone3d canvas {
    position: absolute;
    inset: 0;
    display: block;
    width: 100% !important;
    height: 100% !important;
    opacity: 0;
}

.app-stage.has-3d .phone3d canvas {
    animation: stage-in 0.6s var(--ease-io) both;
}

/* The WebGL phone fades in over the CSS one, which then steps aside. Same box,
   same size, so nothing on the page moves during the swap. */
.app-stage.has-3d .phone {
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.6s var(--ease-io), visibility 0s linear 0.6s;
    pointer-events: none;
}

@keyframes stage-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

@media (max-width: 900px) {
    .phone3d {
        max-width: 17rem;
        aspect-ratio: 0.78;
    }
}

/* ==========================================================================
   Mobile page, second pass: active step label, install row, stage caption,
   the CSS phone's back face, the mirror table, store badges on the closer
   ========================================================================== */

.app-step.is-active .tag {
    color: var(--signal-text);
}

/* Install row under the demo head: both store paths on screen with the phone. */
.app-cta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem 1.5rem;
    margin-top: 1.5rem;
}

.app-cta .tag {
    flex-basis: 100%;
    margin: 0;
}

.app-cta .app-badges {
    margin-top: 0;
}

/* The caption under the stage names the current step. */
#stageCap {
    transition: opacity 0.3s var(--ease-io);
}

#stageCap.is-swapping {
    opacity: 0;
}

/* CSS fallback phone: a back face with the camera plateau, shown when the
   step turns the device around. */
.phone-frame,
.phone-glare {
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
}

.phone-back {
    position: absolute;
    inset: 0;
    border-radius: 12.5% / 5.8%;
    background: linear-gradient(200deg, #2a3140, #141821 55%, #1b2130);
    box-shadow: inset 0 0 0 1px rgba(238, 242, 245, 0.18), inset 0 0 0 3px #0b0e14, var(--elev-high);
    transform: rotateY(180deg) translateZ(1px);
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
}

.phone-cam {
    position: absolute;
    top: 3.5%;
    left: 5%;
    width: 38%;
    aspect-ratio: 1;
    border-radius: 22%;
    background: linear-gradient(160deg, #343b4a, #1c212c);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(238, 242, 245, 0.14);
}

.phone-cam i {
    position: absolute;
    width: 34%;
    aspect-ratio: 1;
    border-radius: 50%;
    background: radial-gradient(circle at 38% 36%, #2b3442 0 18%, #05070c 40%, #0d1119 100%);
    box-shadow: inset 0 0 0 2px #0a0d13, 0 0 0 1px rgba(238, 242, 245, 0.16);
}

.phone-cam i:nth-child(1) { top: 10%; left: 10%; }
.phone-cam i:nth-child(2) { bottom: 10%; left: 10%; }
.phone-cam i:nth-child(3) { top: 33%; right: 10%; }

.phone-cam b {
    position: absolute;
    top: 12%;
    right: 16%;
    width: 11%;
    aspect-ratio: 1;
    border-radius: 50%;
    background: #e8e2c8;
}

@media (prefers-reduced-motion: reduce) {
    #stageCap {
        transition: none;
    }
}

/* ==========================================================================
   Demo layout, third pass: the head sits above the steps on the left and the
   stage spans both rows on the right. On a phone the steps become a snap rail
   of cards under a large device, with dots for position.
   ========================================================================== */

.app-demo-inner {
    grid-template-rows: auto 1fr;
}

.app-demo-inner > .prose-head {
    grid-column: 1;
    grid-row: 1;
    margin-bottom: clamp(3rem, 6vw, 5rem);
}

.app-steps {
    grid-column: 1;
    grid-row: 2;
}

.app-stage {
    grid-column: 2;
    grid-row: 1 / span 2;
}

.app-rail {
    display: contents;
}

.app-dots {
    display: none;
}

@media (max-width: 900px) {
    .app-demo {
        padding: clamp(2.5rem, 8vw, 4rem) var(--gutter) 2rem;
    }

    .app-demo-inner {
        grid-template-columns: minmax(0, 1fr);
        grid-template-rows: none;
        gap: 1.5rem;
    }

    .app-demo-inner > .prose-head {
        grid-column: 1;
        grid-row: auto;
        margin-bottom: 0.25rem;
    }

    .app-demo-inner > .prose-head h2 {
        font-size: var(--step-3);
    }

    .app-cta {
        margin-top: 1rem;
    }

    /* The device, large, in normal flow. */
    .app-stage {
        grid-column: 1;
        grid-row: auto;
        order: 2;
        position: static;
        top: auto;
        z-index: auto;
        padding: 0;
        background: none;
        gap: 0.9rem;
    }

    /* Sized so the device, its caption, the card rail and the dots fit
       between the nav and the tab bar (about 32rem of chrome and cards), so
       the whole stage stays on screen while a card is swiped. The floor
       keeps it legible on very short phones, which then scroll a little. */
    .phone3d {
        max-width: min(19rem, 64vw, max(11rem, (100svh - 32rem) * 0.72));
    }

    .app-steps {
        grid-column: 1;
        grid-row: auto;
        order: 3;
    }

    /* The rail: cards that snap to centre and bleed to the screen edges. */
    .app-rail {
        display: flex;
        gap: 0.75rem;
        overflow-x: auto;
        scroll-snap-type: x mandatory;
        scroll-padding-inline: var(--gutter);
        padding: 0.25rem var(--gutter) 0.75rem;
        margin-inline: calc(-1 * var(--gutter));
        scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
    }

    .app-rail::-webkit-scrollbar {
        display: none;
    }

    .app-step {
        flex: 0 0 min(78vw, 22rem);
        scroll-snap-align: center;
        min-height: 0;
        padding: 1.1rem 1.15rem 1.25rem;
        border: 1px solid var(--line);
        border-radius: 14px;
        background: var(--ink-0);
        opacity: 0.6;
        transition: opacity 0.35s var(--ease-io), border-color 0.35s var(--ease-io);
    }

    .app-step.is-active {
        opacity: 1;
        border-color: var(--signal-edge);
    }

    .app-step .tag {
        margin-bottom: 0.7rem;
    }

    .app-step h3 {
        font-size: var(--step-1);
        margin-bottom: 0.6rem;
    }

    .app-step p:not(.tag) {
        font-size: var(--step--1);
        line-height: 1.6;
    }

    /* Dots: one per card, the active one long and red. */
    .app-dots {
        display: flex;
        justify-content: center;
        gap: 0.45rem;
        margin-top: 0.25rem;
    }

    /* A 24px tap target around a small drawn dot. */
    .app-dot {
        display: grid;
        place-items: center;
        width: 1.5rem;
        height: 1.5rem;
        padding: 0;
        border: 0;
        background: none;
        cursor: pointer;
    }

    .app-dot::before {
        content: '';
        width: 0.5rem;
        height: 0.5rem;
        border-radius: 999px;
        background: var(--line-strong);
        transition: width 0.3s var(--ease), background 0.3s var(--ease-io);
    }

    .app-dot.is-active {
        width: 2.5rem;
    }

    .app-dot.is-active::before {
        width: 1.5rem;
        background: var(--signal);
    }
}

/* ==========================================================================
   Dashboards demo (/analytics): one drawn board that each step re-points.
   HTML and SVG only. Bars rescale with a transform, donut slices redraw
   through stroke-dasharray, and the words swap behind a short fade. Surface
   pinned dark like the other product art so it reads as the product in both
   themes. Type is sized from the frame's own width so the board scales as
   one piece.
   ========================================================================== */

/* The board takes the wider column; the steps read fine narrower. Shared by
   the dashboards stage and the record boards on the capability pages. */
.app-demo--dash .app-demo-inner,
.app-demo--board .app-demo-inner {
    grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
}

.app-demo--dash .app-stage,
.app-demo--board .app-stage {
    perspective: none;
}

/* The halo reaches past the section's right edge; clip it rather than let it
   widen the document. `clip` and not `hidden`, so the sticky stage inside
   still sticks. */
.app-demo--dash,
.app-demo--board {
    overflow-x: clip;
}

/* The scene: perspective for the tilt, and the halo behind the glass. The
   frame's transform is composed in dash-stage.js; nothing here sets one. */
/* The dashboard uses the same pinned-device story as /mobile. */
.app-demo--dashboard { overflow-x: clip; }
.app-demo--dashboard .app-demo-inner {
    grid-template-columns: minmax(0, 0.72fr) minmax(0, 1.28fr);
    column-gap: clamp(2rem, 4vw, 4rem);
}
.app-demo--dashboard .app-step {
    min-height: 52vh;
    scroll-margin-top: 35vh;
}
.dashboard-stage { perspective: none; }
.dashboard-device { margin: 0; width: 100%; min-width: 0; }
.dashboard-viewport {
    position: relative;
    width: 100%;
    aspect-ratio: 1.2;
    max-height: calc(100svh - var(--nav-h) - 13rem);
    min-height: 240px;
    isolation: isolate;
    border-radius: 18px;
    background:
        radial-gradient(ellipse at 50% 46%, rgba(78, 147, 175, 0.13), transparent 67%),
        linear-gradient(rgba(95, 124, 148, 0.045) 1px, transparent 1px),
        linear-gradient(90deg, rgba(95, 124, 148, 0.045) 1px, transparent 1px);
    background-size: auto, 32px 32px, 32px 32px;
}
.dashboard-fallback {
    position: absolute;
    width: 94%;
    height: auto;
    left: 3%;
    top: 50%;
    transform: translateY(-50%);
    border: 8px solid #181d26;
    border-radius: 10px;
}
/* Hide the fallback itself, not opacity: no second picture under the WebGL device. */
.dashboard-fallback[hidden] { display: none !important; }
.dashboard-canvas { position: absolute; inset: 0; }
.dashboard-canvas canvas { display: block; width: 100%; height: 100%; }
.dashboard-device.has-3d .dashboard-canvas { cursor: grab; touch-action: pan-y; }
.dashboard-device.is-dragging .dashboard-canvas { cursor: grabbing; }
.dashboard-hud {
    position: absolute; top: 0.65rem; left: 1rem; right: 1rem;
    display: flex; justify-content: space-between; gap: 1rem;
    font: 500 0.65rem var(--font-mono, monospace); letter-spacing: 0.1em;
    text-transform: uppercase; color: var(--text-lo); pointer-events: none;
    z-index: 2;
}
.dashboard-hud span:first-child::before {
    content: ''; display: inline-block; width: 5px; height: 5px;
    border-radius: 50%; background: #4b9fa7; margin-right: 0.6rem;
}
.dashboard-hud span { background: var(--ink-0); padding: 0.3rem 0.4rem; border-radius: 3px; }
.dashboard-drag-hint {
    position: absolute; bottom: 0.7rem; left: 1rem;
    font: 0.65rem var(--font-mono, monospace); color: var(--text-lo);
    pointer-events: none; opacity: 0; z-index: 2;
}
.has-3d .dashboard-drag-hint { opacity: 0.75; }
@media (hover: none), (prefers-reduced-motion: reduce) {
    .has-3d .dashboard-drag-hint { display: none; }
}
.dashboard-tools { display: flex; justify-content: center; flex-wrap: wrap; gap: 0.5rem; margin: 0.5rem 0 0.9rem; }
.dashboard-tools[hidden] { display: none; }
.dashboard-tools button {
    cursor: pointer; border: 1px solid var(--line); border-radius: 6px;
    background: var(--ink-0); color: var(--text-lo); padding: 0.5rem 0.7rem;
    font: inherit; font-size: 0.72rem;
}
.dashboard-tools button:hover, .dashboard-tools button[aria-pressed="true"] {
    color: var(--text-hi); border-color: #4b9fa7;
}
.dashboard-tools button:focus-visible { outline: 2px solid var(--signal); outline-offset: 3px; }
.dashboard-device .app-stage-cap { justify-content: center; flex-wrap: wrap; }
.dashboard-device figcaption { text-align: center; }
.dashboard-views {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.4rem;
    margin-block: 1rem 0.75rem;
}
.dashboard-views a {
    padding: 0.55rem 0.8rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    color: var(--text-lo);
    font-size: 0.8rem;
    text-decoration: none;
}
.dashboard-views a[aria-current] { border-color: var(--signal); color: var(--text-hi); background: var(--ink-0); }
.dashboard-views a:focus-visible { outline: 2px solid var(--signal); outline-offset: 3px; }
.dashboard-fullsize { font-size: 0.8rem; color: var(--text-lo); }
@media (max-width: 900px) {
    .app-demo--dashboard .app-demo-inner { display: flex; flex-direction: column; gap: 1.5rem; }
    .app-demo--dashboard .app-demo-inner > .prose-head { order: 1; }
    .app-demo--dashboard .dashboard-stage { order: 2; width: 100%; }
    .app-demo--dashboard .app-steps { order: 3; width: 100%; min-width: 0; }
    .app-demo--dashboard .app-step { min-height: 0; }
    .dashboard-viewport { aspect-ratio: 1.35; max-height: 48svh; min-height: 220px; }
}
.dash-scene {
    position: relative;
    width: 100%;
    max-width: 46rem;
    perspective: 1500px;
}

.dash-halo {
    position: absolute;
    inset: -22% -16% -26%;
    z-index: 0;
    border-radius: 50%;
    background:
        radial-gradient(closest-side at 26% 36%, rgba(63, 154, 166, 0.75), transparent 72%),
        radial-gradient(closest-side at 78% 74%, rgba(230, 51, 41, 0.55), transparent 70%),
        radial-gradient(closest-side at 62% 18%, rgba(98, 144, 204, 0.5), transparent 70%);
    animation: dash-halo-drift 16s var(--ease-io) infinite alternate;
    pointer-events: none;
}

@keyframes dash-halo-drift {
    from { transform: translate(-2%, 1%) scale(0.98); }
    to { transform: translate(2%, -2%) scale(1.04); }
}

/* Light crossing the glass. Sits above the panels, under nothing. */
.dash-sheen {
    position: absolute;
    inset: 0;
    z-index: 3;
    border-radius: inherit;
    background: linear-gradient(105deg,
        transparent 40%, rgba(255, 255, 255, 0.07) 50%, transparent 60%);
    background-size: 260% 100%;
    animation: board-sweep 9s linear infinite;
    pointer-events: none;
}

.dash-frame {
    position: relative;
    z-index: 1;
    transform-style: preserve-3d;
    will-change: transform;
    --dash-ink: rgba(238, 242, 245, 0.9);
    --dash-dim: rgba(238, 242, 245, 0.5);
    --dash-chrome: rgba(238, 242, 245, 0.34);
    --dash-line: rgba(238, 242, 245, 0.12);
    --dash-well: rgba(98, 144, 204, 0.06);
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto auto minmax(0, 1fr) auto;
    gap: 0.6rem;
    width: 100%;
    aspect-ratio: 16 / 11;
    padding: 0.9rem 1rem 0.8rem;
    border: 1px solid var(--dash-line);
    border-radius: 12px;
    background:
        repeating-linear-gradient(180deg, rgba(255, 255, 255, 0.022) 0 1px, transparent 1px 5px),
        radial-gradient(120% 80% at 20% 0%, rgba(63, 154, 166, 0.16), transparent 55%),
        linear-gradient(165deg, #131926, #0f131c 60%, #0d1118);
    color: var(--dash-ink);
    font-family: var(--mono);
    font-size: 0.62rem;
    font-size: clamp(0.5rem, 1.9cqi, 0.8rem);
    letter-spacing: 0.04em;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.06),
        0 30px 60px -30px rgba(10, 16, 28, 0.7),
        var(--elev-high);
    container-type: inline-size;
    transition: box-shadow 0.6s var(--ease-io), border-color 0.6s var(--ease-io);
}

/* The refresh: the rim brightens for a beat and the moved figures flash. */
.dash-frame.is-refreshing {
    animation: dash-refresh 0.9s var(--ease-io);
}

@keyframes dash-refresh {
    0% { border-color: var(--dash-line); }
    25% { border-color: rgba(63, 154, 166, 0.7); }
    100% { border-color: var(--dash-line); }
}

.dash-kpi strong.is-fresh {
    animation: dash-fresh 0.9s var(--ease-io);
}

@keyframes dash-fresh {
    0% { color: #fff; }
    30% { color: #7fd6e0; }
    100% { color: #fff; }
}

/* Depth: the readouts and the two charts sit a little in front of the sheet,
   so the tilt reads as a real object rather than a picture of one. */
.dash-range { transform: translateZ(8px); }
.dash-kpi { transform: translateZ(16px); }
.dash-panel { transform: translateZ(24px); }
.dash-bar, .dash-foot { transform: translateZ(4px); }

.dash-bar,
.dash-foot {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
}

.dash-chrome,
.dash-foot span,
.dash-range-lab,
.dash-panel-title {
    font-size: 0.92em;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--dash-chrome);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The title gives way before the live readout does. */
.dash-chrome:first-child {
    flex: 1 1 auto;
    min-width: 0;
    color: var(--dash-dim);
}

.dash-chrome--live::before {
    content: '';
    display: inline-block;
    width: 0.5em;
    height: 0.5em;
    margin-right: 0.6em;
    border-radius: 999px;
    background: var(--signal-hot);
    vertical-align: middle;
}

.dash-bar {
    padding-bottom: 0.6rem;
    border-bottom: 1px solid var(--dash-line);
}

/* The range bar: activity over the window, with the period in view marked. */
.dash-range {
    position: relative;
    height: 2.7rem;
    border: 1px solid var(--dash-line);
    border-radius: 6px;
    background: var(--dash-well);
    overflow: hidden;
}

/* The activity line sits in the upper part of the strip, above the labels. */
.dash-range svg {
    position: absolute;
    top: 0.15rem;
    left: 0;
    width: 100%;
    height: 52%;
}

.dash-range-line {
    fill: none;
    stroke: rgba(238, 242, 245, 0.5);
    stroke-width: 1.2;
    vector-effect: non-scaling-stroke;
}

.dash-range-win {
    position: absolute;
    top: 0;
    bottom: 0;
    background: rgba(230, 51, 41, 0.14);
    border-inline: 1px solid var(--signal-hot);
    transition: left 0.7s var(--ease), width 0.7s var(--ease);
    overflow: hidden;
}

/* The playhead: crosses the period in view once per refresh. */
.dash-range-head {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 1px;
    background: var(--signal-hot);
    box-shadow: 0 0 10px var(--signal-hot), 0 0 2px #fff;
    animation: dash-head 5s linear infinite;
}

@keyframes dash-head {
    from { left: 0; }
    to { left: 100%; }
}

.dash-range-lab {
    position: absolute;
    bottom: 0.25rem;
    font-size: 0.8em;
}

.dash-range-lab--l { left: 0.5rem; }
.dash-range-lab--r { right: 0.5rem; }

/* Three readouts across the top. */
.dash-kpis {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0.5rem;
}

.dash-kpi {
    display: grid;
    gap: 0.2rem;
    padding: 0.55rem 0.7rem;
    border: 1px solid var(--dash-line);
    border-radius: 6px;
    background: var(--dash-well);
    min-width: 0;
}

.dash-kpi strong {
    font-family: var(--display);
    font-size: 2.1em;
    font-weight: 550;
    letter-spacing: -0.02em;
    line-height: 1;
    color: #fff;
}

.dash-kpi span {
    font-size: 0.88em;
    color: var(--dash-dim);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The two charts. */
.dash-panels {
    display: grid;
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
    gap: 0.6rem;
    min-height: 0;
}

.dash-panel {
    display: grid;
    grid-template-rows: auto minmax(0, 1fr);
    gap: 0.5rem;
    padding: 0.6rem 0.7rem 0.55rem;
    border: 1px solid var(--dash-line);
    border-radius: 6px;
    background: var(--dash-well);
    min-height: 0;
    min-width: 0;
}

.dash-bars {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    gap: 0.45rem;
    align-items: stretch;
    min-height: 0;
    padding-top: 0.2rem;
    border-bottom: 1px solid var(--dash-line);
}

.dash-col {
    display: grid;
    grid-template-rows: minmax(0, 1fr) auto;
    gap: 0.35rem;
    min-width: 0;
    min-height: 0;
}

.dash-col-bar {
    display: block;
    height: 100%;
    border-radius: 2px 2px 0 0;
    background: linear-gradient(180deg, #6cc3d1, #3f8b9b 70%, #2e6b78);
    box-shadow: 0 0 18px rgba(63, 154, 166, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.35);
    transform: scaleY(var(--h, 0));
    transform-origin: bottom;
    transition: transform 0.7s var(--ease);
}

/* Labels wrap at spaces only, so the data keeps them to one short word or
   two; five columns leave each label room for eight characters. */
.dash-col-lab {
    font-size: 0.72em;
    line-height: 1.2;
    text-align: center;
    color: var(--dash-dim);
    padding-bottom: 0.3rem;
}

/* The pie and its tag callouts share one SVG so the leaders can meet the
   slices. Sized to the panel; the viewBox keeps the drawing in proportion. */
.dash-pie {
    display: block;
    width: 100%;
    height: 100%;
    min-height: 0;
}

/* Slices are wedge paths, morphed in script so neighbours always meet; no
   CSS transition, which would move each shape independently. The hairline
   stroke in the slice's own colour covers the anti-aliasing seam between
   wedges. */
.dash-slice {
    stroke-width: 0.6;
    stroke-linejoin: round;
}

.dash-call path {
    fill: none;
    stroke-width: 0.8;
    opacity: 0.7;
}

.dash-call text {
    font-family: var(--mono);
    font-size: 7.6px;
    font-weight: 500;
    letter-spacing: 0;
    fill: #10141d;
}

.dash-call-pct {
    text-anchor: end;
}

.dash-foot {
    padding-top: 0.55rem;
    border-top: 1px solid var(--dash-line);
}

/* Words fade while the shapes move. */
.dash-frame :is(#dashTitle, .dash-kpi, .dash-panel-title, .dash-col-lab, .dash-calls) {
    transition: opacity 0.16s var(--ease-io);
}

.dash-frame.is-swapping :is(#dashTitle, .dash-kpi, .dash-panel-title, .dash-col-lab, .dash-calls) {
    opacity: 0;
}

@media (max-width: 900px) {
    /* On a phone the board is sized by its content, not a fixed aspect: the
       two charts stack, the legend runs in two columns and the footer goes,
       so the board, its caption, the card rail and the dots fit between the
       nav and the tab bar on a typical phone. Short phones scroll a little. */
    .app-demo--dash .app-demo-inner,
    .app-demo--board .app-demo-inner {
        grid-template-columns: minmax(0, 1fr);
    }

    .dash-scene {
        max-width: 100%;
        perspective: none;
    }

    .dash-halo,
    .dash-sheen {
        display: none;
    }

    .dash-frame {
        aspect-ratio: auto;
        max-width: 100%;
        gap: 0.5rem;
        padding: 0.75rem 0.8rem 0.7rem;
        font-size: 0.6rem;
        transform-style: flat;
        will-change: auto;
    }

    .dash-range, .dash-kpi, .dash-panel, .dash-bar, .dash-foot {
        transform: none;
    }

    .dash-range {
        height: 1.9rem;
    }

    .dash-range svg {
        top: 0.3rem;
        height: 70%;
    }

    .dash-range-lab {
        display: none;
    }

    .dash-kpi {
        padding: 0.45rem 0.55rem;
    }

    .dash-kpi strong {
        font-size: 1.7em;
    }

    .dash-kpi span {
        white-space: normal;
        line-height: 1.3;
    }

    .dash-panels {
        grid-template-columns: minmax(0, 1fr);
        gap: 0.5rem;
    }

    .dash-bars {
        height: 3.5rem;
    }

    .dash-pie {
        height: auto;
        max-height: 6.5rem;
    }

    .dash-foot {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .dash-range-win,
    .dash-col-bar,
    .dash-slice,
    .dash-frame :is(#dashTitle, .dash-kpi, .dash-panel-title, .dash-col-lab, .dash-calls) {
        transition: none;
    }

    .dash-halo,
    .dash-sheen,
    .dash-range-head,
    .dash-frame.is-refreshing,
    .dash-kpi strong.is-fresh {
        animation: none;
    }
}

/* ==========================================================================
   Record boards (asset tracking, service history, inventory, safety): one
   board per page, lit part by part as the steps scroll past. Two kinds: a
   generated render with live plates over its chips (--art), and a drawn
   sheet built from the dashboard pieces (--sheet). The tilt transform is
   composed in record-stage.js; nothing here sets one on the frame.
   ========================================================================== */

.rec-frame {
    --dash-ink: rgba(238, 242, 245, 0.9);
    --dash-dim: rgba(238, 242, 245, 0.5);
    --dash-chrome: rgba(238, 242, 245, 0.34);
    --dash-line: rgba(238, 242, 245, 0.12);
    --dash-well: rgba(98, 144, 204, 0.06);
    --lit: 63, 154, 166;
    position: relative;
    z-index: 1;
    width: 100%;
    border: 1px solid var(--dash-line);
    border-radius: 12px;
    background: #0b0e14;
    color: var(--dash-ink);
    font-family: var(--mono);
    font-size: 0.62rem;
    font-size: clamp(0.5rem, 1.9cqi, 0.8rem);
    letter-spacing: 0.04em;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.06),
        0 30px 60px -30px rgba(10, 16, 28, 0.7),
        var(--elev-high);
    container-type: inline-size;
    transform-style: preserve-3d;
    will-change: transform;
}

/* The render is the board: natural height, plates positioned over it. */
.rec-frame--art .art-media {
    border-radius: inherit;
    overflow: hidden;
}

.rec-frame--art .art-media img {
    display: block;
    width: 100%;
    height: auto;
}

.rec-frame--art .feature-art {
    display: block;
    width: 100%;
    height: auto;
    border-radius: inherit;
}

/* Per-step drawings laid over the base board. The base image keeps the frame's
   height; these sit on top of it and crossfade as the reader moves between
   steps (record-stage.js). Later siblings, so they paint under the plates. */
.rec-frame--art .art-media img.rec-swap,
.js .rec-frame--art .art-media img.rec-swap.img-fade.is-loaded {
    /* site.js gives every image an img-fade class and flips it to opacity 1 on
       load; that rule outranks a plain class chain, so this one names the same
       element more specifically and keeps the step logic in charge. */
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0;
    transition: opacity 0.65s var(--ease-io);
}

.rec-frame--art .art-media img.rec-swap.is-shown,
.js .rec-frame--art .art-media img.rec-swap.img-fade.is-loaded.is-shown {
    opacity: 1;
}

/* The plates and pin are measured to the base board, so they step aside while
   another drawing is up. */
.rec-frame.is-swapped .art-label,
.rec-frame.is-swapped .map-pulse {
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s var(--ease-io), visibility 0s linear 0.3s;
}

/* Plates that belong to the service-history drawing. The drawing's own blank
   cards were painted out of the file (they were in perspective, and no flat box
   lines up with a tilted card at every size), so these plates ARE the cards and
   take the same sizing as the base board's. They appear only while that drawing
   is up. Hidden by default; the attribute selector outranks the is-swapped rule
   above so they can show while the base plates are hidden. */
.art-media--asset .art-label--hist {
    opacity: 0;
    visibility: hidden;
}

.rec-frame[data-art='history'] .art-media--asset .art-label--hist {
    opacity: 1;
    visibility: visible;
    transition: opacity 0.35s var(--ease-io) 0.25s;
}

/* ==========================================================================
   Record-board renders on the capability pages. Each render has blank plates
   baked in; these min sizes are those plates measured off the file, as a share
   of the frame width, so the HTML plate blankets the painted one at any size.
   Re-measure if a render is regenerated.
   ========================================================================== */

/* art-safety.webp: three plates stacked on the right, 29.5% x 11.2% each. */
.art-media--safety .art-label {
    min-width: 30cqi;
    min-height: 11.6cqi;
    border-radius: max(6px, 1.1cqi);
    padding: 0.5em 1.4cqi;
    gap: 0.3em;
}
.art-media--safety .art-label strong { font-size: max(1.8cqi, 8.5px); }
.art-media--safety .art-label span { font-size: max(1.3cqi, 7.5px); }

/* art-service.webp: three small card stacks in a row above the timeline, about
   10.4% x 12% each and 13% apart, so these plates stay narrow and the type
   small; the parts plate below shares the sizing. */
.art-media--service .art-label {
    min-width: 12.6cqi;
    min-height: 12.4cqi;
    border-radius: max(5px, 0.8cqi);
    padding: 0.45em 0.9cqi;
    gap: 0.22em;
}
.art-media--service .art-label strong { font-size: max(1.35cqi, 8px); }
.art-media--service .art-label span { font-size: max(1cqi, 7px); }

/* art-inventory.webp: two plates side by side at the bottom right, 18.7% x 15%. */
.art-media--inventory .art-label {
    min-width: 19cqi;
    min-height: 15cqi;
    border-radius: max(6px, 1cqi);
    padding: 0.5em 1.2cqi;
    gap: 0.3em;
}
.art-media--inventory .art-label strong { font-size: max(1.6cqi, 8.5px); }
.art-media--inventory .art-label span { font-size: max(1.15cqi, 7.5px); }

.art-media--safety .art-chrome,
.art-media--service .art-chrome,
.art-media--inventory .art-chrome { font-size: max(1.1cqi, 8px); }

/* ==========================================================================
   Dashboards board over its render. art-dashboards-equipment.webp is a wall
   board with its own bezel: a site map on the left half, three readout plates
   across the top right, an empty bar frame and an empty ring frame below them.
   The live pieces are placed over those frames by percentage of the image, and
   the board's own surface, border and panel boxes switch off so only the render
   shows. The range strip has no home on the render and is hidden.
   ========================================================================== */
.dash-frame--art {
    display: block;
    aspect-ratio: 1586 / 992;
    padding: 0;
    border: 0;
    border-radius: 10px;
    background-color: #0b0e14;
    background-size: cover;
    background-position: center;
    box-shadow: 0 30px 60px -30px rgba(10, 16, 28, 0.7), var(--elev-high);
}

.dash-frame--art .dash-range { display: none; }

.dash-frame--art .dash-bar,
.dash-frame--art .dash-foot {
    position: absolute;
    left: 48.75%;
    width: 46.25%;
    height: 6%;
    padding: 0;
    border: 0;
    align-items: center;
}

.dash-frame--art .dash-bar { top: 13.2%; }
.dash-frame--art .dash-foot { left: 4%; width: 41%; top: 13.2%; }

.dash-frame--art .dash-kpis {
    position: absolute;
    left: 48.75%;
    width: 46.25%;
    top: 20%;
    height: 12.5%;
    display: grid;
    /* Three plates 13.75% wide with 1.25% between, as drawn. */
    grid-template-columns: 13.75fr 1.25fr 15fr 1.25fr 15fr;
    gap: 0;
}

.dash-frame--art .dash-kpi:nth-child(1) { grid-column: 1; }
.dash-frame--art .dash-kpi:nth-child(2) { grid-column: 3; }
.dash-frame--art .dash-kpi:nth-child(3) { grid-column: 5; }

.dash-frame--art .dash-kpi,
.dash-frame--art .dash-panel {
    background: none;
    border: 0;
    box-shadow: none;
    border-radius: 0;
}

.dash-frame--art .dash-kpi {
    display: grid;
    align-content: center;
    padding: 0 1.2cqi;
    min-width: 0;
}

.dash-frame--art .dash-panels {
    position: absolute;
    left: 48.75%;
    width: 47.5%;
    top: 35%;
    height: 42.5%;
    display: grid;
    /* Bar frame 23.75% wide, gap 1.25%, ring frame 22.5% wide, as drawn. */
    grid-template-columns: 23.75fr 1.25fr 22.5fr;
    gap: 0;
}

.dash-frame--art .dash-panel--bars { grid-column: 1; }
.dash-frame--art .dash-panel--pie { grid-column: 3; }

.dash-frame--art .dash-panel {
    padding: 1.4cqi 1.4cqi 1.1cqi;
    min-width: 0;
    min-height: 0;
}

/* The drawn sheet, from the dashboard pieces. */
.rec-frame--sheet {
    display: grid;
    /* One bounded column: without it the header's non-wrapping text sizes
       the implicit track wider than the board on a phone. */
    grid-template-columns: minmax(0, 1fr);
    gap: 0.6rem;
    padding: 0.9rem 1rem 0.8rem;
    background:
        repeating-linear-gradient(180deg, rgba(255, 255, 255, 0.022) 0 1px, transparent 1px 5px),
        radial-gradient(120% 80% at 20% 0%, rgba(63, 154, 166, 0.16), transparent 55%),
        linear-gradient(165deg, #131926, #0f131c 60%, #0d1118);
}

.rec-cols {
    display: grid;
    grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.75fr);
    gap: 0.6rem;
    min-width: 0;
}

.rec-cols--3 {
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr) minmax(0, 1fr);
}

.rec-stack {
    display: grid;
    gap: 0.6rem;
    align-content: start;
    min-width: 0;
}

/* Event rows: date, what and who, and a status tag. */
.rec-rows {
    display: grid;
    gap: 0.4rem;
    align-content: start;
}

.rec-rows,
.rec-frame .dash-panel > * {
    min-width: 0;
}

.rec-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: 0.7rem;
    padding: 0.5rem 0.65rem;
    border: 1px solid var(--dash-line);
    border-radius: 6px;
    background: var(--dash-well);
    min-width: 0;
    overflow: hidden;
}

.rec-row > div {
    min-width: 0;
}

/* The detail line is a block and wraps: a sheet can grow, a chart cannot. */
.rec-row small {
    display: block;
    white-space: normal;
    line-height: 1.3;
    overflow: visible;
}

.rec-row time {
    color: var(--dash-dim);
    white-space: nowrap;
}

.rec-row b {
    display: block;
    font-family: var(--display);
    font-size: 1.15em;
    font-weight: 550;
    letter-spacing: -0.01em;
    color: #fff;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.rec-row small,
.rec-field dt,
.rec-note {
    font-size: 0.9em;
    color: var(--dash-dim);
}

.rec-field dt,
.rec-note {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.rec-tag {
    padding: 0.2em 0.55em;
    border-radius: 4px;
    font-size: 0.85em;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    background: rgba(98, 144, 204, 0.2);
    color: #9fc0ea;
    white-space: nowrap;
}

.rec-tag--ok { background: rgba(63, 154, 166, 0.22); color: #7fd6e0; }
.rec-tag--hot { background: rgba(230, 51, 41, 0.22); color: #ff8f86; }
.rec-tag--warn { background: rgba(217, 164, 65, 0.22); color: #f0c56a; }

/* Photo evidence: tiles standing in for the attachments. */
.rec-photos {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0.4rem;
}

.rec-photo {
    aspect-ratio: 4 / 3;
    border-radius: 4px;
    border: 1px solid var(--dash-line);
    background: linear-gradient(135deg, #2b3649, #161c28);
}

.rec-photo:nth-child(2) { background: linear-gradient(160deg, #26384a, #141a26); }
.rec-photo:nth-child(3) { background: linear-gradient(120deg, #33313f, #161a24); }
.rec-photo:nth-child(4) { background: linear-gradient(140deg, #223347, #131923); }
.rec-photo:nth-child(5) { background: linear-gradient(150deg, #2f2a33, #151a25); }
.rec-photo:nth-child(6) { background: linear-gradient(130deg, #253243, #121823); }

/* Field pairs on a card. */
.rec-field {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 0.3rem 0.8rem;
    align-content: start;
    margin: 0;
}

/* Values wrap rather than clip: a sheet can grow, a chart cannot. */
.rec-field dd {
    margin: 0;
    color: #fff;
    line-height: 1.35;
    min-width: 0;
}

/* Audit checklist. */
.rec-checks {
    display: grid;
    gap: 0.4rem;
    align-content: start;
}

.rec-check {
    display: flex;
    align-items: center;
    gap: 0.6em;
    line-height: 1.3;
}

.rec-check i {
    flex: none;
    width: 1em;
    height: 1em;
    border: 1px solid var(--dash-dim);
    border-radius: 3px;
}

.rec-check.is-done i {
    background: #3f9aa6;
    border-color: #3f9aa6;
    box-shadow: inset 0 0 0 2px #10141d;
}

.rec-check.is-open { color: #ffb0a9; }
.rec-check.is-open i { border-color: var(--signal-hot); }

/* Lighting. Plates dim together and the active ones glow. The render's
   plates keep their red language; the sheet's parts glow teal. */
.rec-frame [data-plate] {
    transition: opacity 0.35s var(--ease-io), box-shadow 0.35s var(--ease-io), border-color 0.35s var(--ease-io);
}

.rec-frame .is-dim {
    opacity: 0.38;
}

.rec-frame .is-lit {
    border-color: rgba(var(--lit), 0.85);
    box-shadow: 0 0 0 1px rgba(var(--lit), 0.35), 0 0 24px rgba(var(--lit), 0.28);
}

.rec-frame .art-label {
    --lit: 255, 74, 63;
}

.rec-frame .map-pulse.is-dim {
    opacity: 0.2;
}

.rec-frame .dash-panel,
.rec-frame .dash-kpi {
    min-width: 0;
}

/* The readout in the footer names the step. */
.rec-frame [data-line-out],
.app-stage-cap [data-cap-out] {
    transition: opacity 0.15s var(--ease-io);
}

.rec-frame [data-line-out].is-swapping,
.app-stage-cap [data-cap-out].is-swapping {
    opacity: 0;
}

.rec-frame .dash-foot span:last-child {
    color: var(--dash-dim);
}

/* A tag inside a field value sits on the text line. */
.rec-field .rec-tag {
    display: inline-block;
    vertical-align: middle;
}

@media (max-width: 900px) {
    .rec-frame {
        transform-style: flat;
        will-change: auto;
        font-size: 0.6rem;
    }

    .rec-frame--sheet {
        gap: 0.5rem;
        padding: 0.75rem 0.8rem 0.7rem;
    }

    /* Phones: the readouts go, the side panels pair up, and the three-column
       sheet becomes two columns with its stack across the bottom, so the
       board, caption, rail and dots stay close to one screen. */
    .rec-frame--sheet .dash-kpis {
        display: none;
    }

    .rec-cols {
        grid-template-columns: minmax(0, 1fr);
        gap: 0.5rem;
    }

    .rec-cols--3 {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    }

    .rec-stack {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        gap: 0.5rem;
    }

    .rec-cols--3 .rec-stack {
        grid-column: 1 / -1;
    }

    .rec-row {
        gap: 0.5rem;
        padding: 0.4rem 0.5rem;
    }

    .rec-frame .dash-foot {
        display: flex;
    }
}

@media (prefers-reduced-motion: reduce) {
    .rec-frame [data-plate],
    .rec-frame [data-line-out],
    .app-stage-cap [data-cap-out] {
        transition: none;
    }
}

/* ==========================================================================
   Questions block (_Faq.cshtml): masthead on the left, numbered cards on the
   right. Answers stay visible, so the text is there for a reader and for a
   crawler; the cards only give the run of questions a shape.
   ========================================================================== */

.faq {
    background: var(--ink-1);
}

.faq-inner {
    display: grid;
    grid-template-columns: minmax(0, 17rem) minmax(0, 1fr);
    gap: clamp(2rem, 5vw, 4.5rem);
    align-items: start;
}

.faq-head {
    position: sticky;
    top: calc(var(--nav-h) + 2rem);
}

.faq-head h2 {
    font-size: clamp(1.7rem, 2.6vw, 2.4rem);
    line-height: 1.1;
    letter-spacing: -0.02em;
    margin-top: 0.9rem;
}

.faq-head p:not(.tag) {
    margin-top: 1rem;
    color: var(--text-lo);
    line-height: 1.7;
}

.faq-list {
    display: grid;
    gap: 0.9rem;
    counter-reset: faq;
}

.faq-item {
    position: relative;
    padding: clamp(1.25rem, 2.4vw, 1.9rem) clamp(1.25rem, 2.4vw, 2rem);
    padding-left: clamp(3.4rem, 5vw, 4.5rem);
    border: 1px solid var(--line);
    border-radius: 14px;
    background: var(--ink-0);
    box-shadow: var(--elev-rest);
    transition: border-color 0.3s var(--ease-io), transform 0.3s var(--ease);
}

.faq-item:hover {
    border-color: var(--signal-edge);
    transform: translateY(-2px);
}

.faq-index {
    position: absolute;
    left: clamp(1.25rem, 2.4vw, 2rem);
    top: clamp(1.45rem, 2.6vw, 2.1rem);
    font-family: var(--mono);
    font-size: 0.7rem;
    letter-spacing: 0.1em;
    color: var(--signal-text);
}

.faq-item h3 {
    font-size: clamp(1.05rem, 1.5vw, 1.25rem);
    font-weight: 550;
    line-height: 1.35;
    letter-spacing: -0.01em;
    color: var(--text-hi);
    margin-bottom: 0.6rem;
    text-wrap: pretty;
}

.faq-item p {
    color: var(--text-lo);
    line-height: 1.75;
    max-width: 68ch;
    text-wrap: pretty;
}

.faq-item a {
    text-decoration: underline;
    text-underline-offset: 0.2em;
}

@media (max-width: 860px) {
    .faq-inner {
        grid-template-columns: minmax(0, 1fr);
        gap: 1.75rem;
    }

    .faq-head {
        position: static;
    }
}

@media (prefers-reduced-motion: reduce) {
    .faq-item {
        transition: none;
    }

    .faq-item:hover {
        transform: none;
    }
}

/* ==========================================================================
   weTrack Safety: the old site's eight points as a numbered two-up list,
   head sticky on the left the way the FAQ spread works.
   ========================================================================== */

.safety-list {
    list-style: none;
    counter-reset: safety;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.75rem;
}

.safety-list li {
    counter-increment: safety;
    display: flex;
    gap: 0.9rem;
    align-items: baseline;
    padding: 1.05rem 1.15rem;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--ink-0);
    font-size: var(--step-0);
    line-height: 1.45;
    transition: border-color 0.3s var(--ease-io), transform 0.3s var(--ease-io);
}

.safety-list li:hover {
    border-color: var(--signal-edge);
    transform: translateY(-2px);
}

.safety-list li::before {
    content: counter(safety, decimal-leading-zero);
    flex: 0 0 auto;
    font-family: var(--mono);
    font-size: var(--step--1);
    letter-spacing: 0.08em;
    color: var(--signal);
}

@media (max-width: 640px) {
    .safety-list {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* ==========================================================================
   Generated asset record and morning board (art-asset.webp, art-dashboard.webp).
   Chips measured off the renders: 24.8% x 19.2% of a 1586x992 frame for the
   three asset cards, 12.6% x 7.6% of a 1568x1003 frame for the board chip,
   expressed as inline-size shares so the plates track the artwork. The pillar
   is about 620px wide on a desktop, so the narrow rules start at 480px.
   ========================================================================== */

.art-media--asset .art-label {
    min-width: 25.4cqi;
    min-height: 12.4cqi;
    border-radius: max(6px, 1.2cqi);
    padding: 0.55em 1.5cqi;
    gap: 0.35em;
}
.art-media--asset .art-label strong { font-size: max(1.9cqi, 8.5px); }
.art-media--asset .art-label span { font-size: max(1.3cqi, 7.5px); }

.art-media--dash .art-label {
    min-width: 13.2cqi;
    min-height: 5.2cqi;
    border-radius: max(5px, 0.7cqi);
    padding: 0.4em 0.9cqi;
}
.art-media--dash .art-label strong { font-size: max(1.3cqi, 8px); }
.art-media--dash .art-label span { font-size: max(1cqi, 7px); }
.art-media--dash .art-chrome { font-size: max(1.05cqi, 8px); }

/* The board hides its live readout under 780px; this board has room for it. */
@container (min-width: 480px) {
    .art-media--dash .art-chrome--live { display: block; }
}

/*
 * Narrow: the type stops shrinking, so the plates outgrow their chips. Hang
 * the asset plates off the chips right edge (the machine is on the left) and
 * drop their third line; keep the board plate small enough to stay clear of
 * the flagged pin.
 */
@container (max-width: 480px) {
    .art-media--asset .art-label {
        left: auto !important;
        right: 4.5%;
        transform: translateY(-50%);
        gap: 0.2em;
        padding: 0.4em 1.1cqi;
        line-height: 1.15;
    }
    .art-media--asset .art-label strong { font-size: 11px; }
    .art-media--asset .art-label span { font-size: 10px; }
    .art-media--asset .art-label span + span { display: none; }

    .art-media--dash .art-label {
        padding: 0.2em 0.4em;
        gap: 0.1em;
    }
    .art-media--dash .art-label strong { font-size: 9px; letter-spacing: 0.04em; }
    .art-media--dash .art-label span { display: none; }
    .art-media--dash .art-chrome { font-size: 9px; }
}

/* The generated morning board inside a feature frame: the render is the card,
   so the frame drops its padding and glow and the hover zoom (the plates over
   the chips would not follow a scaled image). */
.feature-media--board {
    padding: 0;
    aspect-ratio: auto;
    background: none;
    border: 0;
    display: block;
}

.feature-media--board .art-media--dash {
    width: 100%;
    border-radius: 18px;
    overflow: hidden;
    border: 1px solid var(--line);
}

.feature-media--board img {
    display: block;
    width: 100%;
    height: auto;
    max-width: none;
    filter: none;
}

.feature-media--board:hover img {
    transform: none;
}

/* The frame lifts its image to z-index 1; the plates must sit above it. */
.feature-media--board img {
    z-index: auto;
}

.feature-media--board .art-label,
.feature-media--board .art-chrome {
    z-index: 2;
}

/* ==========================================================================
   The 3D rod pump on /rod-pumps. Same stage as the phone: the drawn pump sits
   in the box until the canvas fades in over it.
   ========================================================================== */

.pump3d {
    position: relative;
    width: 100%;
    max-width: 30rem;
    aspect-ratio: 0.72;
}

.pump3d canvas {
    position: absolute;
    inset: 0;
    display: block;
    width: 100% !important;
    height: 100% !important;
    opacity: 0;
}

.app-stage.has-3d .pump3d canvas {
    animation: stage-in 0.6s var(--ease-io) both;
}

.pump-fallback {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
    filter: drop-shadow(0 30px 40px rgba(0, 0, 0, 0.45));
    transition: opacity 0.6s var(--ease-io);
}

.app-stage.has-3d .pump-fallback {
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.6s var(--ease-io), visibility 0s linear 0.6s;
    pointer-events: none;
}

.pump-fallback [data-p] {
    transition: opacity 0.45s var(--ease-io), transform 0.9s var(--ease);
}

.pump-fallback [data-p].is-dim {
    opacity: 0.3;
}

/* Exploded: the traveling assembly slides out beside the barrel and both
   stacks open up, the way the two assemblies lie on a bench. */
.pump-fallback.is-exploded .pf-rod { transform: translate(64px, -30px); }
.pump-fallback.is-exploded .pf-plunger { transform: translate(64px, -12px); }
.pump-fallback.is-exploded .pf-tv { transform: translate(64px, 4px); }
.pump-fallback.is-exploded .pf-guide { transform: translate(-30px, -26px); }
.pump-fallback.is-exploded .pf-ext-up { transform: translate(-30px, -14px); }
.pump-fallback.is-exploded .pf-barrel { transform: translate(-30px, 0); }
.pump-fallback.is-exploded .pf-ext-lo { transform: translate(-30px, 12px); }
.pump-fallback.is-exploded .pf-sv { transform: translate(-30px, 24px); }
.pump-fallback.is-exploded .pf-holddown { transform: translate(-30px, 38px); }

/* Same viewport budget as .phone3d: the pump, caption, card rail and dots
   share the screen between the nav and the tab bar. */
@media (max-width: 900px) {
    .pump3d {
        max-width: min(19rem, 64vw, max(11rem, (100svh - 32rem) * 0.72));
    }
}

/* Callouts on the 3D pump: API part numbers projected onto the parts. */
.pump-tags {
    position: absolute;
    inset: 0;
    z-index: 2;
    overflow: hidden;
    pointer-events: none;
}

.pump-tag {
    position: absolute;
    left: 0;
    top: 0;
    font-family: var(--mono);
    font-size: 0.6rem;
    font-weight: 450;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    white-space: nowrap;
    padding: 0.32em 0.6em;
    border-radius: 5px;
    background: rgba(12, 17, 27, 0.92);
    color: rgba(238, 242, 245, 0.92);
    border: 1px solid rgba(230, 51, 41, 0.35);
    opacity: 0;
    transition: opacity 0.35s var(--ease-io);
    will-change: transform;
}

.pump-tag span {
    margin-left: 0.6em;
    color: rgba(238, 242, 245, 0.55);
}

.pump-tag::before {
    content: '';
    position: absolute;
    top: 50%;
    width: 14px;
    height: 1px;
    background: rgba(230, 51, 41, 0.65);
}

.pump-tag--left::before { right: -15px; }
.pump-tag--right::before { left: -15px; }

.pump-tag.is-on {
    opacity: 1;
}

@media (max-width: 900px) {
    .pump-tag {
        font-size: 0.5rem;
        padding: 0.28em 0.5em;
    }
}

/* Narrow stage: the callouts carry the part number only. */
.pump-tags.is-narrow .pump-tag .tag-name {
    display: none;
}

.pump-tags.is-narrow .pump-tag .tag-num {
    margin-left: 0;
    color: rgba(238, 242, 245, 0.92);
}

/* ==========================================================================
   Problem and fix ledger (_ProblemFix.cshtml). Light sheet, one
   numbered line per problem with the fix across from it. A cool wash high
   left and the signal low right give the sheet depth without a photograph.
   ========================================================================== */

.fixes {
    background: var(--ink-0);
    border-block: 1px solid var(--line);
    overflow: hidden;
}

.fixes::before {
    content: '';
    position: absolute;
    inset: 0;
    background:
        radial-gradient(ellipse 900px 520px at 8% -10%, rgba(98, 144, 204, 0.16), transparent 62%),
        radial-gradient(ellipse 760px 460px at 96% 108%, rgba(230, 51, 41, 0.09), transparent 60%);
    pointer-events: none;
}

.fixes > .shell {
    position: relative;
}

.fixes .prose-head--center h2 {
    max-width: 24ch;
}

.fixes .prose-head--center h2 + p:not(.tag) {
    color: var(--text-lo);
    max-width: 48ch;
    margin-inline: auto;
}

.fixes-ledger {
    padding-top: 0.25rem;
}

.fixes-head,
.fixes-row {
    display: grid;
    grid-template-columns: 3.25rem minmax(0, 1fr) 4.5rem minmax(0, 1fr);
    column-gap: clamp(1rem, 2.5vw, 2rem);
    align-items: center;
}

.fixes-head {
    padding: 0 clamp(1.2rem, 2.6vw, 1.9rem) 0.9rem;
}

.fixes-head .fixes-index {
    color: var(--text-chrome);
}

.fixes-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 0.9rem;
}

/* Each row is a lifted plate: a pale gradient sheet, a specular line along
   the top edge, and the elevation stack under it. */
.fixes-row {
    position: relative;
    padding: clamp(1.2rem, 2.4vw, 1.7rem) clamp(1.2rem, 2.6vw, 1.9rem);
    border-radius: 16px;
    border: 1px solid rgba(28, 43, 68, 0.1);
    background: linear-gradient(180deg, #f7f9fb, #eef2f5);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.95),
        0 1px 2px rgba(16, 27, 46, 0.06),
        0 14px 30px -14px rgba(16, 27, 46, 0.32),
        0 36px 64px -32px rgba(16, 27, 46, 0.34);
    overflow: hidden;
    transition: transform 0.45s var(--ease), box-shadow 0.45s var(--ease), border-color 0.35s var(--ease-io);
}

/* A red seam that grows down the row's left edge as the pointer arrives. */
.fixes-row::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 3px;
    background: var(--signal);
    box-shadow: 0 0 14px var(--signal-glow);
    transform: scaleY(0);
    transform-origin: top;
    transition: transform 0.4s var(--ease);
}

.fixes-row:hover {
    transform: translateY(-3px);
    border-color: var(--signal-edge);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 1),
        0 2px 4px rgba(16, 27, 46, 0.06),
        0 22px 44px -16px rgba(16, 27, 46, 0.4),
        0 48px 80px -36px rgba(16, 27, 46, 0.42);
}

.fixes-row:hover::before {
    transform: scaleY(1);
}

.fixes-index {
    font-family: var(--mono);
    font-size: 0.82rem;
    font-weight: 450;
    letter-spacing: 0.06em;
    color: var(--text-chrome);
    font-variant-numeric: tabular-nums;
    transition: color 0.35s var(--ease-io);
}

.fixes-row:hover .fixes-index {
    color: var(--signal-text);
}

.fixes-problem,
.fixes-fix {
    margin: 0;
    font-size: var(--step-0);
    line-height: 1.5;
    text-wrap: pretty;
}

.fixes-problem {
    color: var(--text-lo);
}

.fixes-fix {
    color: var(--text-hi);
    font-weight: 500;
}

/* Column labels ride inside each cell only when the columns have collapsed. */
.fixes-label {
    display: none;
}

.fixes-arrow {
    display: grid;
    place-items: center;
    width: 2.25rem;
    height: 2.25rem;
    justify-self: center;
    border-radius: 999px;
    border: 1px solid var(--line-strong);
    background: var(--glass-fill);
    box-shadow: inset 0 1px 0 var(--glass-specular);
    color: var(--text-lo);
    transition: color 0.35s var(--ease-io), border-color 0.35s var(--ease-io), box-shadow 0.35s var(--ease-io), transform 0.4s var(--ease);
}

.fixes-arrow svg {
    width: 1rem;
    height: 1rem;
}

.fixes-row:hover .fixes-arrow {
    color: var(--signal-text);
    border-color: var(--signal-edge);
    box-shadow: inset 0 1px 0 var(--glass-specular), 0 0 0 4px var(--signal-ink), 0 0 24px var(--signal-glow);
    transform: translateX(4px);
}

@media (max-width: 760px) {
    .fixes-head {
        display: none;
    }

    .fixes-row {
        grid-template-columns: 2.5rem minmax(0, 1fr);
        grid-template-areas:
            'index problem'
            'index arrow'
            'index fix';
        row-gap: 0.85rem;
        padding-block: 1.4rem;
    }

    .fixes-index { grid-area: index; align-self: start; padding-top: 0.2rem; }
    .fixes-problem { grid-area: problem; }
    .fixes-arrow { grid-area: arrow; justify-self: start; transform: rotate(90deg); }
    .fixes-row:hover .fixes-arrow { transform: rotate(90deg) translateX(4px); }
    .fixes-fix { grid-area: fix; }

    .fixes-label {
        display: block;
        font-family: var(--mono);
        font-size: 0.66rem;
        font-weight: 450;
        letter-spacing: 0.08em;
        text-transform: uppercase;
        color: var(--text-dim);
        margin-bottom: 0.35rem;
    }

    .fixes-label--fix {
        color: var(--signal-text);
    }
}

/* ==========================================================================
   How it works (_HowItWorks.cshtml). Four badges on a rule that draws itself
   in when the section is revealed, the step copy under each, one call to
   action beneath. Phones read it as a vertical timeline.
   ========================================================================== */

.how .prose-head--center h2 {
    max-width: 26ch;
}

.how .prose-head--center h2 + p:not(.tag) {
    color: var(--text-lo);
    max-width: 50ch;
    margin-inline: auto;
}

.how-steps {
    --badge: 4rem;
    position: relative;
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(var(--steps, 4), minmax(0, 1fr));
    gap: clamp(1.25rem, 3vw, 2.5rem);
    text-align: center;
}

/* The rule, from the first badge centre to the last: a hairline underneath,
   and a brighter line that draws across it once the section is on screen. */
.how-steps::before,
.how-steps::after {
    content: '';
    position: absolute;
    top: calc(var(--badge) / 2);
    left: calc(100% / var(--steps, 4) / 2);
    right: calc(100% / var(--steps, 4) / 2);
    height: 1px;
    pointer-events: none;
}

.how-steps::before {
    background: var(--line-strong);
}

.how-steps::after {
    background: linear-gradient(90deg, var(--signal), #1c2b44 55%, var(--signal));
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 1.6s var(--ease-io) 0.2s;
}

.js .is-revealed .how-steps::after {
    transform: scaleX(1);
}

.how-step {
    position: relative;
    z-index: 1;   /* above the rule drawn by the list's pseudo-elements */
    display: flex;
    flex-direction: column;
    align-items: center;
}

.how-badge {
    position: relative;
    display: grid;
    place-items: center;
    width: var(--badge);
    height: var(--badge);
    margin-bottom: 1.25rem;
    border-radius: 999px;
    background: linear-gradient(160deg, #2b3d5e, #16233a 60%, #101a2c);
    color: #eef2f5;
    box-shadow:
        inset 0 1px 0 rgba(238, 242, 245, 0.28),
        0 0 0 6px var(--ink-0),
        0 0 0 7px var(--line),
        0 18px 34px -16px rgba(16, 27, 46, 0.6);
    transition: transform 0.45s var(--ease), box-shadow 0.45s var(--ease);
}

.how-badge svg {
    width: 1.45rem;
    height: 1.45rem;
}

.how-step:hover .how-badge {
    transform: translateY(-3px);
    box-shadow:
        inset 0 1px 0 rgba(238, 242, 245, 0.32),
        0 0 0 6px var(--ink-0),
        0 0 0 7px var(--signal-edge),
        0 0 30px var(--signal-glow),
        0 22px 40px -16px rgba(16, 27, 46, 0.7);
}

.how-copy {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.how-num {
    margin: 0 0 0.45rem;
    font-family: var(--mono);
    font-size: 0.72rem;
    font-weight: 450;
    letter-spacing: 0.12em;
    color: var(--signal-text);
    font-variant-numeric: tabular-nums;
}

.how-step h3 {
    font-size: var(--step-1);
    margin: 0 0 0.55rem;
}

.how-step p:not(.how-num) {
    margin: 0;
    font-size: var(--step-0);
    line-height: 1.55;
    color: var(--text-lo);
    max-width: 26ch;
    text-wrap: pretty;
}

.how-cta {
    margin: clamp(2.25rem, 4.5vw, 3.5rem) 0 0;
    text-align: center;
}

@media (max-width: 760px) {
    .how-steps {
        --badge: 3.25rem;
        grid-template-columns: minmax(0, 1fr);
        row-gap: 2rem;
        text-align: left;
    }

    /* The rule turns vertical and runs down through the badges. */
    .how-steps::before,
    .how-steps::after {
        top: calc(var(--badge) / 2);
        bottom: calc(var(--badge) / 2);
        left: calc(var(--badge) / 2);
        right: auto;
        width: 1px;
        height: auto;
    }

    .how-steps::after {
        background: linear-gradient(180deg, var(--signal), #1c2b44 55%, var(--signal));
        transform: scaleY(0);
        transform-origin: top;
    }

    .js .is-revealed .how-steps::after {
        transform: scaleY(1);
    }

    .how-step {
        flex-direction: row;
        align-items: flex-start;
        gap: 1.1rem;
    }

    .how-badge {
        flex: none;
        margin-bottom: 0;
    }

    .how-badge svg {
        width: 1.2rem;
        height: 1.2rem;
    }

    .how-copy {
        align-items: flex-start;
        padding-top: 0.35rem;
    }

    .how-step p:not(.how-num) {
        max-width: none;
    }
}

/* ==========================================================================
   Phone menu sheet. The same navy glass as the desktop dropdowns, risen from
   the tab bar: the two products as photo tiles, one big row per group, and a
   sub-panel per group that slides in from the right. Transform and opacity
   only; display none while closed.
   ========================================================================== */

.nav-sheet {
    position: fixed;
    inset: 0;
    z-index: 1150;
    --ink-0: #1c2b44;
    --ink-1: #22304b;
    --line: rgba(238, 242, 245, 0.1);
    --line-strong: rgba(238, 242, 245, 0.22);
    --text-hi: #eef2f5;
    --text: rgba(238, 242, 245, 0.9);
    --text-lo: rgba(238, 242, 245, 0.66);
    --text-dim: rgba(238, 242, 245, 0.56);
    --text-chrome: rgba(238, 242, 245, 0.34);
    --signal-text: #ff6b60;
    --signal-ink: rgba(230, 51, 41, 0.14);
    --signal-edge: rgba(230, 51, 41, 0.45);
    color: var(--text);
}

.nav-sheet[hidden] {
    display: none;
}

.nav-sheet-veil {
    position: absolute;
    inset: 0;
    background: rgba(8, 14, 26, 0.55);
    opacity: 0;
    transition: opacity 0.35s var(--ease-io);
}

.nav-sheet-panel {
    position: absolute;
    inset: calc(2.25rem + env(safe-area-inset-top, 0px)) 0 0;   /* runs under the tab bar, so no page shows between */
    overflow: hidden;
    background:
        radial-gradient(ellipse 120% 60% at 100% 0%, rgba(98, 144, 204, 0.22), transparent 60%),
        radial-gradient(ellipse 90% 50% at 0% 100%, rgba(230, 51, 41, 0.16), transparent 60%),
        linear-gradient(178deg, #1c2b44, #16233a 70%, #121c30);
    border-radius: 26px 26px 0 0;
    box-shadow: inset 0 1px 0 rgba(238, 242, 245, 0.18), 0 -24px 60px -20px rgba(4, 8, 16, 0.8);
    transform: translateY(100%);
    transition: transform 0.55s var(--ease);
}

.nav-sheet.is-open .nav-sheet-veil {
    opacity: 1;
}

.nav-sheet.is-open .nav-sheet-panel {
    transform: none;
}

/* Views stack; the root slides left a little as a sub-panel arrives. */
.sheet-view {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    transition: transform 0.45s var(--ease), opacity 0.35s var(--ease-io), visibility 0s linear 0.45s;
}

.sheet-view--sub {
    transform: translateX(100%);
    visibility: hidden;
}

.nav-sheet:not([data-view='root']) .sheet-view--root {
    transform: translateX(-18%);
    opacity: 0;
    visibility: hidden;
}

.nav-sheet[data-view='root'] .sheet-view--root,
.sheet-view--sub.is-active {
    transform: none;
    opacity: 1;
    visibility: visible;
    transition: transform 0.45s var(--ease), opacity 0.35s var(--ease-io), visibility 0s;
}

.sheet-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 1.1rem var(--gutter) 0.75rem;
}

.sheet-view--sub .sheet-bar {
    padding: 0.85rem var(--gutter) 0.55rem;
}

.sheet-brand {
    width: auto;
    height: 1.55rem;
}

.sheet-close,
.sheet-back {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    background: rgba(238, 242, 245, 0.06);
    color: var(--text-hi);
    font: inherit;
    cursor: pointer;
}

.sheet-close {
    width: 2.4rem;
    height: 2.4rem;
    justify-content: center;
}

.sheet-close .ico {
    font-size: 0.95rem;
}

.sheet-back {
    padding: 0.5rem 0.95rem 0.5rem 0.7rem;
    font-size: 0.9rem;
    font-weight: 500;
}

.sheet-back .ico--chev {
    font-size: 0.7rem;
    transform: rotate(90deg);
}

.sheet-scroll {
    flex: 1;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    padding: 0.25rem var(--gutter) calc(4.75rem + env(safe-area-inset-bottom, 0px));
}

/* Product tiles: the page's own photograph under a duotone scrim. */
.sheet-tiles {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.7rem;
}

.sheet-tile {
    position: relative;
    display: block;
    /* Square-ish on a phone; taller when the screen has the height to spare. */
    height: clamp(11.5rem, calc(11.5rem + (100vh - 844px) * 0.35), 22rem);
    border-radius: 18px;
    overflow: hidden;
    border: 1px solid var(--line-strong);
    background: #10182a;
    text-decoration: none;
    color: var(--text-hi);
    box-shadow: inset 0 1px 0 rgba(238, 242, 245, 0.16), 0 18px 34px -18px rgba(4, 8, 16, 0.8);
}

.sheet-tile img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0.85;
}

.sheet-tile::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(16, 24, 42, 0.05) 0%, rgba(16, 24, 42, 0.55) 55%, rgba(16, 24, 42, 0.92) 100%);
}

.sheet-tile[aria-current='page'] {
    border-color: var(--signal-edge);
    box-shadow: inset 0 1px 0 rgba(238, 242, 245, 0.16), 0 0 0 3px var(--signal-ink), 0 18px 34px -18px rgba(4, 8, 16, 0.8);
}

.sheet-tile-copy {
    position: absolute;
    inset: auto 0 0;
    z-index: 1;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 0.9rem 0.95rem 1rem;
}

.sheet-tile-copy .tag {
    font-size: 0.58rem;
    color: var(--signal-text);
}

.sheet-tile-copy strong {
    font-size: 1.35rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    line-height: 1.1;
}

.sheet-tile-copy > span:last-child {
    font-size: 0.78rem;
    line-height: 1.35;
    color: var(--text-lo);
}

/* The groups: one big row each, the sub-heads as a hint underneath. */
.sheet-groups {
    list-style: none;
    margin: clamp(1.4rem, calc(1.4rem + (100vh - 844px) * 0.02), 2.6rem) 0 0;
    padding: 0;
    border-top: 1px solid var(--line);
}

.sheet-group {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    width: 100%;
    padding: clamp(1.05rem, calc(1.05rem + (100vh - 844px) * 0.02), 2.2rem) 0.2rem;
    border: 0;
    border-bottom: 1px solid var(--line);
    background: transparent;
    color: var(--text-hi);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.sheet-group:active {
    background: rgba(238, 242, 245, 0.05);
}

.sheet-group-text {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    min-width: 0;
}

.sheet-group-text strong {
    font-size: clamp(1.5rem, calc(1.5rem + (100vh - 844px) * 0.02), 2.4rem);
    font-weight: 700;
    letter-spacing: -0.015em;
    line-height: 1.1;
}

.sheet-group-text span {
    font-family: var(--mono);
    font-size: clamp(0.62rem, calc(0.62rem + (100vh - 844px) * 0.006), 0.85rem);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-dim);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.sheet-group .ico--chev {
    flex: none;
    font-size: 0.85rem;
    color: var(--signal-text);
    transform: rotate(-90deg);
}

.sheet-foot {
    margin-top: clamp(1.6rem, calc(1.6rem + (100vh - 844px) * 0.02), 2.8rem);
}

.sheet-foot .btn {
    width: 100%;
    justify-content: center;
}

.sheet-foot-links {
    display: flex;
    justify-content: center;
    gap: 1.6rem;
    margin: 1.1rem 0 0;
    font-size: 0.86rem;
}

.sheet-foot-links a {
    color: var(--text-lo);
    text-decoration: none;
}

/* Sub-panels: the group title, then one glass card per sub-group, each row
   an icon chip, the name, a one-line description and a chevron. Sized so a
   whole group fits a phone screen. */
.sheet-title {
    margin: 0 0 clamp(0.6rem, calc(0.6rem + (100vh - 844px) * 0.012), 1.3rem);
    font-size: clamp(1.45rem, calc(1.45rem + (100vh - 844px) * 0.02), 2.5rem);
    line-height: 1;
}

.sheet-card {
    margin-top: clamp(0.6rem, calc(0.6rem + (100vh - 844px) * 0.012), 1.3rem);
    padding: clamp(0.55rem, calc(0.55rem + (100vh - 844px) * 0.012), 1.1rem) 0.3rem 0.3rem;
    border-radius: 16px;
    border: 1px solid rgba(238, 242, 245, 0.13);
    background: linear-gradient(170deg, rgba(238, 242, 245, 0.085), rgba(238, 242, 245, 0.035) 45%, rgba(238, 242, 245, 0.06));
    box-shadow: inset 0 1px 0 rgba(238, 242, 245, 0.2), 0 14px 30px -18px rgba(4, 8, 16, 0.8);
}

.sheet-colhead {
    margin: 0 0 clamp(0.2rem, calc(0.2rem + (100vh - 844px) * 0.008), 0.6rem) 0.55rem;
    font-family: var(--mono);
    font-size: clamp(0.58rem, calc(0.58rem + (100vh - 844px) * 0.006), 0.8rem);
    font-weight: 450;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--signal-text);
}

.sheet-links {
    list-style: none;
    margin: 0;
    padding: 0;
}

.sheet-links li + li .sheet-link {
    border-top: 1px solid var(--line);
}

.sheet-link {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: clamp(0.7rem, calc(0.7rem + (100vh - 844px) * 0.012), 1.2rem);
    padding: clamp(0.42rem, calc(0.42rem + (100vh - 844px) * 0.015), 1.2rem) 0.5rem clamp(0.42rem, calc(0.42rem + (100vh - 844px) * 0.015), 1.2rem) 0.4rem;
    border-radius: 10px;
    text-decoration: none;
    color: var(--text-hi);
}

.sheet-link:active,
.sheet-link[aria-current='page'] {
    background: var(--signal-ink);
}

.sheet-link .sheet-ico {
    display: grid;
    place-items: center;
    width: clamp(1.9rem, calc(1.9rem + (100vh - 844px) * 0.03), 3.4rem);
    height: clamp(1.9rem, calc(1.9rem + (100vh - 844px) * 0.03), 3.4rem);
    flex: none;
    border-radius: 8px;
    background: linear-gradient(160deg, rgba(238, 242, 245, 0.16), rgba(238, 242, 245, 0.06));
    box-shadow: inset 0 1px 0 rgba(238, 242, 245, 0.2);
    color: var(--text-hi);
}

.sheet-link[aria-current='page'] .sheet-ico {
    background: linear-gradient(160deg, var(--signal-hot), var(--signal));
}

.sheet-link .sheet-ico .ico {
    font-size: clamp(0.9rem, calc(0.9rem + (100vh - 844px) * 0.012), 1.5rem);
}

.sheet-link-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.sheet-link-text strong {
    font-size: clamp(0.98rem, calc(0.98rem + (100vh - 844px) * 0.02), 1.75rem);
    font-weight: 650;
    letter-spacing: -0.01em;
    line-height: 1.2;
    text-wrap: balance;
}

.sheet-link-text > span {
    margin-top: 0.05rem;
    font-size: clamp(0.72rem, calc(0.72rem + (100vh - 844px) * 0.012), 1.2rem);
    line-height: 1.3;
    color: var(--text-lo);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.sheet-link > .ico {
    font-size: 0.75rem;
    color: var(--text-chrome);
}

.sheet-link > .ico--chev {
    transform: rotate(-90deg);
}

.sheet-link > .ico--ext {
    color: var(--signal-text);
}

.tab-item--menu {
    border: 0;
    background: transparent;
    font: inherit;
    cursor: pointer;
}

.tab-item--menu[aria-expanded='true'] {
    color: var(--text-hi);
}

.tab-item--menu[aria-expanded='true'] .ico {
    color: var(--signal-hot);
}

@media (prefers-reduced-motion: reduce) {
    .nav-sheet-panel,
    .sheet-view {
        transition: none;
    }
}

@media (min-width: 993px) {
    .nav-sheet {
        display: none;
    }
}

/* ==========================================================================
   Proof (_Proof.cshtml): a quote, a row of figures, a case-study strip and
   the ask, as lifted plates on the light sheet.
   ========================================================================== */

.proof .prose-head--center h2 {
    max-width: 30ch;
}

.proof-plate,
.proof-quote,
.proof-stat,
.proof-case {
    border-radius: 18px;
    border: 1px solid rgba(28, 43, 68, 0.1);
    background: linear-gradient(180deg, #f7f9fb, #eef2f5);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.95),
        0 1px 2px rgba(16, 27, 46, 0.06),
        0 14px 30px -14px rgba(16, 27, 46, 0.32),
        0 36px 64px -32px rgba(16, 27, 46, 0.34);
}

.proof-quote {
    position: relative;
    margin: 0 auto;
    max-width: 52rem;
    padding: clamp(1.8rem, 4vw, 2.8rem) clamp(1.5rem, 4vw, 3rem) clamp(1.5rem, 3vw, 2.2rem);
    text-align: center;
}

.proof-mark {
    position: absolute;
    top: 0.2rem;
    left: 50%;
    transform: translateX(-50%);
    font-family: var(--serif, Georgia, serif);
    font-size: 5rem;
    line-height: 1;
    color: var(--signal);
    opacity: 0.35;
    pointer-events: none;
}

.proof-quote blockquote {
    margin: 0;
}

.proof-quote blockquote p {
    margin: 0;
    font-size: var(--step-2);
    font-weight: 500;
    line-height: 1.35;
    letter-spacing: -0.01em;
    color: var(--text-hi);
    text-wrap: balance;
}

.proof-quote figcaption {
    margin-top: 1.1rem;
    font-family: var(--mono);
    font-size: 0.7rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.proof-quote figcaption strong {
    font-weight: 500;
    color: var(--text-lo);
}

.proof-quote figcaption span::before {
    content: ' \00b7\00a0';
}

.proof-stats {
    list-style: none;
    margin: clamp(1.25rem, 3vw, 2rem) 0 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(var(--n, 3), minmax(0, 1fr));
    gap: 0.9rem;
}

.proof-stat {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.35rem;
    padding: clamp(1.2rem, 2.6vw, 1.8rem) 1rem;
    text-align: center;
}

.proof-value {
    font-size: var(--step-4);
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1;
    color: var(--text-hi);
    font-variant-numeric: tabular-nums;
}

.proof-label {
    font-size: 0.92rem;
    color: var(--text-lo);
    max-width: 18ch;
    text-wrap: balance;
}

.proof-case {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    margin-top: 0.9rem;
    padding: 1.1rem clamp(1.2rem, 3vw, 1.8rem);
    text-decoration: none;
    color: inherit;
    transition: transform 0.45s var(--ease), border-color 0.35s var(--ease-io);
}

.proof-case:hover {
    transform: translateY(-2px);
    border-color: var(--signal-edge);
}

.proof-case-text {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    min-width: 0;
}

.proof-case-text strong {
    font-size: var(--step-1);
    font-weight: 600;
    line-height: 1.25;
    color: var(--text-hi);
    text-wrap: balance;
}

.proof-case .btn {
    flex: none;
}

.proof-cta {
    margin: clamp(1.8rem, 4vw, 2.6rem) 0 0;
    text-align: center;
}

@media (max-width: 760px) {
    .proof-stats {
        grid-template-columns: minmax(0, 1fr);
    }

    .proof-case {
        flex-direction: column;
        align-items: flex-start;
    }
}

/* ==========================================================================
   Footer on a phone: two link columns side by side, the brand paragraph
   dropped, contact and the store badges on one row, the sign-off smaller.
   Desktop is untouched.
   ========================================================================== */
@media (max-width: 560px) {
    .footer-main {
        padding: 2.25rem var(--gutter) 1.25rem;
    }

    .footer-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 1.5rem 1.25rem;
    }

    .footer-brand {
        grid-column: 1 / -1;
    }

    .footer-brand p {
        display: none;
    }

    .footer-logo-img {
        height: 1.7rem;
        margin-bottom: 0.6rem;
    }

    .footer-stamp {
        margin-top: 0;
    }

    .footer-heading {
        margin-bottom: 0.6rem;
    }

    .footer-links,
    .footer-contact {
        gap: 0.4rem;
    }

    .footer-links a,
    .footer-contact a {
        font-size: 0.86rem;
    }

    .footer-col:last-child {
        grid-column: 1 / -1;
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 0.5rem 1.25rem;
        align-items: start;
    }

    .footer-col:last-child > .footer-heading:first-child { grid-column: 1; grid-row: 1; }
    .footer-col:last-child > .footer-contact { grid-column: 1; grid-row: 2; }
    .footer-col:last-child > .footer-heading--spaced { grid-column: 2; grid-row: 1; margin-top: 0; }
    .footer-col:last-child > .footer-apps { grid-column: 2; grid-row: 2; }
    .footer-col:last-child > .social-link { grid-column: 1 / -1; grid-row: 3; margin-top: 0.5rem; }

    .footer-apps .app-badge img {
        height: 32px;
        width: auto;
    }

    .footer-word {
        padding-top: 1.5rem;
    }

    .footer-word-line {
        font-size: 2.2rem;
    }

    .footer-word-cap {
        margin-top: 0.75rem;
        padding-top: 0.6rem;
        gap: 0.35rem 1rem;
    }

    .footer-bottom {
        padding: 1rem var(--gutter) 1.25rem;
        gap: 0.35rem;
    }
}

/* ==========================================================================
   Cookie notice
   ========================================================================== */

.cookie-note {
    position: fixed;
    z-index: 1150;
    left: var(--gutter);
    bottom: calc(1rem + env(safe-area-inset-bottom, 0px));
    display: flex;
    align-items: center;
    gap: 1rem;
    max-width: 34rem;
    padding: 0.9rem 1.1rem;
    border: 1px solid var(--glass-edge);
    border-radius: var(--radius);
    background: var(--nav-panel-bg);
    box-shadow: var(--elev-high);
    color: var(--text);
    font-size: var(--step--1);
    line-height: 1.5;
}

.cookie-note[hidden] {
    display: none;
}

.cookie-note .btn {
    flex-shrink: 0;
    padding: 0.6rem 1.1rem;
}

@media (max-width: 992px) {
    /* Sits above the tab bar. */
    .cookie-note {
        left: 0.75rem;
        right: 0.75rem;
        bottom: calc(4.6rem + env(safe-area-inset-bottom, 0px));
    }
}

/* The Rod Pumps hero clip is rendered with the pump on the right two thirds
   of a 16:9 frame. On a phone the cover crop would push it out of view, so aim
   the crop at the pump; it sits behind the headline, dim, still recognisable. */
@media (max-width: 900px) {
    .hero--rodpumps .hero-media,
    .hero--rodpumps .hero-media--video {
        object-position: 64% 50%;
    }
}

/* ==========================================================================
   Back to top
   A small navy plate in the bottom right corner, hidden until the page has
   moved about a screen (site.js adds .is-shown). Transform and opacity only.
   ========================================================================== */

.to-top {
    position: fixed;
    z-index: 1150;
    right: var(--gutter);
    bottom: calc(1rem + env(safe-area-inset-bottom, 0px));
    display: grid;
    place-items: center;
    width: 2.9rem;
    height: 2.9rem;
    padding: 0;
    border: 1px solid rgba(255, 255, 255, 0.14);
    border-radius: 50%;
    background: #1c2b44;
    color: #eef2f5;
    box-shadow: var(--elev-high);
    cursor: pointer;
    opacity: 0;
    visibility: hidden;
    transform: translateY(0.75rem) scale(0.92);
    transition: opacity 0.28s var(--ease-io), transform 0.36s var(--ease),
                background 0.22s var(--ease-io), visibility 0s linear 0.28s;
}

.to-top.is-shown {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition-delay: 0s;
}

.to-top .ico {
    width: 1.15rem;
    height: 1.15rem;
    stroke-width: 1.8;
    transform: rotate(-90deg);   /* the sprite arrow points right */
}

.to-top:hover,
.to-top:focus-visible {
    background: var(--signal);
}

/* ==========================================================================
   Live chat launcher. The site's own button until pressed; then HubSpot draws
   its launcher in the same corner and this one hides (initChat() in site.js).
   Back to top steps up to clear either.
   ========================================================================== */
.chat-launch {
    position: fixed;
    z-index: 1150;
    right: var(--gutter);
    bottom: calc(1rem + env(safe-area-inset-bottom, 0px));
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    height: 2.9rem;
    padding: 0 1.15rem 0 0.95rem;
    border: 1px solid rgba(255, 255, 255, 0.14);
    border-radius: 999px;
    background: #1c2b44;
    color: #eef2f5;
    font: 500 0.9rem/1 var(--display);
    letter-spacing: 0.01em;
    box-shadow: var(--elev-high);
    cursor: pointer;
    transition: background 0.22s var(--ease-io), transform 0.22s var(--ease-io);
}

.chat-launch[hidden] {
    display: none;
}

.chat-launch .ico {
    width: 1.2rem;
    height: 1.2rem;
    stroke-width: 1.7;
}

.chat-launch:hover,
.chat-launch:focus-visible {
    background: var(--signal-fill);
}

.chat-launch:active {
    transform: translateY(1px);
}

.chat-launch.is-loading {
    cursor: progress;
}

.chat-launch.is-loading .ico {
    animation: chat-wait 1.1s var(--ease-io) infinite;
}

@keyframes chat-wait {
    50% { opacity: 0.35; }
}

.has-chat .to-top {
    bottom: calc(1rem + 3.9rem + env(safe-area-inset-bottom, 0px));
}

/* HubSpot's launcher is a little taller than ours. */
.chat-live .to-top {
    bottom: calc(1rem + 5rem + env(safe-area-inset-bottom, 0px));
}

@media (prefers-reduced-motion: reduce) {
    .chat-launch.is-loading .ico { animation: none; }
}

.to-top:focus-visible {
    outline: 2px solid var(--signal-edge);
    outline-offset: 3px;
}

.to-top:active {
    transform: scale(0.94);
}

/* While the cookie notice is up they would share the bottom edge on a phone. */
.cookie-note:not([hidden]) ~ .to-top {
    display: none;
}

@media (max-width: 992px) {
    /* Sits above the tab bar. */
    .to-top {
        right: 0.75rem;
        bottom: calc(4.6rem + env(safe-area-inset-bottom, 0px));
        width: 2.6rem;
        height: 2.6rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    .to-top {
        transition: none;
    }
}

/* ==========================================================================
   Client login — the web application's own sign-in form, embedded.
   A utility page: the band is shorter than a content band so the form is on
   screen when the page opens, rather than below a full masthead.
   ========================================================================== */
.login-band {
    padding-top: calc(var(--nav-h) + clamp(2rem, 5vw, 3.5rem));
    padding-bottom: clamp(3rem, 7vw, 5.5rem);
}

.login-head {
    margin-bottom: clamp(1.5rem, 3vw, 2.25rem);
}

.login-head h1 {
    font-size: var(--step-3);
    margin: 0;
}

.login-head h1 + p:not(.tag) {
    margin-top: 0.9rem;
    color: var(--text-lo);
}

/* The plate is sized to the form inside it, not to the measure of the page.
   The sign-in page the application serves is a fixed 500px block plus the
   document's own margins, so a wider frame would leave it stranded off to one
   side. 34rem holds it with even air down both edges.

   Height is a stand-in: a cross-origin frame cannot be measured from here, so
   the sign-in page reports its own and the listener on the page applies it.
   This value matches the form as it stands today, so there is no dead space
   under it before that lands either. */
.login-embed {
    position: relative;
    max-width: 34rem;
    margin-inline: auto;
    overflow: hidden;
    border: 1px solid var(--line);
    border-radius: 14px;
    box-shadow: var(--elev-rest);
}

.login-frame {
    display: block;
    /*
     * Deliberately 33px wider than the plate, and the overflow above crops the
     * surplus off the right.
     *
     * The sign-in page the application serves today centres a 500px wrapper and
     * then sits its 467px form flush against the wrapper's left edge, leaving 33px
     * dead on the right. That offset is fixed, so no frame width cancels it: the
     * form always lands half of 33px left of centre. Over-widening the frame by
     * exactly that much pushes the form back to the middle of the plate, and
     * cropping the surplus keeps the plate's own edges square.
     *
     * The replacement sign-in page centres itself and needs none of this, so the
     * script on the page drops back to a plain 100% the moment that page reports
     * its height. Nothing here has to be unpicked by hand after it ships.
     */
    width: calc(100% + 33px);
    height: 24rem;
    border: 0;
    /* The page inside paints its own surface; this only shows in the moment
       before it arrives. */
    background: #fff;
}

/* ==========================================================================
   Static device shot — a real RPT Mobile screenshot in the same phone body the
   Mobile page draws, minus the 3D stage and its script. The body's inner parts
   (.phone-frame, .phone-screen, .phone-shot, .phone-island) are positioned
   absolutely, so this wrapper only has to give them a box of the right shape.
   Sits beside a column of copy; stacks above it when the column gets narrow.
   ========================================================================== */
.shot-col {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: clamp(1.25rem, 3vw, 2rem);
    align-items: start;
}

.shot-phone {
    position: relative;
    width: clamp(7.5rem, 14vw, 10rem);
    aspect-ratio: 828 / 1800;
    margin: 0.35rem 0 0;
    flex: none;
}

@media (max-width: 30rem) {
    .shot-col {
        grid-template-columns: 1fr;
    }

    .shot-phone {
        width: 9rem;
    }
}
